Skip to content
Merged
94 changes: 92 additions & 2 deletions Sources/CodexAccountSwitcher/main.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import AppKit
import ApplicationServices
import CryptoKit
import Darwin
import Foundation
Expand Down Expand Up @@ -2342,6 +2343,8 @@ private struct CodePilotSetupStatus {
cloudflareRequirement = .cloudflareNeedsConfiguration
cloudflareDetail = "cloudflared is installed; set up a tunnel for remote access."
}
let permissions = SystemRemoteDesktopPermissions()
let notificationsConfigured = apnsConfigured()
return CodePilotSetupStatus(rows: [
CodePilotSetupRow(
title: "Codex CLI",
Expand Down Expand Up @@ -2372,6 +2375,27 @@ private struct CodePilotSetupStatus {
title: "Cloudflare",
requirement: cloudflareRequirement,
detail: cloudflareDetail
),
CodePilotSetupRow(
title: "Screen Recording",
requirement: permissions.screenRecordingGranted ? .screenRecordingReady : .screenRecordingMissing,
detail: permissions.screenRecordingGranted
? "Remote Desktop viewing is allowed."
: "Allow CodePilot in System Settings to view the Mac screen."
),
CodePilotSetupRow(
title: "Accessibility",
requirement: permissions.accessibilityGranted ? .accessibilityReady : .accessibilityMissing,
detail: permissions.accessibilityGranted
? "Remote Desktop control is allowed."
: "Allow CodePilot in System Settings to control the Mac."
),
CodePilotSetupRow(
title: "Notifications",
requirement: notificationsConfigured ? .notificationsReady : .notificationsOptional,
detail: notificationsConfigured
? "APNs credentials are configured for turn-finished alerts."
: "Configure APNs only if background iPhone alerts are needed."
)
])
}
Expand Down Expand Up @@ -2454,6 +2478,22 @@ private struct CodePilotSetupStatus {
guard let data = try? Data(contentsOf: path) else { return nil }
return try? JSONDecoder().decode(CodePilotCloudflareMetadata.self, from: data)
}

private static func apnsConfigured(
environment: [String: String] = ProcessInfo.processInfo.environment,
fileExists: (String) -> Bool = { FileManager.default.fileExists(atPath: NSString(string: $0).expandingTildeInPath) }
) -> Bool {
let certPath = environment["CODEX_PHONE_APNS_CERT_PATH"]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let certKeyPath = environment["CODEX_PHONE_APNS_CERT_KEY_PATH"]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if !certPath.isEmpty, !certKeyPath.isEmpty, fileExists(certPath), fileExists(certKeyPath) {
return true
}

let teamID = environment["CODEX_PHONE_APNS_TEAM_ID"]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let keyID = environment["CODEX_PHONE_APNS_KEY_ID"]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let keyPath = environment["CODEX_PHONE_APNS_KEY_PATH"]?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return !teamID.isEmpty && !keyID.isEmpty && !keyPath.isEmpty && fileExists(keyPath)
}
}

struct CodePilotSetupRow: Equatable {
Expand Down Expand Up @@ -2490,6 +2530,16 @@ enum CodePilotCloudflareErrorMapper {
}
}

enum CodePilotTryCloudflareCommand {
static func terminalCommand(scriptPath: String, currentDirectory: String) -> String {
"cd \(shellQuoted(currentDirectory)) && \(shellQuoted(scriptPath)) start-trycloudflare"
}

private static func shellQuoted(_ value: String) -> String {
"'\(value.replacingOccurrences(of: "'", with: "'\\''"))'"
}
}

