diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 296c00fd..2c1c35f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: - name: Gateway tests working-directory: gateway - run: python3 -m unittest test_codex_phone_gateway.py + run: python3 -m unittest test_codex_phone_gateway.py test_remote_desktop_gateway.py - name: Fastlane syntax run: ruby -c ios/CodexPhone/fastlane/Fastfile diff --git a/.gitignore b/.gitignore index 2735c102..344822f6 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ __pycache__/ .pytest_cache/ # Local runtime state and secrets +.private/ .agents/ .superpowers/ AGENTS.md diff --git a/README.md b/README.md index baa6a848..2d638302 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ See: - [Support](docs/SUPPORT.md) - [Changelog](docs/CHANGELOG.md) - [App Store Metadata Draft](docs/APP_STORE_METADATA_DRAFT.md) -- [Screenshot Plan](docs/SCREENSHOTS.md) +- [Screenshot Plan](metadata/screenshots/README.md) - [Security](docs/SECURITY.md) - [Architecture](docs/ARCHITECTURE.md) - [Troubleshooting](docs/TROUBLESHOOTING.md) diff --git a/Tests/CodexAccountSwitcherTests/RemoteDesktopCoordinatorTests.swift b/Tests/CodexAccountSwitcherTests/RemoteDesktopCoordinatorTests.swift index 2bf606dc..dc8ac6f1 100644 --- a/Tests/CodexAccountSwitcherTests/RemoteDesktopCoordinatorTests.swift +++ b/Tests/CodexAccountSwitcherTests/RemoteDesktopCoordinatorTests.swift @@ -29,9 +29,9 @@ final class RemoteDesktopCoordinatorTests: XCTestCase { let pending = try coordinator.beginPairing( deviceID: "phone-1", - name: "Tony's iPhone", + name: "Test iPhone", publicKeyRawRepresentation: publicKey, - macName: "Mac mini" + macName: "Test Mac" ) XCTAssertEqual(coordinator.snapshot.pendingPairing?.deviceID, "phone-1") @@ -42,9 +42,9 @@ final class RemoteDesktopCoordinatorTests: XCTestCase { let approvalPending = try coordinator.beginPairing( deviceID: "phone-1", - name: "Tony's iPhone", + name: "Test iPhone", publicKeyRawRepresentation: publicKey, - macName: "Mac mini" + macName: "Test Mac" ) _ = try coordinator.verifyPendingPairing( challengeID: approvalPending.challenge.id, @@ -104,9 +104,9 @@ final class RemoteDesktopCoordinatorTests: XCTestCase { let key = P256.Signing.PrivateKey() let challenge = coordinator.pairingStore.issueChallenge( deviceID: "phone-1", - name: "Tony's iPhone", + name: "Test iPhone", publicKeyRawRepresentation: key.publicKey.rawRepresentation, - macName: "Mac mini" + macName: "Test Mac" ) let approval = try coordinator.pairingStore.verifyChallenge( challenge, diff --git a/docs/APP_STORE_METADATA_DRAFT.md b/docs/APP_STORE_METADATA_DRAFT.md index 2ae77794..d8fdddc2 100644 --- a/docs/APP_STORE_METADATA_DRAFT.md +++ b/docs/APP_STORE_METADATA_DRAFT.md @@ -18,7 +18,7 @@ Public beta for Codex CLI users who want iPhone visibility, file uploads, turn c CodePilot is a Mac menu bar app, local gateway, and iPhone companion for AI coding users who already run Codex CLI on a Mac. -Use the iPhone app to check active account status, follow threads, upload files, steer or stop supported turns, receive turn-finished notifications, and connect through your own same-network or Cloudflare Tunnel setup. The Mac stays in your control, and the current beta does not require a hosted CodePilot account service. +Use the iPhone app to check active account status, follow threads, upload files, steer or stop supported turns, receive turn-finished notifications, and connect through your own Cloudflare Tunnel setup. The Mac stays in your control, and the current beta does not require a hosted CodePilot account service. The beta is focused on setup clarity, connection reliability, privacy-safe diagnostics, and practical remote workflows for existing Codex CLI users. Some internals still use Codex-specific names while the public CodePilot product surface is prepared for broader provider support later. @@ -32,7 +32,7 @@ Codex, coding agent, developer tools, Mac, iPhone, remote coding, AI coding, Tes Install CodePilot on a Mac that already runs Codex CLI, create a saved account profile, start the gateway, connect the iPhone app with the gateway URL and token, and verify thread visibility, file upload, turn control, notifications, and setup recovery messages. -Please report whether you used same-network access or Cloudflare Tunnel. Remove account names, gateway URLs, tokens, hostnames, local paths, private prompts, screenshots, and logs containing private data before sharing feedback. +Please include the connection and recovery flow you tested. Remove account names, gateway URLs, tokens, hostnames, local paths, private prompts, screenshots, and logs containing private data before sharing feedback. ## Support URL @@ -50,7 +50,7 @@ Use only sanitized demo data: - Example hostnames such as `codepilot.example.com`. - No tokens, auth details, private prompts, local paths, Apple identifiers, personal account names, private screenshots, or live desktop contents. -See [Screenshot Plan](SCREENSHOTS.md). +See the [App Store screenshot checklist](../metadata/screenshots/README.md). ## Approval Gates diff --git a/docs/PRODUCTION_READINESS_IMPLEMENTATION_PLAN.md b/docs/PRODUCTION_READINESS_IMPLEMENTATION_PLAN.md index 58362bfe..596364ba 100644 --- a/docs/PRODUCTION_READINESS_IMPLEMENTATION_PLAN.md +++ b/docs/PRODUCTION_READINESS_IMPLEMENTATION_PLAN.md @@ -323,7 +323,7 @@ curl -fsS -X POST http://127.0.0.1:8787/codexphone/api/build curl -fsS http://127.0.0.1:8787/codexphone/api/status ``` -Wait until `current.state == "complete"`, then verify the manifest and IPA from `https://ota.sprenkeling.co` return HTTP 200. +Wait until `current.state == "complete"`, then verify the manifest and IPA from the configured OTA domain return HTTP 200. - [ ] **Step 8: Commit** @@ -497,8 +497,8 @@ Expected: `current.state` becomes `complete`. Run: ```sh -curl -I https://ota.sprenkeling.co/codexphone/manifest.plist -curl -I https://ota.sprenkeling.co/codexphone/CodePilot.ipa +curl -I https://ota.example.com/codexphone/manifest.plist +curl -I https://ota.example.com/codexphone/CodePilot.ipa ``` Expected: HTTP 200 or the exact current generated manifest/IPA paths return HTTP 200. diff --git a/docs/superpowers/plans/2026-06-27-secure-remote-desktop.md b/docs/superpowers/plans/2026-06-27-secure-remote-desktop.md index aa7a7512..cd0a48f5 100644 --- a/docs/superpowers/plans/2026-06-27-secure-remote-desktop.md +++ b/docs/superpowers/plans/2026-06-27-secure-remote-desktop.md @@ -632,11 +632,11 @@ Verify pairing approval, Face ID session start, Wi-Fi direct path, cellular TURN Run: ```bash -cd ios +cd /path/to/ios-release-worktree python3 scripts/dke_ota_build.py build --app codexphone --branch release ``` -Expected: status reaches `complete`; output reports bundle ID `com.tony.codexphone`; both public manifest and IPA return HTTP 200; the OTA install updates the configured CodePilot app rather than creating another instance. +Expected: status reaches `complete`; output reports the canonical CodePilot iOS bundle ID; both public manifest and IPA return HTTP 200; the OTA install updates the configured CodePilot app rather than creating another instance. - [ ] **Step 7: Commit** diff --git a/docs/superpowers/plans/2026-07-01-codepilot-launch-agent-system.md b/docs/superpowers/plans/2026-07-01-codepilot-launch-agent-system.md index 506d14f1..d62920f3 100644 --- a/docs/superpowers/plans/2026-07-01-codepilot-launch-agent-system.md +++ b/docs/superpowers/plans/2026-07-01-codepilot-launch-agent-system.md @@ -15,14 +15,14 @@ - Agents must not invent credentials, create accounts with fake identity, spam communities, or bypass platform rules. - Reddit promotion must be transparent: disclose the creator relationship and follow subreddit rules. - Anything involving App Store submission, payments, pricing, legal terms, public claims, or user data policy requires explicit human approval. -- Agents should ping Tony in the current Codex thread only when intervention is necessary. +- Agents should ping the maintainer in the current Codex thread only when intervention is necessary. - Routine findings should become GitHub issues, PRs, docs updates, or release notes without a ping. - Every code change must be tested before being marked complete. - Every iOS app change must trigger the project OTA build process before completion. ## Escalation Rules -Ping Tony only for: +Ping the maintainer only for: - Apple, Cloudflare, GitHub, Reddit, or payment-provider authentication. - App Store Connect agreements, pricing, tax, banking, privacy, age rating, export compliance, or review submission approval. @@ -31,7 +31,7 @@ Ping Tony only for: - A public post, launch announcement, paid promotion, or subreddit submission that needs final approval. - A product decision that affects subscription value, pricing, usage limits, or public positioning. -Do not ping Tony for: +Do not ping the maintainer for: - Routine dependency updates that pass tests. - GitHub issue triage. @@ -146,7 +146,7 @@ Do not ping Tony for: - Track feedback and convert real product problems into GitHub issues. - Maintain a transparent creator profile. -**Important constraint:** The agent can prepare the Reddit account plan, profile text, and posts, but Tony must create or approve any Reddit account and final posts. Creating accounts and posting must comply with Reddit rules and subreddit rules. +**Important constraint:** The agent can prepare the Reddit account plan, profile text, and posts, but the maintainer must create or approve any Reddit account and final posts. Creating accounts and posting must comply with Reddit rules and subreddit rules. **Outputs:** @@ -216,7 +216,7 @@ Do not ping Tony for: 4. **Prepare community launch** - Community Growth Agent drafts Reddit account profile and launch posts. - - Tony approves account/posting strategy. + - Maintainer approves account/posting strategy. - Agent schedules staged, rule-compliant posts. 5. **Start beta loop** @@ -229,7 +229,7 @@ Do not ping Tony for: - Revenue Agent proposes tiers and pricing. - Presence Agent updates website and App Store copy. - Security Agent signs off on public release. - - Tony approves pricing, legal, and App Store submission. + - Maintainer approves pricing, legal, and App Store submission. ## Proposed Recurring Automations diff --git a/gateway/test_codex_phone_gateway.py b/gateway/test_codex_phone_gateway.py index c92fbe5d..bf5ff6e0 100644 --- a/gateway/test_codex_phone_gateway.py +++ b/gateway/test_codex_phone_gateway.py @@ -2235,7 +2235,7 @@ def test_start_turn_rejects_second_running_turn_in_same_thread(self): } old_run_turn = GatewayState.run_turn - GatewayState.run_turn = lambda self, job_id, thread, prompt, attachments, resume_existing=True: None + GatewayState.run_turn = lambda self, job_id, thread, prompt, attachments, resume_existing=True, reasoning_effort=None: None try: state = GatewayState(Path("/tmp/codex"), "token", Path("/missing-codex"), False) state.get_thread = lambda thread_id: { @@ -2265,7 +2265,7 @@ def test_start_turn_allows_running_turn_in_different_thread(self): } old_run_turn = GatewayState.run_turn - GatewayState.run_turn = lambda self, job_id, thread, prompt, attachments, resume_existing=True: None + GatewayState.run_turn = lambda self, job_id, thread, prompt, attachments, resume_existing=True, reasoning_effort=None: None try: state = GatewayState(Path("/tmp/codex"), "token", Path("/missing-codex"), False) state.get_thread = lambda thread_id: { diff --git a/ios/CodexPhone/fastlane/Fastfile b/ios/CodexPhone/fastlane/Fastfile index 531761cd..a5e9cc63 100644 --- a/ios/CodexPhone/fastlane/Fastfile +++ b/ios/CodexPhone/fastlane/Fastfile @@ -52,10 +52,15 @@ platform :ios do def codex_phone_beta_description ENV.fetch( "BETA_APP_DESCRIPTION", - "CodePilot lets you securely connect an iPhone to your own Mac gateway for remote coding sessions, thread management, usage status, file upload, and remote desktop control." + "CodePilot connects an iPhone to your own Mac gateway for remote coding sessions, thread management, usage status, file upload, file previews, and local Mac web links. Remote Desktop is not included in this beta." ) end + def codex_phone_testflight_changelog + notes_path = File.expand_path("../../../metadata/testflight/0.1/en-US.md", __dir__) + File.read(notes_path) + end + def codex_phone_beta_review_info info = { contact_email: codex_phone_feedback_email, @@ -77,7 +82,7 @@ platform :ios do "default" => { feedback_email: codex_phone_feedback_email, description: codex_phone_beta_description, - privacy_policy_url: ENV.fetch("APP_PRIVACY_POLICY_URL", "https://codepilotios.github.io/CodePilot/privacy.html"), + privacy_policy_url: ENV.fetch("APP_PRIVACY_POLICY_URL"), marketing_url: ENV.fetch("APP_MARKETING_URL", "https://github.com/codepilotios/CodePilot") } } @@ -183,7 +188,7 @@ platform :ios do beta_app_review_info: codex_phone_beta_review_info, localized_app_info: codex_phone_localized_app_info, demo_account_required: false, - changelog: options[:changelog] || "CodePilot remote client update." + changelog: options[:changelog] || codex_phone_testflight_changelog ) end @@ -198,7 +203,7 @@ platform :ios do ipa: ipa_path, skip_waiting_for_build_processing: true, distribute_external: false, - changelog: options[:changelog] || "CodePilot remote client update." + changelog: options[:changelog] || codex_phone_testflight_changelog ) end @@ -222,7 +227,7 @@ platform :ios do beta_app_review_info: codex_phone_beta_review_info, localized_app_info: codex_phone_localized_app_info, demo_account_required: false, - changelog: options[:changelog] || "CodePilot remote client update." + changelog: options[:changelog] || codex_phone_testflight_changelog ) end diff --git a/metadata/screenshots/README.md b/metadata/screenshots/README.md new file mode 100644 index 00000000..89cd6406 --- /dev/null +++ b/metadata/screenshots/README.md @@ -0,0 +1,20 @@ +# App Store Screenshot Checklist + +No App Store-ready screenshots are currently present in the repository. + +The minimum practical submission set is 1–10 screenshots for the 6.9-inch iPhone display. Use an accepted portrait canvas such as 1320×2868, 1290×2796, or 1260×2736 pixels. Export PNG or JPEG files without alpha or transparency. No iPad set is required for the current iPhone-only target. + +Prepare approved screenshots for: + +- First connection screen with the Cloudflare connection choice. +- Gateway health or setup status after a successful connection. +- Total-credit status with synthetic account names and balances. +- Total Credit Live Activity on a supported Lock Screen or Dynamic Island. +- Thread list or active turn view. +- File preview from a local Mac path. +- Local Mac web page opened through the gateway. +- Error recovery state with non-sensitive copy. + +Do not include Remote Desktop in the 0.1 screenshot set while its paired-device and session-authorization protections remain release-blocked. + +All screenshots must avoid real tokens, private hostnames, private account names, private file paths, private emails, and personal identifiers. diff --git a/metadata/testflight/0.1/en-US.md b/metadata/testflight/0.1/en-US.md new file mode 100644 index 00000000..6f0674cd --- /dev/null +++ b/metadata/testflight/0.1/en-US.md @@ -0,0 +1,24 @@ +# CodePilot 0.1 TestFlight Notes + +## What To Test + +Test CodePilot from your iPhone while your Mac gateway handles coding turns, account status, file previews, and local Mac web links. + +- First connection with a Cloudflare gateway URL. +- Gateway token entry and recovery copy for 401, 403, 502, and offline states. +- Account status, account switching, and stale-auth recovery. +- Total available credit across configured accounts, including refresh timing and per-account breakdowns. +- Total Credit Live Activity updates on the Lock Screen and Dynamic Island. +- Reset-credit confirmation and refresh behavior for the selected account. +- Starting, following, steering, and stopping a coding turn. +- File preview links from local paths, Markdown links, and file URLs. +- Local Mac web URL opening through the gateway. + +Remote Desktop is not included in this beta while its paired-device and session-authorization protections are being completed. + +## Beta Requirements + +- CodePilot Mac app installed and running. +- CodePilot gateway reachable from the iPhone. +- Valid gateway token copied from the Mac setup flow. +- Live Activities enabled when testing total-credit updates. diff --git a/metadata/version/0.1/en-US.json b/metadata/version/0.1/en-US.json new file mode 100644 index 00000000..b2c8b206 --- /dev/null +++ b/metadata/version/0.1/en-US.json @@ -0,0 +1,13 @@ +{ + "locale": "en-US", + "version": "0.1", + "name": "CodePilot", + "subtitle": "Mac coding agent from iPhone", + "keywords": "coding,assistant,developer,remote,mac,iphone,automation", + "promotionalText": "Connect your iPhone to your Mac gateway for beta testing, remote turns, file previews, and local Mac web links.", + "description": "CodePilot connects your iPhone to a Mac running Codex CLI and the CodePilot gateway, so you can monitor and steer coding-agent work away from your desk.\n\nUse CodePilot to start and follow turns, switch configured accounts, inspect gateway status, preview files, and open local Mac web URLs through the gateway.\n\nThis beta is intended for developers who already run Codex CLI, the CodePilot Mac app, and the gateway. You will need a reachable gateway URL and token from your Mac setup. Remote Desktop is excluded from this beta while its paired-device and session-authorization protections are completed.", + "whatsNew": "See your total remaining Codex credit at a glance with a Live Activity, and use an available reset credit for the account you choose without returning to your Mac.\n\nNew:\n- Track combined available credit across configured accounts from the Lock Screen and Dynamic Island.\n- Use a rate-limit reset credit for a specific account after a clear confirmation.\n\nImproved:\n- Clearer gateway recovery when a token is missing, the gateway is offline, or authentication needs attention.\n- Better file previews for local paths and Markdown links.\n- Clearer account status and turn controls.\n\nFixed:\n- More stable gateway event formatting, turn-state handling, and background-session cleanup.\n- More reliable per-account credit refresh and reset state after manual account switches.", + "supportUrl": "TODO: maintainer-provided support URL", + "privacyPolicyUrl": "TODO: maintainer-provided privacy policy URL", + "reviewNotes": "TODO: add maintainer-approved review notes, demo prerequisites, and any required compliance details before upload." +} diff --git a/ops/agents/escalations/release-readiness.md b/ops/agents/escalations/release-readiness.md new file mode 100644 index 00000000..3d9a2bf9 --- /dev/null +++ b/ops/agents/escalations/release-readiness.md @@ -0,0 +1,58 @@ +# CodePilot Release Readiness Escalation + +Date: 2026-07-19 + +Status: CodePilot is not ready for a new OTA, TestFlight, or App Store release. Local builds, gateway tests, and public CI are healthy, but release identity, signing/App Store Connect access, privacy work, screenshots, and review metadata remain blocked. + +## Verified Locally + +- `scripts/privacy-audit.sh`: passed its generic private-path, email, and secret-pattern checks. Supplemental current-tree scans found no credible secrets, private paths, local username/hostname, or private email exposure, and Git is configured with the public CodePilot identity. The optional project-specific private-identifier denylist was not available in this worktree, so this is not a complete private-name/host signoff. +- `swift test`: passed; 57 tests, 0 failures. +- The release-readiness draft PR and the open draft PRs referenced below remain mergeable, and their latest completed public CI checks passed. This worktree was also re-verified locally on July 19. +- Gateway unit tests: passed with Python 3.13; 112 tests, 0 failures. An alternate Xcode-provided Python 3.9 invocation cannot import `tomllib`, confirming that local gateway verification must use the documented Python 3.11-or-newer runtime. +- macOS Swift release build: passed. +- iOS clean Release simulator build: passed with code signing disabled. +- Unsigned generic-device iOS Release archive compile: passed with Xcode 26.5. +- iOS simulator tests: passed; 37 tests, 0 failures. +- Cloudflare setup-script tests: passed. +- The iOS build emits one non-blocking compiler warning for an unused `resetRateLimit` return value; release compilation still succeeds. +- Fastlane Ruby syntax, iOS `Info.plist`, entitlements, and version metadata JSON validation passed. With the supported Ruby 4.0.4 and Bundler 4.0.11 selected explicitly, `bundle check` and Fastlane lane discovery pass. The default unattended working-directory environment still selects system Ruby 2.6 and cannot start the lockfile-required Bundler, so the release entrypoint is not yet deterministic. No signed archive or upload lane was run. +- Canonical 0.1 App Store copy remains within the local field limits: 9-character name, 28-character subtitle, 55-character keywords, 111-character promotional text, 603-character description, and 770-character What's New text. No user-facing source changes have landed since those release notes were prepared. +- Public-write guard, agent-runner model-selection, and scheduler-lock tests: passed. +- A July 19 read-only check of the canonical local OTA status endpoint still points to a July 8 build with no recorded source commit. Its tokenized manifest and IPA both returned HTTP 200, but the manifest does not match the current bundle ID and the public CodePilot install page returned HTTP 403. No OTA build or external-state mutation was performed in this run. + +## Release Blockers + +- **Remote Desktop pairing is not enforced on the active capture/control paths (issue #25 remains open and labeled critical).** The frame, input, and WebRTC signaling routes accept arbitrary session identifiers after the shared gateway bearer token check; they do not require a trusted device or validate an active signed lease. The iOS control view also bypasses the available session-start API. This can let any gateway-token holder view or control the Mac without the documented per-device pairing boundary. Draft PR #26 records the cross-stack remediation and verification scope, but does not implement the fix. Block OTA, TestFlight, and App Store distribution until the native host, gateway routes, and iOS client enforce one lease end-to-end with regression tests for unpaired, expired, revoked, replayed, and mismatched sessions. +- **Canonical bundle identity requires a maintainer decision.** The latest distributed OTA build uses a legacy identifier that differs from the current Xcode project and Fastlane default. Publishing without aligning OTA, signing, and App Store Connect could create a second installation. +- The latest OTA build is stale relative to current source and has no recorded source commit. No OTA build was triggered because this run is not authorized to mutate external distribution systems. +- The public OTA install page currently returns HTTP 403. Restore unauthenticated install-page access and verify it from a real iPhone before treating OTA distribution as release-ready. +- App Store Connect inspection was unavailable in this run. The Apple developer team setting is empty, and allowed read-only `asc` commands recurse into the command guard because the runner does not export the real `asc` executable. Issue #30 tracks the guard fix. The app record, processed builds, availability, version attachment, and strict validation therefore could not be checked. +- The unattended Fastlane entrypoint does not select the supported Ruby/Bundler toolchain deterministically. Add a repository-owned release wrapper or equivalent environment setup before relying on a scheduled TestFlight build. +- The project-specific private-identifier denylist is unavailable in this release worktree. Restore the ignored local denylist before treating the public-content privacy audit as complete. +- Public Git history contains 11 legacy commits authored with a non-CodePilot identity. Review the redacted author metadata and decide whether it is intentionally public before treating repository-history privacy as complete. +- No signed device archive/export was run. TestFlight upload, group distribution, build processing, and App Store submission were intentionally not run. +- `metadata/version/0.1/en-US.json` still needs approved support and privacy-policy URLs plus App Review notes/contact and gateway access instructions. +- The App Store draft and canonical JSON currently disagree on subtitle and promotional/keyword copy; reconcile one approved canonical metadata set before staging. +- App Privacy, privacy labels, age rating, content rights, export compliance, category, availability, and any pricing/legal settings require maintainer confirmation in App Store Connect. +- No app-owned `PrivacyInfo.xcprivacy` is present despite required-reason API use. Draft PR #22 prepares gateway-token Keychain migration, file-access scoping, localhost-capability constraints, and additional gateway hardening; it remains open, mergeable, and green but must be reconciled and verified before release. +- The published privacy, support, and screenshot URLs currently return HTTP 404, and the corresponding pages are absent on this branch. Draft PR #17 prepares those pages; it remains open, mergeable, and green but must be reconciled before the links can be treated as release-ready. +- App Store-ready screenshots are absent. All future captures need synthetic accounts, hosts, paths, and tokens. +- All nine App icon files include alpha channels, including the marketing icon; flatten and validate them before archive upload. +- Fastlane now uses the prepared 0.1 What-to-Test notes by default, excludes Remote Desktop from its default beta description, and requires an approved privacy-policy URL for external distribution. The App Store JSON is still not connected to a staging/validation lane, and external TestFlight metadata still lacks an approved feedback email and review contact. +- The locally built Mac app is arm64-only; Intel/universal distribution has not been verified. + +## Prepared Artifacts + +- Draft TestFlight notes, including total-credit, Live Activity, and reset-credit coverage: `metadata/testflight/0.1/en-US.md`. +- Draft App Store metadata and benefit-focused release notes: `metadata/version/0.1/en-US.json`. +- Screenshot capture/redaction checklist, including sanitized total-credit and Live Activity captures: `metadata/screenshots/README.md`. + +## Maintainer Actions + +1. Confirm the canonical iOS bundle identifier and align Xcode, OTA, signing, and the App Store Connect record before another distributed build. +2. Approve and verify an end-to-end Remote Desktop trust boundary that requires a paired device and active signed lease for capture, signaling, and input. +3. Provide an authorized release environment for a signed archive and read-only App Store Connect validation. +4. Approve the support/privacy URLs, review contact and gateway-access instructions, privacy answers, age rating, content-rights answer, and export-compliance answer. +5. Supply a sanitized 6.9-inch iPhone screenshot set and approve remediation of the token-storage/privacy-manifest issues. +6. Review the redacted legacy Git author metadata and confirm whether it may remain in public history. diff --git a/scripts/install-phone-gateway-agent.sh b/scripts/install-phone-gateway-agent.sh index b6350bb3..e08cf775 100755 --- a/scripts/install-phone-gateway-agent.sh +++ b/scripts/install-phone-gateway-agent.sh @@ -19,7 +19,31 @@ LABEL="${CODEPILOT_GATEWAY_LAUNCHD_LABEL:-io.codepilot.phone-gateway}" PLIST="$HOME/Library/LaunchAgents/$LABEL.plist" mkdir -p "$HOME/Library/LaunchAgents" "$HOME/Library/Logs" -/usr/bin/python3 - "$ROOT" "$PLIST" "$LABEL" <<'PY' +gateway_python="" +python_candidates=( + "${CODEPILOT_GATEWAY_PYTHON:-}" + "$(command -v python3 2>/dev/null || true)" + "$(command -v python3.13 2>/dev/null || true)" + "$(command -v python3.12 2>/dev/null || true)" + "$(command -v python3.11 2>/dev/null || true)" + /opt/homebrew/bin/python3 + /usr/local/bin/python3 + /usr/bin/python3 +) + +for candidate in "${python_candidates[@]}"; do + if [[ -n "$candidate" && -x "$candidate" ]] && "$candidate" -c 'import tomllib' >/dev/null 2>&1; then + gateway_python="$candidate" + break + fi +done + +if [[ -z "$gateway_python" ]]; then + echo "CodePilot gateway requires Python 3.11 or newer (tomllib is unavailable)." >&2 + exit 1 +fi + +/usr/bin/python3 - "$ROOT" "$PLIST" "$LABEL" "$gateway_python" <<'PY' import plistlib import sys from pathlib import Path @@ -27,10 +51,8 @@ from pathlib import Path root = Path(sys.argv[1]) plist_path = Path(sys.argv[2]) label = sys.argv[3] +python_path = Path(sys.argv[4]) env_path = Path.home() / ".codex-account-switcher" / "phone-gateway.env" -python_path = Path("/usr/local/bin/python3") -if not python_path.is_file(): - python_path = Path("/usr/bin/python3") allowed_env_keys = { "CODEX_PHONE_APNS_CERT_PATH", "CODEX_PHONE_APNS_CERT_KEY_PATH", diff --git a/scripts/privacy-audit.sh b/scripts/privacy-audit.sh index 11557ad3..4885b300 100755 --- a/scripts/privacy-audit.sh +++ b/scripts/privacy-audit.sh @@ -9,48 +9,59 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then exit 2 fi -private_patterns_file="${CODEPILOT_PRIVACY_PATTERNS_FILE:-$HOME/.codepilot-privacy-patterns}" +candidate_files=("${(@f)$(git ls-files --cached --others --exclude-standard)}") + +if (( ${#candidate_files[@]} == 0 )); then + echo "privacy audit skipped: no candidate files" >&2 + exit 0 +fi + +private_patterns_file="${CODEPILOT_PRIVATE_AUDIT_PATTERNS_FILE:-${CODEPILOT_PRIVACY_PATTERNS_FILE:-$ROOT/.private/privacy-audit-patterns.txt}}" + +if [[ -n "${CODEPILOT_PRIVATE_AUDIT_PATTERNS_FILE:-}${CODEPILOT_PRIVACY_PATTERNS_FILE:-}" && ! -f "$private_patterns_file" ]]; then + echo "privacy audit failed: configured private-pattern file is unavailable" >&2 + exit 2 +fi + +if [[ -f "$private_patterns_file" ]]; then + if LC_ALL=C grep -nI -F -f "$private_patterns_file" -- "${candidate_files[@]}"; then + echo "privacy audit failed: repository files contain private identifiers" >&2 + exit 1 + fi +fi + users_dir_pattern="/$(printf %s Users)/[^[:space:]\"']+" generic_private_patterns=( "$users_dir_pattern" '[A-Za-z][A-Za-z0-9._%+-]*@[A-Za-z][A-Za-z0-9.-]*\.[A-Za-z]{2,}' ) -pattern="$(IFS='|'; echo "${generic_private_patterns[*]}")" - -if [[ -f "$private_patterns_file" ]]; then - while IFS= read -r private_pattern; do - [[ -n "$private_pattern" && "${private_pattern:0:1}" != "#" ]] || continue - pattern="$pattern|$private_pattern" - done < "$private_patterns_file" -fi - -tracked_files=("${(@f)$(git ls-files)}") audit_files=() -for file in "${tracked_files[@]}"; do +for file in "${candidate_files[@]}"; do [[ "$file" == "scripts/privacy-audit.sh" ]] && continue audit_files+=("$file") done -if git grep -n -I -E "$pattern" -- "${audit_files[@]}"; then - echo "privacy audit failed: tracked files contain private identifiers" >&2 +generic_pattern="$(IFS='|'; echo "${generic_private_patterns[*]}")" +if LC_ALL=C grep -nI -E "$generic_pattern" -- "${audit_files[@]}"; then + echo "privacy audit failed: repository files contain private paths or email addresses" >&2 exit 1 fi secret_patterns=( - "g""hp_[A-Za-z0-9_]+" - "github_""pat_[A-Za-z0-9_]+" - "s""k-[A-Za-z0-9]{20,}" + "g""hp_[A-Za-z0-9_]{20,}" + "github_""pat_[A-Za-z0-9_]{20,}" + "(^|[^A-Za-z0-9])s""k-[A-Za-z0-9]{20,}" "-----BEGIN (RSA|OPENSSH|PRIVATE) ""KEY" 'Bearer [A-Za-z0-9._-]{20,}' - "client_""secret" - "private_""key" + "client_""secret[[:space:]]*[:=]" + "private_""key[[:space:]]*[:=]" ) secret_pattern="$(IFS='|'; echo "${secret_patterns[*]}")" -if git grep -n -I -E "$secret_pattern" -- "${audit_files[@]}"; then - echo "privacy audit failed: tracked files contain secret-looking material" >&2 +if LC_ALL=C grep -nI -E "$secret_pattern" -- "${audit_files[@]}"; then + echo "privacy audit failed: repository files contain secret-looking material" >&2 exit 1 fi diff --git a/scripts/start-phone-gateway.sh b/scripts/start-phone-gateway.sh index eaa2d23b..3aa880de 100755 --- a/scripts/start-phone-gateway.sh +++ b/scripts/start-phone-gateway.sh @@ -2,7 +2,29 @@ set -euo pipefail cd "$(dirname "$0")/.." -if [[ -x /usr/local/bin/python3 ]]; then - exec /usr/local/bin/python3 gateway/codex_phone_gateway.py --host 127.0.0.1 --port 18790 + +gateway_python="" +python_candidates=( + "${CODEPILOT_GATEWAY_PYTHON:-}" + "$(command -v python3 2>/dev/null || true)" + "$(command -v python3.13 2>/dev/null || true)" + "$(command -v python3.12 2>/dev/null || true)" + "$(command -v python3.11 2>/dev/null || true)" + /opt/homebrew/bin/python3 + /usr/local/bin/python3 + /usr/bin/python3 +) + +for candidate in "${python_candidates[@]}"; do + if [[ -n "$candidate" && -x "$candidate" ]] && "$candidate" -c 'import tomllib' >/dev/null 2>&1; then + gateway_python="$candidate" + break + fi +done + +if [[ -z "$gateway_python" ]]; then + echo "CodePilot gateway requires Python 3.11 or newer (tomllib is unavailable)." >&2 + exit 1 fi -exec /usr/bin/python3 gateway/codex_phone_gateway.py --host 127.0.0.1 --port 18790 + +exec "$gateway_python" gateway/codex_phone_gateway.py --host 127.0.0.1 --port 18790 diff --git a/scripts/tests/codepilot-agent-public-write-guard-test.sh b/scripts/tests/codepilot-agent-public-write-guard-test.sh index 020d49a4..7741021f 100755 --- a/scripts/tests/codepilot-agent-public-write-guard-test.sh +++ b/scripts/tests/codepilot-agent-public-write-guard-test.sh @@ -28,6 +28,7 @@ chmod +x "$TMP_ROOT/gh" "$TMP_ROOT/git" export CODEPILOT_GUARD_CAPTURE="$TMP_ROOT/capture" export CODEPILOT_AGENT_REAL_GH="$TMP_ROOT/gh" export CODEPILOT_AGENT_REAL_GIT="$TMP_ROOT/git" +export CODEPILOT_AGENT_PUBLIC_AUTONOMY="review" "$GUARD_BIN/gh" issue list --repo codepilotios/codepilot grep -qx 'list' "$TMP_ROOT/capture" diff --git a/scripts/tests/codepilot-agent-runner-model-test.sh b/scripts/tests/codepilot-agent-runner-model-test.sh index f1b768bf..b0e7dd03 100755 --- a/scripts/tests/codepilot-agent-runner-model-test.sh +++ b/scripts/tests/codepilot-agent-runner-model-test.sh @@ -1,19 +1,23 @@ #!/usr/bin/env zsh set -euo pipefail -ROOT="$(mktemp -d)" -STATE_DIR="$ROOT/state" +TMP_ROOT="$(mktemp -d)" +ROOT="$TMP_ROOT/repo" +STATE_DIR="$TMP_ROOT/state" LOG_DIR="$ROOT/logs" JOB="health-watch" CAPTURE="$ROOT/codex-args" PROMPT_CAPTURE="$ROOT/codex-prompt" +unset CODEPILOT_AGENT_MODEL CODEPILOT_AGENT_REASONING_EFFORT + cleanup() { - rm -rf "$ROOT" + rm -rf "$TMP_ROOT" } trap cleanup EXIT mkdir -p \ + "$ROOT" \ "$ROOT/ops/agents/prompts" \ "$ROOT/ops/agents/escalations" \ "$STATE_DIR/worktrees/$JOB"