Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"configurations": [
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Debug CodexCommanderMenuBar (app)",
"target": "CodexCommanderMenuBar",
"configuration": "debug",
"preLaunchTask": "swift: Build Debug CodexCommanderMenuBar (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Release CodexCommanderMenuBar (app)",
"target": "CodexCommanderMenuBar",
"configuration": "release",
"preLaunchTask": "swift: Build Release CodexCommanderMenuBar (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Debug MenuBarCoreTests (app)",
"target": "MenuBarCoreTests",
"configuration": "debug",
"preLaunchTask": "swift: Build Debug MenuBarCoreTests (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Release MenuBarCoreTests (app)",
"target": "MenuBarCoreTests",
"configuration": "release",
"preLaunchTask": "swift: Build Release MenuBarCoreTests (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Debug MenuBarUITests (app)",
"target": "MenuBarUITests",
"configuration": "debug",
"preLaunchTask": "swift: Build Debug MenuBarUITests (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Release MenuBarUITests (app)",
"target": "MenuBarUITests",
"configuration": "release",
"preLaunchTask": "swift: Build Release MenuBarUITests (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Debug UIProbe (app)",
"target": "UIProbe",
"configuration": "debug",
"preLaunchTask": "swift: Build Debug UIProbe (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Release UIProbe (app)",
"target": "UIProbe",
"configuration": "release",
"preLaunchTask": "swift: Build Release UIProbe (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Debug IconProbe (app)",
"target": "IconProbe",
"configuration": "debug",
"preLaunchTask": "swift: Build Debug IconProbe (app)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:CodexCommander}/app",
"name": "Release IconProbe (app)",
"target": "IconProbe",
"configuration": "release",
"preLaunchTask": "swift: Build Release IconProbe (app)"
}
]
}
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ bun run build:macos
open dist/macos/CodexCommander.app
```

Double-clicking that source build ensures the proxy through the same checkout; if startup fails, the
menu app stays open so its diagnostics and **Start** control remain available. **Quit** closes only
the companion UI. **Stop** and **Restart** are separate, confirmation-gated proxy actions.
Every built app launches only the Bun runtime and server resources embedded in its own
`Contents/Resources/runtime`; it never executes checkout `src/` or an ambient `ccx`. Rebuild the app
to pick up source changes. If startup fails, the menu app stays open so its diagnostics and **Start**
control remain available. **Quit** closes only the companion UI. **Stop** and **Restart** are
separate, confirmation-gated proxy actions.

On its first launch from `dist/macos` or Applications, the app enables **Launch at Login** so the
menu icon returns after sign-in. The startup row exposes the actual mode: **Desktop** launches the
Expand Down
46 changes: 42 additions & 4 deletions app/Sources/MenuBarCore/Discovery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,47 @@ public struct ProxyEndpoint: Equatable, Sendable {
}
}

/// Protected process identity read from `runtime-port.json`. The secret never
/// leaves the process; it authenticates `/healthz` immediately before the app
/// sends a management credential or request body.
public struct ProxyRuntimeAttestation: Equatable, Sendable {
public let host: String
public let port: Int
public let pid: Int
let secret: String

public init?(host: String, port: Int, pid: Int, secret: String) {
guard let normalized = ProxyEndpoint.normalizedLoopbackHost(host),
ProxyEndpoint.validPorts.contains(port),
pid > 0,
secret.range(of: "^[A-Za-z0-9_-]{43}$", options: .regularExpression) != nil
else { return nil }
self.host = normalized
self.port = port
self.pid = pid
self.secret = secret
}
}

public struct ProxyInstallation: Sendable {
public let endpoint: ProxyEndpoint
public let credential: String?
public let credentialAvailability: ManagementCredentialAvailability
public let configDirectory: URL
public let runtimeAttestation: ProxyRuntimeAttestation?

public init(
endpoint: ProxyEndpoint,
credential: String?,
credentialAvailability: ManagementCredentialAvailability,
configDirectory: URL
configDirectory: URL,
runtimeAttestation: ProxyRuntimeAttestation? = nil
) {
self.endpoint = endpoint
self.credential = credential
self.credentialAvailability = credentialAvailability
self.configDirectory = configDirectory
self.runtimeAttestation = runtimeAttestation
}
}

Expand Down Expand Up @@ -268,6 +293,7 @@ public enum ProxyDiscovery {
}

let endpoint: ProxyEndpoint
let runtimeAttestation: ProxyRuntimeAttestation?
do {
let data = try directory.readFile(named: "runtime-port.json", maxBytes: 16 * 1024)
guard let record = try? JSONDecoder().decode(RuntimePortRecord.self, from: data),
Expand All @@ -285,8 +311,17 @@ public enum ProxyDiscovery {
throw DiscoveryError.unsafeRuntimeRecord
}
endpoint = discovered
runtimeAttestation = record.attestationSecret.flatMap {
ProxyRuntimeAttestation(
host: record.hostname ?? "127.0.0.1",
port: record.port,
pid: record.pid,
secret: $0
)
}
} catch SecureReadError.missing {
endpoint = .default
runtimeAttestation = nil
} catch let error as DiscoveryError {
throw error
} catch {
Expand All @@ -298,7 +333,8 @@ public enum ProxyDiscovery {
endpoint: endpoint,
credential: inherited,
credentialAvailability: .inheritedEnvironment,
configDirectory: directoryURL
configDirectory: directoryURL,
runtimeAttestation: runtimeAttestation
)
}

Expand All @@ -317,14 +353,16 @@ public enum ProxyDiscovery {
endpoint: endpoint,
credential: token,
credentialAvailability: .file,
configDirectory: directoryURL
configDirectory: directoryURL,
runtimeAttestation: runtimeAttestation
)
} catch {
return ProxyInstallation(
endpoint: endpoint,
credential: nil,
credentialAvailability: .unavailable,
configDirectory: directoryURL
configDirectory: directoryURL,
runtimeAttestation: runtimeAttestation
)
}
}
Expand Down
Loading
Loading