enum CodePilotSetupRequirement: Equatable {
case codexCLIInstalled
case codexCLIMissing
Expand All @@ -2506,13 +2556,16 @@ enum CodePilotSetupRequirement: Equatable {
case cloudflareOptional
case cloudflareMissing
case cloudflareNeedsConfiguration
case screenRecordingReady
case screenRecordingMissing
case accessibilityReady
case accessibilityMissing
case notificationsReady
case notificationsOptional

var statusLabel: String {
switch self {
case .codexCLIInstalled, .codexSignedIn, .profilesCreated, .gatewayTokenPresent, .gatewayRunning, .cloudflareReady:
case .codexCLIInstalled, .codexSignedIn, .profilesCreated, .gatewayTokenPresent, .gatewayRunning, .cloudflareReady, .screenRecordingReady, .accessibilityReady, .notificationsReady:
return "Ready"
case .cloudflareNeedsConfiguration:
return "Needs setup"
Expand Down Expand Up @@ -2598,6 +2651,14 @@ private final class CodePilotSetupWindowController: NSWindowController {
button("Open Cloudflare Guide", #selector(openCloudflareGuide))
]
))
root.addArrangedSubview(section(
title: "Remote Desktop & Notifications",
buttons: [
button("Request Screen Recording", #selector(requestScreenRecordingPermission)),
button("Request Accessibility", #selector(requestAccessibilityPermission)),
button("Open iOS Notification Guide", #selector(openIOSNotificationGuide))
]
))

let refresh = button("Refresh Status", #selector(refreshStatusAction))
root.addArrangedSubview(refresh)
Expand Down Expand Up @@ -2706,6 +2767,25 @@ private final class CodePilotSetupWindowController: NSWindowController {
}
}

@objc private func requestScreenRecordingPermission() {
_ = CGRequestScreenCaptureAccess()
outputLabel.stringValue = "Screen Recording prompt requested. Reopen CodePilot after changing System Settings."
refreshStatus()
}

@objc private func requestAccessibilityPermission() {
let options = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: true] as CFDictionary
_ = AXIsProcessTrustedWithOptions(options)
outputLabel.stringValue = "Accessibility prompt requested. Refresh after changing System Settings."
refreshStatus()
}

@objc private func openIOSNotificationGuide() {
let sourceURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
.appendingPathComponent("docs/INSTALL_IOS.md")
NSWorkspace.shared.open(sourceURL)
}

private func runBundledScript(named name: String, arguments: [String] = [], force: Bool = false) {
let candidates = [
Bundle.main.resourceURL?.appendingPathComponent("scripts/\(name)"),
Expand Down Expand Up @@ -2879,7 +2959,17 @@ private final class CodePilotCloudflareWizardController: NSWindowController {
}

@objc private func startTemporary() {
runCloudflareStep(["start-trycloudflare"], successMessage: "Temporary Cloudflare URL started. Use this only for testing.")
guard let script = scriptURL() else {
outputLabel.stringValue = "Could not find setup-cloudflare-remote-access.sh."
return
}
let command = CodePilotTryCloudflareCommand.terminalCommand(
scriptPath: script.path,
currentDirectory: FileManager.default.currentDirectoryPath
)
runInTerminal(command)
outputLabel.stringValue = "Temporary Cloudflare URL is starting in Terminal. Copy the trycloudflare.com URL from the Terminal output when it appears."
detailsLabel.stringValue = command
}

@objc private func openCloudflareDashboard() {
Expand Down
10 changes: 10 additions & 0 deletions Tests/CodexAccountSwitcherTests/CloudflareSetupTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@ final class CloudflareSetupTests: XCTestCase {
"cloudflared is missing. Install it from the Cloudflare setup step before continuing."
)
}

func testTryCloudflareTerminalCommandUsesLongRunningSubcommand() {
let command = CodePilotTryCloudflareCommand.terminalCommand(
scriptPath: "/tmp/Code Pilot/scripts/setup-cloudflare-remote-access.sh",
currentDirectory: "/tmp/Code Pilot"
)

XCTAssertTrue(command.contains("cd '/tmp/Code Pilot'"))
XCTAssertTrue(command.contains("'/tmp/Code Pilot/scripts/setup-cloudflare-remote-access.sh' start-trycloudflare"))
}
}
9 changes: 9 additions & 0 deletions Tests/CodexAccountSwitcherTests/SetupStatusTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ final class SetupStatusTests: XCTestCase {
XCTAssertEqual(CodePilotSetupRequirement.cloudflareNeedsConfiguration.statusLabel, "Needs setup")
XCTAssertEqual(CodePilotSetupRequirement.cloudflareOptional.statusLabel, "Optional")
}

func testRemoteDesktopAndNotificationStatusLabelsAreUserFacing() {
XCTAssertEqual(CodePilotSetupRequirement.screenRecordingReady.statusLabel, "Ready")
XCTAssertEqual(CodePilotSetupRequirement.screenRecordingMissing.statusLabel, "Missing")
XCTAssertEqual(CodePilotSetupRequirement.accessibilityReady.statusLabel, "Ready")
XCTAssertEqual(CodePilotSetupRequirement.accessibilityMissing.statusLabel, "Missing")
XCTAssertEqual(CodePilotSetupRequirement.notificationsReady.statusLabel, "Ready")
XCTAssertEqual(CodePilotSetupRequirement.notificationsOptional.statusLabel, "Optional")
}
}
7 changes: 7 additions & 0 deletions ops/agents/escalations/issue-triage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Issue Triage Escalations

Date: 2026-07-03

- #2 Same Network setup path: needs a product/security decision to either support LAN binding explicitly or disable the iOS Same Network path until it is safe and documented.
- #3 Remote Desktop pairing approval: needs maintainer decision on the trust model before public beta. Either require explicit Mac approval in the pairing flow or remove unused pairing-code UI and document bearer-token possession as the trust boundary.
- #8 Public screenshot set: maintainer approval is required before screenshots are committed, used in README/GitHub Pages, or reused in TestFlight/App Store metadata.
43 changes: 43 additions & 0 deletions ops/agents/triage/2026-07-01-issue-triage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# CodePilot Issue Triage - 2026-07-01

Remote write policy: read-only GitHub inspection only. No remote comments, labels, pushes, PRs, or issue edits were made.

## Reviewed Issues

- #1 Add permissions and notification readiness to the Mac setup checklist
- Proposed labels: `setup`, `remote-desktop`, `mac`, `notifications`, `severity: low`.
- Triage: confirmed from existing issue body/comment that the setup checklist still needed visible Screen Recording, Accessibility, and notification/APNs readiness rows.
- Local fix: added setup checklist rows for Screen Recording, Accessibility, and Notifications; added setup-window recovery buttons for the macOS permission prompts and iOS notification guide; added status-label coverage for the new Ready/Optional/Missing states.
- #2 Support or disable the iOS Same Network setup path
- Proposed labels: `setup`, `ios`, `gateway`, `severity: medium`.
- Triage: product/security decision required because default gateway binding is loopback-only while iOS exposes Same Network setup. Already escalated locally.
- #3 Require clear Remote Desktop pairing approval in setup
- Proposed labels: `setup`, `remote-desktop`, `mac`, `ios`, `severity: high`.
- Triage: trust-model decision required before public beta. Already escalated locally.
- #4 TryCloudflare setup step waits forever for the tunnel process
- Proposed labels: `bug`, `setup`, `mac`, `cloudflare`, `severity: medium`.
- Triage: reproduced by code inspection. `startTemporary()` previously called `runCloudflareStep(["start-trycloudflare"])`, which uses `runProcess` and waits for process exit; `cloudflared tunnel --url` is long-running.
- Local fix: changed the TryCloudflare action to open the long-running command in Terminal so stdout can stream and the temporary URL appears without the setup sheet waiting for process exit.
- #5 Permanent Cloudflare setup does not safely reuse existing tunnels
- Proposed labels: `bug`, `setup`, `mac`, `cloudflare`, `severity: medium`.
- Triage: already has a prior local/draft fix referenced by the existing issue comment. No duplicate fix made.
- #8 Prepare sanitized public screenshot set
- Proposed labels: `documentation`, `severity: low`.
- Triage: maintainer approval required before screenshots are captured, committed, or reused publicly. Already escalated locally.

## Local Validation

- `swift test --filter CloudflareSetupTests/testTryCloudflareTerminalCommandUsesLongRunningSubcommand` failed before implementation because `CodePilotTryCloudflareCommand` was missing.
- `swift test --filter CloudflareSetupTests` passed after implementation.
- `swift test --filter SetupStatusTests` passed after adding setup readiness rows for #1.
- `swift test` passed after the #1 setup checklist changes.

## Local Branches And Commits

- Branch: `agent/setup-readiness-fixes`.
- Existing local commit: `1d46890 fix: launch trycloudflare setup in terminal`.
- New local commit: `fix: show setup readiness for remote desktop`.

## Escalations

No new escalation beyond the existing `ops/agents/escalations/issue-triage.md` entries for #2, #3, and #8.
57 changes: 57 additions & 0 deletions ops/agents/triage/2026-07-02-issue-triage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# CodePilot Issue Triage - 2026-07-02

Remote write policy: public GitHub writes are allowed after privacy audit when they directly advance launch readiness. Privacy audit passed before inspecting the existing draft PR state. No new remote issue comments or label edits were needed in this pass.

## Follow-Up Pass

- Checked open GitHub issues on 2026-07-02 after the prior triage run.
- No new open issues were found.
- Existing issue labels still match the proposed component and severity triage below.
- Draft PR #10 remains open, clean, and attached to `agent/setup-readiness-fixes`.
- Later 2026-07-02 pass: rechecked the open issue queue and confirmed no issues newer than #8.
- Privacy audit passed before this local note update.
- Draft PR #10 still has passing `Test and Audit` status and `CLEAN` merge state.
- Launch autonomy pass at 2026-07-02T19:00:41Z: rechecked open issues #1, #2, #3, #4, and #8.
- No new open GitHub issues were found.
- Privacy audit passed before this public-write-eligible local note update.
- Draft PR #10 remains open, draft, `CLEAN`, and its `Test and Audit` check is passing.
- Launch autonomy pass at 2026-07-02T21:17:05Z: rechecked the open issue queue and issue details for #1, #2, #3, #4, and #8.
- No new open GitHub issues were found, and no existing issue bodies or comments changed since the prior triage state.
- Privacy audit passed before this public-write-eligible local note update.
- Draft PR #10 remains open, draft, `CLEAN`, and its `Test and Audit` check is passing.
- No new GitHub comments, labels, branches, or PRs were needed in this pass.

## Reviewed Issues

- #1 Add permissions and notification readiness to the Mac setup checklist
- Proposed labels: `setup`, `remote-desktop`, `mac`, `notifications`, `severity: low`.
- Triage: actionable low-risk setup visibility issue.
- Status: addressed by draft PR #10 on branch `agent/setup-readiness-fixes`.
- #2 Support or disable the iOS Same Network setup path
- Proposed labels: `setup`, `ios`, `gateway`, `severity: medium`.
- Triage: still blocked on product/security decision for LAN binding versus disabling Same Network.
- Status: escalation remains in `ops/agents/escalations/issue-triage.md`.
- #3 Require clear Remote Desktop pairing approval in setup
- Proposed labels: `setup`, `remote-desktop`, `mac`, `ios`, `severity: high`.
- Triage: still blocked on Remote Desktop trust-model decision before public beta.
- Status: escalation remains in `ops/agents/escalations/issue-triage.md`.
- #4 TryCloudflare setup step waits forever for the tunnel process
- Proposed labels: `bug`, `setup`, `mac`, `cloudflare`, `severity: medium`.
- Triage: actionable setup bug reproduced by code inspection.
- Status: addressed by draft PR #10 on branch `agent/setup-readiness-fixes`.
- #8 Prepare sanitized public screenshot set
- Proposed labels: `documentation`, `severity: low`.
- Triage: screenshot capture and public use require maintainer approval.
- Status: escalation remains in `ops/agents/escalations/issue-triage.md`.

## Local Branches And Pull Requests

- Existing branch: `agent/setup-readiness-fixes`.
- Existing draft PR: #10, `[codex] Fix setup readiness triage issues`.
- Current branch includes local triage notes and escalations for the unresolved decision-gated issues.

## Blockers

- Maintainer decision needed for #2 Same Network behavior.
- Maintainer decision needed for #3 Remote Desktop pairing trust model.
- Maintainer approval needed before #8 screenshots are captured, committed, or used publicly.
64 changes: 64 additions & 0 deletions ops/agents/triage/2026-07-03-issue-triage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# CodePilot Issue Triage - 2026-07-03

Remote write policy: public GitHub writes are allowed after privacy audit when they directly advance launch readiness. This pass did not need new remote issue comments or label edits.

## Follow-Up Pass

- Rechecked the open GitHub issue queue for CodePilot.
- No new open issues were found after the prior 2026-07-02 triage pass.
- Rechecked open issues #1, #2, #3, #4, and #8.
- Existing labels still match the proposed component and severity triage.
- Draft PR #10 remains open, draft, `CLEAN`, and attached to `agent/setup-readiness-fixes`.
- Draft PR #10 has a successful `Test and Audit` check from CI.

## Reviewed Issues

- #1 Add permissions and notification readiness to the Mac setup checklist
- Proposed labels: `setup`, `remote-desktop`, `mac`, `notifications`, `severity: low`.
- Triage: actionable low-risk setup visibility issue.
- Status: addressed by draft PR #10 on branch `agent/setup-readiness-fixes`.
- #2 Support or disable the iOS Same Network setup path
- Proposed labels: `setup`, `ios`, `gateway`, `severity: medium`.
- Triage: still blocked on product/security decision for LAN binding versus disabling Same Network.
- Status: escalation remains in `ops/agents/escalations/issue-triage.md`.
- #3 Require clear Remote Desktop pairing approval in setup
- Proposed labels: `setup`, `remote-desktop`, `mac`, `ios`, `severity: high`.
- Triage: still blocked on Remote Desktop trust-model decision before public beta.
- Status: escalation remains in `ops/agents/escalations/issue-triage.md`.
- #4 TryCloudflare setup step waits forever for the tunnel process
- Proposed labels: `bug`, `setup`, `mac`, `cloudflare`, `severity: medium`.
- Triage: actionable setup bug reproduced by code inspection in the prior pass.
- Status: addressed by draft PR #10 on branch `agent/setup-readiness-fixes`.
- #8 Prepare sanitized public screenshot set
- Proposed labels: `documentation`, `severity: low`.
- Triage: screenshot capture and public use require maintainer approval.
- Status: escalation remains in `ops/agents/escalations/issue-triage.md`.

## Local Branches And Pull Requests

- Existing branch: `agent/setup-readiness-fixes`.
- Existing draft PR: #10, `[codex] Fix setup readiness triage issues`.
- No new branch was needed in this pass.

## Blockers

- Maintainer decision needed for #2 Same Network behavior.
- Maintainer decision needed for #3 Remote Desktop pairing trust model.
- Maintainer approval needed before #8 screenshots are captured, committed, or used publicly.

## Launch Autonomy Pass

- Rechecked the open GitHub issue queue during the July 3 unattended issue-triage run.
- No new open issues were found beyond #1, #2, #3, #4, and #8.
- Proposed component and severity labels remain unchanged for all reviewed issues.
- Draft PR #10 remains open and draft on `agent/setup-readiness-fixes`.
- The latest visible PR #10 CI check, `Test and Audit`, is passing.
- No GitHub issue comments, label edits, screenshot captures, release actions, or external system mutations were needed in this pass.

## Launch Autonomy Pass - 2026-07-03T08:42:47Z

- Rechecked the open GitHub issue queue for #1, #2, #3, #4, and #8.
- No new open issues were found, and no issue labels needed changes.
- Draft PR #10 remains the active low-risk fix branch for #1 and #4.
- Privacy audit passed before this local note update and before any public-write-eligible action.
- No new GitHub issue comments, label edits, branches, PRs, screenshots, release actions, or external system mutations were needed.
Loading