agent-control-plane 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +589 -0
- package/SKILL.md +149 -0
- package/assets/workflow-catalog.json +57 -0
- package/bin/audit-issue-routing.sh +74 -0
- package/bin/issue-resource-class.sh +58 -0
- package/bin/label-follow-up-issues.sh +114 -0
- package/bin/pr-risk.sh +532 -0
- package/bin/sync-pr-labels.sh +112 -0
- package/hooks/heartbeat-hooks.sh +573 -0
- package/hooks/issue-reconcile-hooks.sh +217 -0
- package/hooks/pr-reconcile-hooks.sh +225 -0
- package/npm/bin/agent-control-plane.js +1984 -0
- package/npm/public-bin/agent-control-plane +3 -0
- package/package.json +61 -0
- package/tools/bin/agent-cleanup-worktree +247 -0
- package/tools/bin/agent-github-update-labels +66 -0
- package/tools/bin/agent-init-worktree +216 -0
- package/tools/bin/agent-project-archive-run +52 -0
- package/tools/bin/agent-project-capture-worker +46 -0
- package/tools/bin/agent-project-catch-up-merged-prs +137 -0
- package/tools/bin/agent-project-cleanup-session +244 -0
- package/tools/bin/agent-project-detached-launch +107 -0
- package/tools/bin/agent-project-heartbeat-loop +2347 -0
- package/tools/bin/agent-project-open-issue-worktree +89 -0
- package/tools/bin/agent-project-open-pr-worktree +80 -0
- package/tools/bin/agent-project-publish-issue-pr +349 -0
- package/tools/bin/agent-project-reconcile-issue-session +1128 -0
- package/tools/bin/agent-project-reconcile-pr-session +1005 -0
- package/tools/bin/agent-project-retry-state +147 -0
- package/tools/bin/agent-project-run-claude-session +657 -0
- package/tools/bin/agent-project-run-codex-resilient +718 -0
- package/tools/bin/agent-project-run-codex-session +316 -0
- package/tools/bin/agent-project-run-kilo-session +27 -0
- package/tools/bin/agent-project-run-openclaw-session +984 -0
- package/tools/bin/agent-project-run-opencode-session +27 -0
- package/tools/bin/agent-project-sync-anchor-repo +128 -0
- package/tools/bin/agent-project-worker-status +143 -0
- package/tools/bin/audit-agent-worktrees.sh +310 -0
- package/tools/bin/audit-issue-routing.sh +11 -0
- package/tools/bin/audit-retained-layout.sh +58 -0
- package/tools/bin/audit-retained-overlap.sh +135 -0
- package/tools/bin/audit-retained-worktrees.sh +228 -0
- package/tools/bin/branch-verification-guard.sh +351 -0
- package/tools/bin/capture-worker.sh +18 -0
- package/tools/bin/check-skill-contracts.sh +324 -0
- package/tools/bin/cleanup-worktree.sh +44 -0
- package/tools/bin/codex-quota +31 -0
- package/tools/bin/create-follow-up-issue.sh +114 -0
- package/tools/bin/dashboard-launchd-bootstrap.sh +38 -0
- package/tools/bin/flow-config-lib.sh +2127 -0
- package/tools/bin/flow-resident-worker-lib.sh +683 -0
- package/tools/bin/flow-runtime-doctor.sh +97 -0
- package/tools/bin/flow-shell-lib.sh +266 -0
- package/tools/bin/heartbeat-recovery-preflight.sh +106 -0
- package/tools/bin/heartbeat-safe-auto.sh +551 -0
- package/tools/bin/install-dashboard-launchd.sh +152 -0
- package/tools/bin/install-project-launchd.sh +219 -0
- package/tools/bin/issue-publish-scope-guard.sh +242 -0
- package/tools/bin/issue-requires-local-workspace-install.sh +31 -0
- package/tools/bin/issue-resource-class.sh +12 -0
- package/tools/bin/kick-scheduler.sh +75 -0
- package/tools/bin/label-follow-up-issues.sh +14 -0
- package/tools/bin/new-pr-worktree.sh +50 -0
- package/tools/bin/new-worktree.sh +49 -0
- package/tools/bin/pr-risk.sh +12 -0
- package/tools/bin/prepare-worktree.sh +140 -0
- package/tools/bin/profile-activate.sh +109 -0
- package/tools/bin/profile-adopt.sh +219 -0
- package/tools/bin/profile-smoke.sh +461 -0
- package/tools/bin/project-init.sh +189 -0
- package/tools/bin/project-launchd-bootstrap.sh +54 -0
- package/tools/bin/project-remove.sh +155 -0
- package/tools/bin/project-runtime-supervisor.sh +56 -0
- package/tools/bin/project-runtimectl.sh +586 -0
- package/tools/bin/provider-cooldown-state.sh +166 -0
- package/tools/bin/publish-issue-worker.sh +31 -0
- package/tools/bin/reconcile-issue-worker.sh +34 -0
- package/tools/bin/reconcile-pr-worker.sh +34 -0
- package/tools/bin/record-verification.sh +71 -0
- package/tools/bin/render-architecture-infographics.sh +110 -0
- package/tools/bin/render-dashboard-demo-media.sh +333 -0
- package/tools/bin/render-dashboard-snapshot.py +16 -0
- package/tools/bin/render-flow-config.sh +86 -0
- package/tools/bin/retry-state.sh +31 -0
- package/tools/bin/reuse-issue-worktree.sh +75 -0
- package/tools/bin/run-codex-bypass.sh +3 -0
- package/tools/bin/run-codex-safe.sh +3 -0
- package/tools/bin/run-codex-task.sh +231 -0
- package/tools/bin/scaffold-profile.sh +374 -0
- package/tools/bin/serve-dashboard.sh +5 -0
- package/tools/bin/split-retained-slice.sh +124 -0
- package/tools/bin/start-issue-worker.sh +796 -0
- package/tools/bin/start-pr-fix-worker.sh +458 -0
- package/tools/bin/start-pr-merge-repair-worker.sh +8 -0
- package/tools/bin/start-pr-review-worker.sh +227 -0
- package/tools/bin/start-resident-issue-loop.sh +908 -0
- package/tools/bin/sync-agent-repo.sh +52 -0
- package/tools/bin/sync-dependency-baseline.sh +247 -0
- package/tools/bin/sync-pr-labels.sh +12 -0
- package/tools/bin/sync-recurring-issue-checklist.sh +274 -0
- package/tools/bin/sync-shared-agent-home.sh +214 -0
- package/tools/bin/sync-vscode-workspace.sh +157 -0
- package/tools/bin/test-smoke.sh +63 -0
- package/tools/bin/uninstall-project-launchd.sh +55 -0
- package/tools/bin/update-github-labels.sh +14 -0
- package/tools/bin/worker-status.sh +19 -0
- package/tools/bin/workflow-catalog.sh +77 -0
- package/tools/dashboard/app.js +286 -0
- package/tools/dashboard/dashboard_snapshot.py +466 -0
- package/tools/dashboard/index.html +41 -0
- package/tools/dashboard/server.py +64 -0
- package/tools/dashboard/styles.css +351 -0
- package/tools/templates/issue-prompt-template.md +109 -0
- package/tools/templates/pr-fix-template.md +120 -0
- package/tools/templates/pr-merge-repair-template.md +91 -0
- package/tools/templates/pr-review-template.md +62 -0
- package/tools/templates/scheduled-issue-prompt-template.md +62 -0
- package/tools/tests/test-agent-control-plane-npm-cli.sh +279 -0
- package/tools/tests/test-agent-github-update-labels-falls-back-to-repository-id.sh +56 -0
- package/tools/tests/test-agent-project-claude-session-wrapper-clears-stale-sandbox-artifacts.sh +89 -0
- package/tools/tests/test-agent-project-claude-session-wrapper-does-not-retry-provider-quota.sh +82 -0
- package/tools/tests/test-agent-project-claude-session-wrapper-retries-transient-failures.sh +90 -0
- package/tools/tests/test-agent-project-claude-session-wrapper-times-out.sh +73 -0
- package/tools/tests/test-agent-project-claude-session-wrapper.sh +103 -0
- package/tools/tests/test-agent-project-cleanup-session-orphan-fallback.sh +90 -0
- package/tools/tests/test-agent-project-cleanup-session-skip-worktree-cleanup.sh +90 -0
- package/tools/tests/test-agent-project-codex-live-thread-persist.sh +76 -0
- package/tools/tests/test-agent-project-codex-recovery.sh +731 -0
- package/tools/tests/test-agent-project-codex-session-wrapper-clears-stale-sandbox-artifacts.sh +105 -0
- package/tools/tests/test-agent-project-codex-session-wrapper.sh +97 -0
- package/tools/tests/test-agent-project-open-pr-worktree-config-prefix.sh +81 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper-clears-stale-sandbox-artifacts.sh +109 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper-infers-blocked-result-contract.sh +89 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-literal-env-artifacts.sh +113 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-version-mismatch.sh +135 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper-resident.sh +179 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper-reuses-existing-agent-after-add-race.sh +119 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper-terminates-rate-limit-hang.sh +91 -0
- package/tools/tests/test-agent-project-openclaw-session-wrapper.sh +117 -0
- package/tools/tests/test-agent-project-publish-issue-pr-prunes-stale-worktree-entry.sh +148 -0
- package/tools/tests/test-agent-project-publish-issue-pr-reads-archived-session.sh +146 -0
- package/tools/tests/test-agent-project-publish-issue-pr-recovers-final-head.sh +145 -0
- package/tools/tests/test-agent-project-publish-issue-pr-reuses-existing-worktree.sh +147 -0
- package/tools/tests/test-agent-project-reconcile-failure-reason.sh +456 -0
- package/tools/tests/test-agent-project-reconcile-issue-archived-session-fallback.sh +96 -0
- package/tools/tests/test-agent-project-reconcile-issue-before-blocked.sh +90 -0
- package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery-uses-recovered-worktree.sh +212 -0
- package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery.sh +207 -0
- package/tools/tests/test-agent-project-reconcile-issue-provider-quota-schedules-provider-cooldown.sh +101 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-backfills-lane-metadata-from-worker-key.sh +113 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-clears-stale-failed-summary.sh +117 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-initializes-shared-agent-home.sh +55 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-normalizes-runner-state.sh +125 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-records-invalid-contract-summary.sh +118 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-skips-duplicate-blocked-comment.sh +144 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-standardizes-no-commits-blocker.sh +145 -0
- package/tools/tests/test-agent-project-reconcile-issue-session-synthesizes-blocked-comment.sh +139 -0
- package/tools/tests/test-agent-project-reconcile-pr-blocked-host-recovery.sh +242 -0
- package/tools/tests/test-agent-project-reconcile-pr-guard-blocked-no-commit.sh +142 -0
- package/tools/tests/test-agent-project-reconcile-pr-provider-quota-schedules-provider-cooldown.sh +106 -0
- package/tools/tests/test-agent-project-reconcile-pr-session-initializes-shared-agent-home.sh +66 -0
- package/tools/tests/test-agent-project-reconcile-pr-updated-branch-noop.sh +129 -0
- package/tools/tests/test-audit-agent-worktrees-active-launch-skips-git-inspection.sh +69 -0
- package/tools/tests/test-audit-agent-worktrees-broken-worktree.sh +43 -0
- package/tools/tests/test-audit-agent-worktrees-pending-launch-owner.sh +46 -0
- package/tools/tests/test-audit-agent-worktrees-unreconciled-owner.sh +79 -0
- package/tools/tests/test-audit-issue-routing-managed-branch-globs.sh +56 -0
- package/tools/tests/test-branch-verification-guard-generated-artifacts.sh +72 -0
- package/tools/tests/test-branch-verification-guard-targeted-coverage.sh +125 -0
- package/tools/tests/test-codex-quota-manager-failure-driven-rotation.sh +178 -0
- package/tools/tests/test-codex-quota-wrapper.sh +37 -0
- package/tools/tests/test-contribution-docs.sh +18 -0
- package/tools/tests/test-control-plane-dashboard-runtime-smoke.sh +343 -0
- package/tools/tests/test-create-follow-up-issue.sh +73 -0
- package/tools/tests/test-dashboard-launchd-bootstrap.sh +55 -0
- package/tools/tests/test-flow-export-execution-env-exports-repo-id.sh +30 -0
- package/tools/tests/test-flow-export-github-cli-auth-env-prefers-git-credential.sh +48 -0
- package/tools/tests/test-flow-github-api-repo-fallback-preserves-input.sh +85 -0
- package/tools/tests/test-flow-github-api-repo-prefers-explicit-repository-id.sh +60 -0
- package/tools/tests/test-flow-github-issue-list-falls-back-to-repository-id.sh +64 -0
- package/tools/tests/test-flow-github-pr-list-falls-back-to-repository-id.sh +77 -0
- package/tools/tests/test-flow-resident-can-reuse-does-not-leak-metadata.sh +52 -0
- package/tools/tests/test-flow-resident-reap-stale-controllers.sh +63 -0
- package/tools/tests/test-flow-resolve-codex-quota-tools.sh +104 -0
- package/tools/tests/test-flow-runtime-doctor-profile-selection.sh +27 -0
- package/tools/tests/test-heartbeat-codex-pr-linked-issue-exclusion.sh +79 -0
- package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-idle-controller.sh +115 -0
- package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-live-lane-controller.sh +117 -0
- package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-claude.sh +96 -0
- package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-codex.sh +96 -0
- package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop.sh +96 -0
- package/tools/tests/test-heartbeat-loop-auth-wait-does-not-consume-capacity.sh +170 -0
- package/tools/tests/test-heartbeat-loop-blocked-recovery-lane.sh +201 -0
- package/tools/tests/test-heartbeat-loop-blocked-recovery-vs-pr-reservation.sh +201 -0
- package/tools/tests/test-heartbeat-loop-idle-resident-controller-does-not-block-launches.sh +160 -0
- package/tools/tests/test-heartbeat-loop-pr-launch-dedup.sh +133 -0
- package/tools/tests/test-heartbeat-loop-provider-cooldown-suppresses-launches.sh +157 -0
- package/tools/tests/test-heartbeat-loop-reaps-stale-resident-controller.sh +181 -0
- package/tools/tests/test-heartbeat-loop-waiting-provider-resident-controller-does-not-block-launches.sh +160 -0
- package/tools/tests/test-heartbeat-ready-issues-blocked-recovery.sh +134 -0
- package/tools/tests/test-heartbeat-safe-auto-dynamic-concurrency.sh +162 -0
- package/tools/tests/test-heartbeat-safe-auto-no-tmux-sessions.sh +136 -0
- package/tools/tests/test-heartbeat-safe-auto-openclaw-skips-codex-quota.sh +139 -0
- package/tools/tests/test-heartbeat-safe-auto-quota-health-signal.sh +119 -0
- package/tools/tests/test-heartbeat-safe-auto-stale-shared-loop-pid-does-not-skip.sh +140 -0
- package/tools/tests/test-heartbeat-safe-auto-static-capacity-without-quota-cache.sh +142 -0
- package/tools/tests/test-heartbeat-safe-auto-zero-healthy-pools.sh +141 -0
- package/tools/tests/test-heartbeat-sync-issue-labels-empty-schedule.sh +65 -0
- package/tools/tests/test-heartbeat-sync-open-agent-prs-terminal-clears-running.sh +179 -0
- package/tools/tests/test-install-dashboard-launchd.sh +78 -0
- package/tools/tests/test-install-project-launchd-adds-tool-paths.sh +87 -0
- package/tools/tests/test-install-project-launchd.sh +110 -0
- package/tools/tests/test-issue-local-workspace-install-policy.sh +81 -0
- package/tools/tests/test-issue-publish-scope-guard-docs-signal.sh +70 -0
- package/tools/tests/test-issue-reconcile-hooks-success-clears-blocked.sh +36 -0
- package/tools/tests/test-kick-scheduler-requires-explicit-profile.sh +47 -0
- package/tools/tests/test-label-follow-up-issues-falls-back-to-repository-id.sh +132 -0
- package/tools/tests/test-manual-operator-entrypoints-require-explicit-profile.sh +64 -0
- package/tools/tests/test-package-funding-metadata.sh +21 -0
- package/tools/tests/test-package-public-metadata.sh +62 -0
- package/tools/tests/test-placeholder-worker-adapters.sh +38 -0
- package/tools/tests/test-pr-reconcile-hooks-refreshes-recurring-issue-checklist.sh +110 -0
- package/tools/tests/test-pr-risk-cohesive-mobile-locale-scope.sh +70 -0
- package/tools/tests/test-pr-risk-fix-label-semantics.sh +114 -0
- package/tools/tests/test-pr-risk-local-first-no-checks.sh +70 -0
- package/tools/tests/test-prepare-worktree-simple-repo-baseline.sh +67 -0
- package/tools/tests/test-profile-activate.sh +33 -0
- package/tools/tests/test-profile-adopt-allow-missing-repo.sh +68 -0
- package/tools/tests/test-profile-adopt-skip-workspace-sync-missing-file.sh +61 -0
- package/tools/tests/test-profile-adopt-syncs-anchor-and-workspace.sh +90 -0
- package/tools/tests/test-profile-smoke-collision.sh +44 -0
- package/tools/tests/test-profile-smoke-invalid-claude-config.sh +31 -0
- package/tools/tests/test-profile-smoke-invalid-provider-pool.sh +68 -0
- package/tools/tests/test-profile-smoke-repo-slug-mismatch.sh +36 -0
- package/tools/tests/test-profile-smoke.sh +45 -0
- package/tools/tests/test-project-init-force-and-skip-sync.sh +61 -0
- package/tools/tests/test-project-init-repo-slug-mismatch.sh +29 -0
- package/tools/tests/test-project-init.sh +66 -0
- package/tools/tests/test-project-launchd-bootstrap.sh +66 -0
- package/tools/tests/test-project-remove.sh +150 -0
- package/tools/tests/test-project-runtime-supervisor.sh +47 -0
- package/tools/tests/test-project-runtimectl-launchd.sh +115 -0
- package/tools/tests/test-project-runtimectl-missing-profile.sh +54 -0
- package/tools/tests/test-project-runtimectl-start-falls-back-to-bootstrap.sh +108 -0
- package/tools/tests/test-project-runtimectl-status-reports-supervisor-as-heartbeat-parent.sh +95 -0
- package/tools/tests/test-project-runtimectl-status-supervisor-running.sh +59 -0
- package/tools/tests/test-project-runtimectl-stop-cancels-pending-kick.sh +85 -0
- package/tools/tests/test-project-runtimectl-stop-clears-running-labels.sh +78 -0
- package/tools/tests/test-project-runtimectl.sh +212 -0
- package/tools/tests/test-provider-cooldown-state-prefers-runtime-worker-context.sh +39 -0
- package/tools/tests/test-provider-cooldown-state.sh +59 -0
- package/tools/tests/test-public-repo-docs.sh +159 -0
- package/tools/tests/test-reconcile-pr-worker-acp-config-routing.sh +75 -0
- package/tools/tests/test-render-dashboard-snapshot.sh +149 -0
- package/tools/tests/test-render-flow-config-demo-profile.sh +36 -0
- package/tools/tests/test-render-flow-config-provider-pool-fallback.sh +81 -0
- package/tools/tests/test-render-flow-config.sh +52 -0
- package/tools/tests/test-run-codex-task-claude-routing.sh +125 -0
- package/tools/tests/test-run-codex-task-codex-resident-routing.sh +108 -0
- package/tools/tests/test-run-codex-task-kilo-routing.sh +98 -0
- package/tools/tests/test-run-codex-task-openclaw-resident-routing.sh +117 -0
- package/tools/tests/test-run-codex-task-openclaw-routing.sh +113 -0
- package/tools/tests/test-run-codex-task-opencode-routing.sh +98 -0
- package/tools/tests/test-run-codex-task-provider-pool-fallback-routing.sh +146 -0
- package/tools/tests/test-scaffold-profile.sh +108 -0
- package/tools/tests/test-serve-dashboard.sh +93 -0
- package/tools/tests/test-start-issue-worker-blocked-context.sh +129 -0
- package/tools/tests/test-start-issue-worker-blocks-complete-recurring-checklist.sh +189 -0
- package/tools/tests/test-start-issue-worker-local-install-routing.sh +157 -0
- package/tools/tests/test-start-issue-worker-profile-template-routing.sh +149 -0
- package/tools/tests/test-start-issue-worker-recurring-resident-reuse-codex.sh +212 -0
- package/tools/tests/test-start-issue-worker-recurring-resident-reuse.sh +219 -0
- package/tools/tests/test-start-issue-worker-renders-verification-snippet.sh +155 -0
- package/tools/tests/test-start-issue-worker-resident-reuse-falls-back-to-new-worktree.sh +199 -0
- package/tools/tests/test-start-pr-fix-worker-host-blocker-context.sh +275 -0
- package/tools/tests/test-start-resident-issue-loop-adopts-next-recurring-issue.sh +185 -0
- package/tools/tests/test-start-resident-issue-loop-clears-pending-while-waiting-due.sh +152 -0
- package/tools/tests/test-start-resident-issue-loop-consumes-queued-lease.sh +186 -0
- package/tools/tests/test-start-resident-issue-loop-fails-over-provider-pool.sh +212 -0
- package/tools/tests/test-start-resident-issue-loop-immediate-cycles.sh +148 -0
- package/tools/tests/test-start-resident-issue-loop-waits-for-provider.sh +194 -0
- package/tools/tests/test-start-resident-issue-loop-waits-for-terminal-reconcile-status.sh +198 -0
- package/tools/tests/test-start-resident-issue-loop-yields-to-live-lane-controller.sh +145 -0
- package/tools/tests/test-sync-pr-labels-fix-lane-uses-repair-queued.sh +67 -0
- package/tools/tests/test-sync-recurring-issue-checklist-backfills-workflow-complete-blocker.sh +70 -0
- package/tools/tests/test-sync-recurring-issue-checklist.sh +95 -0
- package/tools/tests/test-sync-shared-agent-home-local-source-root.sh +66 -0
- package/tools/tests/test-sync-shared-agent-home-preserves-unrelated-workflow-catalog-skill.sh +47 -0
- package/tools/tests/test-test-smoke.sh +86 -0
- package/tools/tests/test-uninstall-project-launchd.sh +37 -0
- package/tools/tests/test-update-github-labels-prefers-sibling-helper.sh +49 -0
- package/tools/tests/test-workflow-catalog.sh +43 -0
- package/tools/vendor/codex-quota/LICENSE +21 -0
- package/tools/vendor/codex-quota/README.md +459 -0
- package/tools/vendor/codex-quota/codex-quota.js +261 -0
- package/tools/vendor/codex-quota/lib/claude-accounts.js +226 -0
- package/tools/vendor/codex-quota/lib/claude-oauth.js +174 -0
- package/tools/vendor/codex-quota/lib/claude-tokens.js +471 -0
- package/tools/vendor/codex-quota/lib/claude-usage.js +929 -0
- package/tools/vendor/codex-quota/lib/codex-accounts.js +205 -0
- package/tools/vendor/codex-quota/lib/codex-tokens.js +326 -0
- package/tools/vendor/codex-quota/lib/codex-usage.js +32 -0
- package/tools/vendor/codex-quota/lib/color.js +72 -0
- package/tools/vendor/codex-quota/lib/constants.js +57 -0
- package/tools/vendor/codex-quota/lib/container.js +143 -0
- package/tools/vendor/codex-quota/lib/display.js +1111 -0
- package/tools/vendor/codex-quota/lib/fs.js +63 -0
- package/tools/vendor/codex-quota/lib/handlers.js +2060 -0
- package/tools/vendor/codex-quota/lib/jwt.js +33 -0
- package/tools/vendor/codex-quota/lib/oauth.js +486 -0
- package/tools/vendor/codex-quota/lib/paths.js +34 -0
- package/tools/vendor/codex-quota/lib/prompts.js +44 -0
- package/tools/vendor/codex-quota/lib/sync.js +1438 -0
- package/tools/vendor/codex-quota/lib/token-match.js +96 -0
- package/tools/vendor/codex-quota-manager/scripts/auto-switch.sh +500 -0
- package/tools/vendor/codex-quota-manager/scripts/batch-add.sh +123 -0
|
@@ -0,0 +1,1984 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const readline = require("readline");
|
|
7
|
+
const util = require("util");
|
|
8
|
+
const { spawnSync } = require("child_process");
|
|
9
|
+
|
|
10
|
+
const packageRoot = path.resolve(__dirname, "..", "..");
|
|
11
|
+
const packageJson = require(path.join(packageRoot, "package.json"));
|
|
12
|
+
const skillName = "agent-control-plane";
|
|
13
|
+
let setupJsonOutputEnabled = false;
|
|
14
|
+
|
|
15
|
+
function printHelp() {
|
|
16
|
+
console.log(`agent-control-plane ${packageJson.version}
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
agent-control-plane <command> [args...]
|
|
20
|
+
|
|
21
|
+
Commands:
|
|
22
|
+
help Show this help
|
|
23
|
+
version Print package version
|
|
24
|
+
setup Guided setup flow for one repo profile
|
|
25
|
+
sync Publish the packaged runtime into ~/.agent-runtime
|
|
26
|
+
install Alias for sync
|
|
27
|
+
init Scaffold and adopt a project profile
|
|
28
|
+
doctor Inspect runtime/source installation state
|
|
29
|
+
profile-smoke Validate installed profiles
|
|
30
|
+
dashboard Start the dashboard server
|
|
31
|
+
launchd-install Install a per-project LaunchAgent (macOS)
|
|
32
|
+
launchd-uninstall Remove a per-project LaunchAgent (macOS)
|
|
33
|
+
runtime Forward to tools/bin/project-runtimectl.sh
|
|
34
|
+
remove Remove one installed profile and runtime state
|
|
35
|
+
smoke Run the packaged smoke suite
|
|
36
|
+
`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function printSetupHelp() {
|
|
40
|
+
console.log(`agent-control-plane setup
|
|
41
|
+
|
|
42
|
+
Usage:
|
|
43
|
+
agent-control-plane setup [options]
|
|
44
|
+
|
|
45
|
+
Guided install/bootstrap flow for one repo profile. It can detect the current
|
|
46
|
+
repo, suggest sane runtime paths, sync ACP into ~/.agent-runtime, scaffold one
|
|
47
|
+
profile, run doctor checks, and optionally start the runtime.
|
|
48
|
+
|
|
49
|
+
Options:
|
|
50
|
+
--profile-id <id> Profile id to create or refresh
|
|
51
|
+
--repo-slug <owner/repo> GitHub repo slug
|
|
52
|
+
--repo-root <path> Local checkout to manage (defaults to current git root)
|
|
53
|
+
--agent-root <path> ACP-managed runtime root for this profile
|
|
54
|
+
--agent-repo-root <path> Clean ACP-managed anchor repo root
|
|
55
|
+
--worktree-root <path> Parent root for ACP worktrees
|
|
56
|
+
--retained-repo-root <path> Manual checkout root to keep linked in the profile
|
|
57
|
+
--vscode-workspace-file <path>
|
|
58
|
+
Workspace file path ACP should generate/use
|
|
59
|
+
--coding-worker <backend> One of: codex, claude, openclaw
|
|
60
|
+
--force Overwrite an existing profile
|
|
61
|
+
--skip-anchor-sync Skip profile-adopt anchor repo sync
|
|
62
|
+
--skip-workspace-sync Skip profile-adopt workspace sync
|
|
63
|
+
--allow-missing-repo Allow setup even when the source repo is incomplete
|
|
64
|
+
--install-missing-deps Install missing core dependencies automatically when supported
|
|
65
|
+
--no-install-missing-deps Skip dependency installation prompts and auto install
|
|
66
|
+
--install-missing-backend Install the selected worker backend automatically when ACP knows how
|
|
67
|
+
--no-install-missing-backend Skip worker backend installation prompts and auto install
|
|
68
|
+
--gh-auth-login Run \`gh auth login\` automatically when GitHub auth is not ready
|
|
69
|
+
--no-gh-auth-login Skip the GitHub auth prompt during setup
|
|
70
|
+
--dry-run Render the setup plan without making changes
|
|
71
|
+
--plan Alias for --dry-run
|
|
72
|
+
--json Emit one JSON result object and send progress logs to stderr
|
|
73
|
+
--start-runtime Start the runtime after setup
|
|
74
|
+
--no-start-runtime Do not start the runtime after setup
|
|
75
|
+
--install-launchd Install macOS autostart after a successful runtime start
|
|
76
|
+
--no-install-launchd Do not install macOS autostart
|
|
77
|
+
--yes Accept detected defaults without prompting
|
|
78
|
+
--non-interactive Same as --yes
|
|
79
|
+
--help Show this help
|
|
80
|
+
`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function copyTree(sourceDir, targetDir) {
|
|
84
|
+
if (!fs.existsSync(sourceDir)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fs.cpSync(sourceDir, targetDir, {
|
|
89
|
+
recursive: true,
|
|
90
|
+
filter: (sourcePath) => {
|
|
91
|
+
const base = path.basename(sourcePath);
|
|
92
|
+
if (base === ".git" || base === "node_modules" || base === ".DS_Store") {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (base === "__pycache__") {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (base.endsWith(".bak") || base.endsWith(".bak2") || base.endsWith(".bak3")) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function stageSharedHome() {
|
|
107
|
+
const stageRoot = fs.mkdtempSync(path.join(os.tmpdir(), "agent-control-plane-"));
|
|
108
|
+
const sharedHome = path.join(stageRoot, "shared-home");
|
|
109
|
+
const stagedSkillRoot = path.join(sharedHome, "skills", "openclaw", skillName);
|
|
110
|
+
|
|
111
|
+
fs.mkdirSync(path.dirname(stagedSkillRoot), { recursive: true });
|
|
112
|
+
copyTree(packageRoot, stagedSkillRoot);
|
|
113
|
+
copyTree(path.join(packageRoot, "tools"), path.join(sharedHome, "tools"));
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
stageRoot: fs.realpathSync.native(stageRoot),
|
|
117
|
+
sharedHome: fs.realpathSync.native(sharedHome),
|
|
118
|
+
stagedSkillRoot: fs.realpathSync.native(stagedSkillRoot)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resolvePlatformHome(env = process.env) {
|
|
123
|
+
const homeDir = env.HOME || os.homedir();
|
|
124
|
+
return env.AGENT_PLATFORM_HOME || path.join(homeDir, ".agent-runtime");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createExecutionContext(stage) {
|
|
128
|
+
const homeDir = process.env.HOME || os.homedir();
|
|
129
|
+
const platformHome = resolvePlatformHome(process.env);
|
|
130
|
+
const runtimeHome = path.join(platformHome, "runtime-home");
|
|
131
|
+
const profileRegistryRoot = path.join(platformHome, "control-plane", "profiles");
|
|
132
|
+
const env = {
|
|
133
|
+
...process.env,
|
|
134
|
+
SHARED_AGENT_HOME: stage.sharedHome,
|
|
135
|
+
AGENT_CONTROL_PLANE_ROOT: stage.stagedSkillRoot,
|
|
136
|
+
ACP_ROOT: stage.stagedSkillRoot,
|
|
137
|
+
AGENT_FLOW_SOURCE_ROOT: stage.stagedSkillRoot,
|
|
138
|
+
ACP_PROJECT_INIT_SOURCE_HOME: stage.sharedHome,
|
|
139
|
+
ACP_PROJECT_INIT_RUNTIME_HOME: runtimeHome,
|
|
140
|
+
ACP_PROJECT_RUNTIME_SOURCE_HOME: stage.sharedHome,
|
|
141
|
+
ACP_DASHBOARD_SOURCE_HOME: stage.sharedHome,
|
|
142
|
+
ACP_PROFILE_REGISTRY_ROOT: profileRegistryRoot,
|
|
143
|
+
ACP_PROJECT_RUNTIME_PROFILE_REGISTRY_ROOT: profileRegistryRoot,
|
|
144
|
+
ACP_DASHBOARD_PROFILE_REGISTRY_ROOT: profileRegistryRoot,
|
|
145
|
+
HOME: homeDir
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
...stage,
|
|
150
|
+
env,
|
|
151
|
+
homeDir,
|
|
152
|
+
platformHome,
|
|
153
|
+
runtimeHome,
|
|
154
|
+
profileRegistryRoot
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function runScriptWithContext(context, scriptRelativePath, forwardedArgs, options = {}) {
|
|
159
|
+
const scriptPath = path.join(packageRoot, scriptRelativePath);
|
|
160
|
+
const stdio = options.stdio || "inherit";
|
|
161
|
+
const result = spawnSync("bash", [scriptPath, ...forwardedArgs], {
|
|
162
|
+
stdio,
|
|
163
|
+
encoding: stdio === "inherit" ? undefined : "utf8",
|
|
164
|
+
env: options.env || context.env,
|
|
165
|
+
cwd: options.cwd || process.cwd()
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (typeof result.status === "number") {
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
...result,
|
|
174
|
+
status: result.error ? 1 : 0
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function runCommand(scriptRelativePath, forwardedArgs) {
|
|
179
|
+
const stage = stageSharedHome();
|
|
180
|
+
const context = createExecutionContext(stage);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const result = runScriptWithContext(context, scriptRelativePath, forwardedArgs, { stdio: "inherit" });
|
|
184
|
+
return result.status;
|
|
185
|
+
} finally {
|
|
186
|
+
fs.rmSync(stage.stageRoot, { recursive: true, force: true });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function runCapture(command, args, options = {}) {
|
|
191
|
+
const result = spawnSync(command, args, {
|
|
192
|
+
encoding: "utf8",
|
|
193
|
+
env: options.env || process.env,
|
|
194
|
+
cwd: options.cwd || process.cwd()
|
|
195
|
+
});
|
|
196
|
+
return {
|
|
197
|
+
status: typeof result.status === "number" ? result.status : (result.error ? 1 : 0),
|
|
198
|
+
stdout: result.stdout || "",
|
|
199
|
+
stderr: result.stderr || ""
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function commandExists(command) {
|
|
204
|
+
return runCapture("bash", ["-lc", `command -v ${JSON.stringify(command)} >/dev/null 2>&1`]).status === 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function detectRepoRoot(startDir) {
|
|
208
|
+
const probe = runCapture("git", ["rev-parse", "--show-toplevel"], { cwd: startDir });
|
|
209
|
+
if (probe.status !== 0) {
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
return probe.stdout.trim();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function parseGithubRepoSlug(remoteUrl) {
|
|
216
|
+
const trimmed = String(remoteUrl || "").trim();
|
|
217
|
+
const patterns = [
|
|
218
|
+
/^git@github\.com:([^/]+\/[^/]+?)(?:\.git)?$/,
|
|
219
|
+
/^https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/,
|
|
220
|
+
/^ssh:\/\/git@github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
for (const pattern of patterns) {
|
|
224
|
+
const match = trimmed.match(pattern);
|
|
225
|
+
if (match) {
|
|
226
|
+
return match[1];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return "";
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function detectRepoSlug(repoRoot) {
|
|
234
|
+
if (!repoRoot) {
|
|
235
|
+
return "";
|
|
236
|
+
}
|
|
237
|
+
const remote = runCapture("git", ["remote", "get-url", "origin"], { cwd: repoRoot });
|
|
238
|
+
if (remote.status !== 0) {
|
|
239
|
+
return "";
|
|
240
|
+
}
|
|
241
|
+
return parseGithubRepoSlug(remote.stdout.trim());
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function sanitizeProfileId(value) {
|
|
245
|
+
const lowered = String(value || "")
|
|
246
|
+
.trim()
|
|
247
|
+
.toLowerCase()
|
|
248
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
249
|
+
.replace(/^-+|-+$/g, "");
|
|
250
|
+
if (!lowered) {
|
|
251
|
+
return "repo";
|
|
252
|
+
}
|
|
253
|
+
if (/^[a-z0-9]/.test(lowered)) {
|
|
254
|
+
return lowered;
|
|
255
|
+
}
|
|
256
|
+
return `repo-${lowered}`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function detectPreferredWorker() {
|
|
260
|
+
if (commandExists("codex")) {
|
|
261
|
+
return "codex";
|
|
262
|
+
}
|
|
263
|
+
if (commandExists("claude")) {
|
|
264
|
+
return "claude";
|
|
265
|
+
}
|
|
266
|
+
if (commandExists("openclaw")) {
|
|
267
|
+
return "openclaw";
|
|
268
|
+
}
|
|
269
|
+
return "openclaw";
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function parseKvOutput(text) {
|
|
273
|
+
const records = {};
|
|
274
|
+
for (const rawLine of String(text || "").split(/\r?\n/)) {
|
|
275
|
+
const line = rawLine.trim();
|
|
276
|
+
if (!line || !line.includes("=")) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const idx = line.indexOf("=");
|
|
280
|
+
const key = line.slice(0, idx);
|
|
281
|
+
const value = line.slice(idx + 1);
|
|
282
|
+
records[key] = value;
|
|
283
|
+
}
|
|
284
|
+
return records;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function printFailureDetails(result) {
|
|
288
|
+
const stdoutTarget = setupJsonOutputEnabled ? process.stderr : process.stdout;
|
|
289
|
+
const stderrTarget = process.stderr;
|
|
290
|
+
if (result.stdout) {
|
|
291
|
+
stdoutTarget.write(result.stdout);
|
|
292
|
+
if (!result.stdout.endsWith("\n")) {
|
|
293
|
+
stdoutTarget.write("\n");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (result.stderr) {
|
|
297
|
+
stderrTarget.write(result.stderr);
|
|
298
|
+
if (!result.stderr.endsWith("\n")) {
|
|
299
|
+
stderrTarget.write("\n");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function parseSetupArgs(args) {
|
|
305
|
+
const options = {
|
|
306
|
+
profileId: "",
|
|
307
|
+
repoSlug: "",
|
|
308
|
+
repoRoot: "",
|
|
309
|
+
agentRoot: "",
|
|
310
|
+
agentRepoRoot: "",
|
|
311
|
+
worktreeRoot: "",
|
|
312
|
+
retainedRepoRoot: "",
|
|
313
|
+
vscodeWorkspaceFile: "",
|
|
314
|
+
codingWorker: "",
|
|
315
|
+
sourceRepoRoot: "",
|
|
316
|
+
force: false,
|
|
317
|
+
skipAnchorSync: false,
|
|
318
|
+
skipWorkspaceSync: false,
|
|
319
|
+
allowMissingRepo: false,
|
|
320
|
+
installMissingDeps: null,
|
|
321
|
+
installMissingBackend: null,
|
|
322
|
+
ghAuthLogin: null,
|
|
323
|
+
dryRun: false,
|
|
324
|
+
json: false,
|
|
325
|
+
startRuntime: null,
|
|
326
|
+
installLaunchd: null,
|
|
327
|
+
interactive: process.stdin.isTTY && process.stdout.isTTY,
|
|
328
|
+
help: false
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
332
|
+
const current = args[index];
|
|
333
|
+
switch (current) {
|
|
334
|
+
case "--profile-id":
|
|
335
|
+
options.profileId = args[++index] || "";
|
|
336
|
+
break;
|
|
337
|
+
case "--repo-slug":
|
|
338
|
+
options.repoSlug = args[++index] || "";
|
|
339
|
+
break;
|
|
340
|
+
case "--repo-root":
|
|
341
|
+
options.repoRoot = args[++index] || "";
|
|
342
|
+
break;
|
|
343
|
+
case "--agent-root":
|
|
344
|
+
options.agentRoot = args[++index] || "";
|
|
345
|
+
break;
|
|
346
|
+
case "--agent-repo-root":
|
|
347
|
+
options.agentRepoRoot = args[++index] || "";
|
|
348
|
+
break;
|
|
349
|
+
case "--worktree-root":
|
|
350
|
+
options.worktreeRoot = args[++index] || "";
|
|
351
|
+
break;
|
|
352
|
+
case "--retained-repo-root":
|
|
353
|
+
options.retainedRepoRoot = args[++index] || "";
|
|
354
|
+
break;
|
|
355
|
+
case "--vscode-workspace-file":
|
|
356
|
+
options.vscodeWorkspaceFile = args[++index] || "";
|
|
357
|
+
break;
|
|
358
|
+
case "--coding-worker":
|
|
359
|
+
options.codingWorker = args[++index] || "";
|
|
360
|
+
break;
|
|
361
|
+
case "--source-repo-root":
|
|
362
|
+
options.sourceRepoRoot = args[++index] || "";
|
|
363
|
+
break;
|
|
364
|
+
case "--force":
|
|
365
|
+
options.force = true;
|
|
366
|
+
break;
|
|
367
|
+
case "--skip-anchor-sync":
|
|
368
|
+
options.skipAnchorSync = true;
|
|
369
|
+
break;
|
|
370
|
+
case "--skip-workspace-sync":
|
|
371
|
+
options.skipWorkspaceSync = true;
|
|
372
|
+
break;
|
|
373
|
+
case "--allow-missing-repo":
|
|
374
|
+
options.allowMissingRepo = true;
|
|
375
|
+
break;
|
|
376
|
+
case "--install-missing-deps":
|
|
377
|
+
options.installMissingDeps = true;
|
|
378
|
+
break;
|
|
379
|
+
case "--no-install-missing-deps":
|
|
380
|
+
options.installMissingDeps = false;
|
|
381
|
+
break;
|
|
382
|
+
case "--install-missing-backend":
|
|
383
|
+
options.installMissingBackend = true;
|
|
384
|
+
break;
|
|
385
|
+
case "--no-install-missing-backend":
|
|
386
|
+
options.installMissingBackend = false;
|
|
387
|
+
break;
|
|
388
|
+
case "--gh-auth-login":
|
|
389
|
+
options.ghAuthLogin = true;
|
|
390
|
+
break;
|
|
391
|
+
case "--no-gh-auth-login":
|
|
392
|
+
options.ghAuthLogin = false;
|
|
393
|
+
break;
|
|
394
|
+
case "--dry-run":
|
|
395
|
+
case "--plan":
|
|
396
|
+
options.dryRun = true;
|
|
397
|
+
break;
|
|
398
|
+
case "--json":
|
|
399
|
+
options.json = true;
|
|
400
|
+
options.interactive = false;
|
|
401
|
+
break;
|
|
402
|
+
case "--start-runtime":
|
|
403
|
+
options.startRuntime = true;
|
|
404
|
+
break;
|
|
405
|
+
case "--no-start-runtime":
|
|
406
|
+
options.startRuntime = false;
|
|
407
|
+
break;
|
|
408
|
+
case "--install-launchd":
|
|
409
|
+
options.installLaunchd = true;
|
|
410
|
+
break;
|
|
411
|
+
case "--no-install-launchd":
|
|
412
|
+
options.installLaunchd = false;
|
|
413
|
+
break;
|
|
414
|
+
case "--yes":
|
|
415
|
+
case "--non-interactive":
|
|
416
|
+
options.interactive = false;
|
|
417
|
+
break;
|
|
418
|
+
case "--help":
|
|
419
|
+
case "-h":
|
|
420
|
+
options.help = true;
|
|
421
|
+
break;
|
|
422
|
+
default:
|
|
423
|
+
throw new Error(`Unknown argument for setup: ${current}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return options;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function createPromptInterface() {
|
|
431
|
+
return readline.createInterface({
|
|
432
|
+
input: process.stdin,
|
|
433
|
+
output: process.stdout
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function question(rl, prompt) {
|
|
438
|
+
return new Promise((resolve) => rl.question(prompt, resolve));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async function promptText(rl, label, defaultValue) {
|
|
442
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
443
|
+
const answer = (await question(rl, `${label}${suffix}: `)).trim();
|
|
444
|
+
return answer || defaultValue;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async function promptYesNo(rl, label, defaultValue) {
|
|
448
|
+
const suffix = defaultValue ? " [Y/n]" : " [y/N]";
|
|
449
|
+
const answer = (await question(rl, `${label}${suffix}: `)).trim().toLowerCase();
|
|
450
|
+
if (!answer) {
|
|
451
|
+
return defaultValue;
|
|
452
|
+
}
|
|
453
|
+
if (answer === "y" || answer === "yes") {
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
if (answer === "n" || answer === "no") {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
return defaultValue;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function buildSetupPaths(platformHome, repoRoot, profileId, overrides) {
|
|
463
|
+
const agentRoot = path.resolve(overrides.agentRoot || path.join(platformHome, "projects", profileId));
|
|
464
|
+
const repoRootResolved = path.resolve(repoRoot);
|
|
465
|
+
return {
|
|
466
|
+
repoRoot: repoRootResolved,
|
|
467
|
+
agentRoot,
|
|
468
|
+
agentRepoRoot: path.resolve(overrides.agentRepoRoot || path.join(agentRoot, "repo")),
|
|
469
|
+
worktreeRoot: path.resolve(overrides.worktreeRoot || path.join(agentRoot, "worktrees")),
|
|
470
|
+
retainedRepoRoot: path.resolve(overrides.retainedRepoRoot || repoRootResolved),
|
|
471
|
+
vscodeWorkspaceFile: path.resolve(overrides.vscodeWorkspaceFile || path.join(agentRoot, `${profileId}-agents.code-workspace`)),
|
|
472
|
+
sourceRepoRoot: path.resolve(overrides.sourceRepoRoot || repoRootResolved)
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function collectPrereqStatus(codingWorker) {
|
|
477
|
+
const requiredTools = ["bash", "git", "gh", "jq", "python3", "tmux"];
|
|
478
|
+
const missingRequired = requiredTools.filter((tool) => !commandExists(tool));
|
|
479
|
+
const workerCommand = codingWorker;
|
|
480
|
+
const workerAvailable = commandExists(workerCommand);
|
|
481
|
+
const ghAuthResult = commandExists("gh") ? runCapture("gh", ["auth", "status"]) : { status: 1, stdout: "", stderr: "" };
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
missingRequired,
|
|
485
|
+
coreToolsOk: missingRequired.length === 0,
|
|
486
|
+
workerCommand,
|
|
487
|
+
workerAvailable,
|
|
488
|
+
ghAuthOk: ghAuthResult.status === 0,
|
|
489
|
+
ghAuthOutput: `${ghAuthResult.stdout}${ghAuthResult.stderr}`.trim()
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function detectPackageManager() {
|
|
494
|
+
if (commandExists("brew")) {
|
|
495
|
+
return { name: "brew" };
|
|
496
|
+
}
|
|
497
|
+
if (commandExists("apt-get")) {
|
|
498
|
+
return { name: "apt-get" };
|
|
499
|
+
}
|
|
500
|
+
if (commandExists("dnf")) {
|
|
501
|
+
return { name: "dnf" };
|
|
502
|
+
}
|
|
503
|
+
if (commandExists("yum")) {
|
|
504
|
+
return { name: "yum" };
|
|
505
|
+
}
|
|
506
|
+
if (commandExists("pacman")) {
|
|
507
|
+
return { name: "pacman" };
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function dependencyPackageMap(managerName) {
|
|
513
|
+
switch (managerName) {
|
|
514
|
+
case "brew":
|
|
515
|
+
return {
|
|
516
|
+
bash: "bash",
|
|
517
|
+
git: "git",
|
|
518
|
+
gh: "gh",
|
|
519
|
+
jq: "jq",
|
|
520
|
+
python3: "python",
|
|
521
|
+
tmux: "tmux"
|
|
522
|
+
};
|
|
523
|
+
case "apt-get":
|
|
524
|
+
return {
|
|
525
|
+
bash: "bash",
|
|
526
|
+
git: "git",
|
|
527
|
+
gh: "gh",
|
|
528
|
+
jq: "jq",
|
|
529
|
+
python3: "python3",
|
|
530
|
+
tmux: "tmux"
|
|
531
|
+
};
|
|
532
|
+
case "dnf":
|
|
533
|
+
case "yum":
|
|
534
|
+
return {
|
|
535
|
+
bash: "bash",
|
|
536
|
+
git: "git",
|
|
537
|
+
gh: "gh",
|
|
538
|
+
jq: "jq",
|
|
539
|
+
python3: "python3",
|
|
540
|
+
tmux: "tmux"
|
|
541
|
+
};
|
|
542
|
+
case "pacman":
|
|
543
|
+
return {
|
|
544
|
+
bash: "bash",
|
|
545
|
+
git: "git",
|
|
546
|
+
gh: "github-cli",
|
|
547
|
+
jq: "jq",
|
|
548
|
+
python3: "python",
|
|
549
|
+
tmux: "tmux"
|
|
550
|
+
};
|
|
551
|
+
default:
|
|
552
|
+
return {};
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function sudoPrefix() {
|
|
557
|
+
if (typeof process.getuid === "function" && process.getuid() === 0) {
|
|
558
|
+
return [];
|
|
559
|
+
}
|
|
560
|
+
return commandExists("sudo") ? ["sudo"] : [];
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function buildDependencyInstallPlan(missingTools) {
|
|
564
|
+
const manager = detectPackageManager();
|
|
565
|
+
if (!manager) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const packageMap = dependencyPackageMap(manager.name);
|
|
570
|
+
const packages = [...new Set(
|
|
571
|
+
missingTools
|
|
572
|
+
.map((tool) => packageMap[tool])
|
|
573
|
+
.filter(Boolean)
|
|
574
|
+
)];
|
|
575
|
+
|
|
576
|
+
if (packages.length === 0) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const prefix = sudoPrefix();
|
|
581
|
+
const commands = [];
|
|
582
|
+
switch (manager.name) {
|
|
583
|
+
case "brew":
|
|
584
|
+
commands.push(["brew", "install", ...packages]);
|
|
585
|
+
break;
|
|
586
|
+
case "apt-get":
|
|
587
|
+
commands.push([...prefix, "apt-get", "update"]);
|
|
588
|
+
commands.push([...prefix, "apt-get", "install", "-y", ...packages]);
|
|
589
|
+
break;
|
|
590
|
+
case "dnf":
|
|
591
|
+
commands.push([...prefix, "dnf", "install", "-y", ...packages]);
|
|
592
|
+
break;
|
|
593
|
+
case "yum":
|
|
594
|
+
commands.push([...prefix, "yum", "install", "-y", ...packages]);
|
|
595
|
+
break;
|
|
596
|
+
case "pacman":
|
|
597
|
+
commands.push([...prefix, "pacman", "-Sy", "--noconfirm", ...packages]);
|
|
598
|
+
break;
|
|
599
|
+
default:
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
manager: manager.name,
|
|
605
|
+
packages,
|
|
606
|
+
commands
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function shellQuote(value) {
|
|
611
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function formatCommand(args) {
|
|
615
|
+
return args.map(shellQuote).join(" ");
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function runInteractiveCommand(command, args) {
|
|
619
|
+
const result = spawnSync(command, args, {
|
|
620
|
+
stdio: "inherit",
|
|
621
|
+
env: process.env
|
|
622
|
+
});
|
|
623
|
+
return typeof result.status === "number" ? result.status : (result.error ? 1 : 0);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
async function maybeInstallMissingDependencies(options, prereq) {
|
|
627
|
+
if (prereq.coreToolsOk) {
|
|
628
|
+
return {
|
|
629
|
+
status: "not-needed",
|
|
630
|
+
reason: "",
|
|
631
|
+
installer: "",
|
|
632
|
+
commands: []
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const plan = buildDependencyInstallPlan(prereq.missingRequired);
|
|
637
|
+
if (!plan) {
|
|
638
|
+
console.log("\nACP found missing core dependencies but cannot install them automatically on this machine.");
|
|
639
|
+
console.log(`- missing tools: ${prereq.missingRequired.join(", ")}`);
|
|
640
|
+
console.log("- supported auto-install package managers today: brew, apt-get, dnf, yum, pacman");
|
|
641
|
+
return {
|
|
642
|
+
status: "unavailable",
|
|
643
|
+
reason: "no-supported-package-manager",
|
|
644
|
+
installer: "",
|
|
645
|
+
commands: []
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (options.installMissingDeps === false) {
|
|
650
|
+
console.log("\nSkipping dependency installation because the setup flags disabled it.");
|
|
651
|
+
return {
|
|
652
|
+
status: "skipped",
|
|
653
|
+
reason: "disabled",
|
|
654
|
+
installer: plan.manager,
|
|
655
|
+
commands: plan.commands
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
let shouldInstall = options.installMissingDeps === true;
|
|
660
|
+
if (options.installMissingDeps === null && options.interactive) {
|
|
661
|
+
console.log("\nACP can install the missing core dependencies for you.");
|
|
662
|
+
console.log(`- package manager: ${plan.manager}`);
|
|
663
|
+
console.log(`- packages: ${plan.packages.join(", ")}`);
|
|
664
|
+
console.log(`- command preview: ${plan.commands.map(formatCommand).join(" && ")}`);
|
|
665
|
+
const rl = createPromptInterface();
|
|
666
|
+
try {
|
|
667
|
+
shouldInstall = await promptYesNo(rl, "Install missing core dependencies now", true);
|
|
668
|
+
} finally {
|
|
669
|
+
rl.close();
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (!shouldInstall) {
|
|
674
|
+
console.log("\nDependency installation skipped.");
|
|
675
|
+
return {
|
|
676
|
+
status: "skipped",
|
|
677
|
+
reason: "not-confirmed",
|
|
678
|
+
installer: plan.manager,
|
|
679
|
+
commands: plan.commands
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
console.log("\nInstalling missing core dependencies...");
|
|
684
|
+
for (const commandArgs of plan.commands) {
|
|
685
|
+
const [command, ...rest] = commandArgs;
|
|
686
|
+
console.log(`> ${formatCommand(commandArgs)}`);
|
|
687
|
+
const status = runInteractiveCommand(command, rest);
|
|
688
|
+
if (status !== 0) {
|
|
689
|
+
return {
|
|
690
|
+
status: "failed",
|
|
691
|
+
reason: `command-failed:${command}`,
|
|
692
|
+
installer: plan.manager,
|
|
693
|
+
commands: plan.commands
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return {
|
|
699
|
+
status: "ok",
|
|
700
|
+
reason: "",
|
|
701
|
+
installer: plan.manager,
|
|
702
|
+
commands: plan.commands
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
async function maybeRunGithubAuthLogin(options, prereq) {
|
|
707
|
+
if (prereq.ghAuthOk) {
|
|
708
|
+
return { status: "not-needed", reason: "" };
|
|
709
|
+
}
|
|
710
|
+
if (!commandExists("gh")) {
|
|
711
|
+
console.log("\nGitHub authentication cannot run yet because `gh` is not installed.");
|
|
712
|
+
return { status: "skipped", reason: "gh-missing" };
|
|
713
|
+
}
|
|
714
|
+
if (options.ghAuthLogin === false) {
|
|
715
|
+
console.log("\nSkipping `gh auth login` because the setup flags disabled it.");
|
|
716
|
+
return { status: "skipped", reason: "disabled" };
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
let shouldRun = options.ghAuthLogin === true;
|
|
720
|
+
if (options.ghAuthLogin === null && options.interactive) {
|
|
721
|
+
console.log("\nGitHub CLI is not authenticated for this OS user.");
|
|
722
|
+
const rl = createPromptInterface();
|
|
723
|
+
try {
|
|
724
|
+
shouldRun = await promptYesNo(rl, "Run `gh auth login` now", true);
|
|
725
|
+
} finally {
|
|
726
|
+
rl.close();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (!shouldRun) {
|
|
731
|
+
console.log("\nGitHub authentication skipped.");
|
|
732
|
+
return { status: "skipped", reason: "not-confirmed" };
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
console.log("\nLaunching GitHub authentication...");
|
|
736
|
+
const status = runInteractiveCommand("gh", ["auth", "login"]);
|
|
737
|
+
if (status !== 0) {
|
|
738
|
+
return { status: "failed", reason: "gh-auth-login-failed" };
|
|
739
|
+
}
|
|
740
|
+
return { status: "ok", reason: "" };
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function backendReadinessHint(workerCommand) {
|
|
744
|
+
switch (workerCommand) {
|
|
745
|
+
case "codex":
|
|
746
|
+
return "Make sure Codex is installed and authenticated for this OS user before you start background runs.";
|
|
747
|
+
case "claude":
|
|
748
|
+
return "Make sure Claude Code is installed and authenticated for this OS user before you start background runs.";
|
|
749
|
+
case "openclaw":
|
|
750
|
+
return "Make sure OpenClaw is installed and provider credentials are ready before you start background runs.";
|
|
751
|
+
default:
|
|
752
|
+
return "Make sure the selected worker backend is installed and authenticated before you start background runs.";
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function backendSetupGuide(workerCommand) {
|
|
757
|
+
switch (workerCommand) {
|
|
758
|
+
case "codex":
|
|
759
|
+
return {
|
|
760
|
+
title: "Codex",
|
|
761
|
+
docsUrl: "https://platform.openai.com/docs/codex",
|
|
762
|
+
installExamples: [
|
|
763
|
+
"npm install -g @openai/codex"
|
|
764
|
+
],
|
|
765
|
+
authExamples: [
|
|
766
|
+
"codex login"
|
|
767
|
+
],
|
|
768
|
+
verifyExamples: [
|
|
769
|
+
"codex --version",
|
|
770
|
+
"codex login status"
|
|
771
|
+
],
|
|
772
|
+
note: "ACP can install Codex through npm when npm is available. Authentication and account selection still stay with your local Codex setup."
|
|
773
|
+
};
|
|
774
|
+
case "claude":
|
|
775
|
+
return {
|
|
776
|
+
title: "Claude Code",
|
|
777
|
+
docsUrl: "https://docs.anthropic.com/en/docs/claude-code/setup",
|
|
778
|
+
installExamples: [
|
|
779
|
+
"npm install -g @anthropic-ai/claude-code"
|
|
780
|
+
],
|
|
781
|
+
authExamples: [
|
|
782
|
+
"claude auth login"
|
|
783
|
+
],
|
|
784
|
+
verifyExamples: [
|
|
785
|
+
"claude --version",
|
|
786
|
+
"claude doctor"
|
|
787
|
+
],
|
|
788
|
+
note: "ACP uses the npm install path when available. Anthropic also documents a native installer if you prefer not to use npm."
|
|
789
|
+
};
|
|
790
|
+
case "openclaw":
|
|
791
|
+
return {
|
|
792
|
+
title: "OpenClaw",
|
|
793
|
+
docsUrl: "https://docs.openclaw.ai/start/getting-started",
|
|
794
|
+
installExamples: [
|
|
795
|
+
"npm install -g openclaw@latest"
|
|
796
|
+
],
|
|
797
|
+
authExamples: [
|
|
798
|
+
"openclaw setup --wizard",
|
|
799
|
+
"openclaw onboard --install-daemon"
|
|
800
|
+
],
|
|
801
|
+
verifyExamples: [
|
|
802
|
+
"openclaw doctor",
|
|
803
|
+
"openclaw status"
|
|
804
|
+
],
|
|
805
|
+
note: "ACP uses the npm install path when npm is available. OpenClaw also documents a dedicated install script and onboarding flow."
|
|
806
|
+
};
|
|
807
|
+
default:
|
|
808
|
+
return {
|
|
809
|
+
title: workerCommand,
|
|
810
|
+
docsUrl: "",
|
|
811
|
+
installExamples: [],
|
|
812
|
+
authExamples: [],
|
|
813
|
+
verifyExamples: [],
|
|
814
|
+
note: "Install and authenticate the selected worker backend before you ask ACP to start background runs."
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function buildWorkerBackendInstallPlan(workerCommand) {
|
|
820
|
+
if (!commandExists("npm")) {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
switch (workerCommand) {
|
|
825
|
+
case "codex":
|
|
826
|
+
return {
|
|
827
|
+
installer: "npm",
|
|
828
|
+
commands: [["npm", "install", "-g", "@openai/codex"]]
|
|
829
|
+
};
|
|
830
|
+
case "claude":
|
|
831
|
+
return {
|
|
832
|
+
installer: "npm",
|
|
833
|
+
commands: [["npm", "install", "-g", "@anthropic-ai/claude-code"]]
|
|
834
|
+
};
|
|
835
|
+
case "openclaw":
|
|
836
|
+
return {
|
|
837
|
+
installer: "npm",
|
|
838
|
+
commands: [["npm", "install", "-g", "openclaw@latest"]]
|
|
839
|
+
};
|
|
840
|
+
default:
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function detectUrlOpener() {
|
|
846
|
+
if (commandExists("open")) {
|
|
847
|
+
return "open";
|
|
848
|
+
}
|
|
849
|
+
if (commandExists("xdg-open")) {
|
|
850
|
+
return "xdg-open";
|
|
851
|
+
}
|
|
852
|
+
return "";
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function printWorkerSetupGuide(guide) {
|
|
856
|
+
console.log(`\n${guide.title} backend setup`);
|
|
857
|
+
if (guide.installExamples.length > 0) {
|
|
858
|
+
console.log("- install");
|
|
859
|
+
for (const example of guide.installExamples) {
|
|
860
|
+
console.log(` ${example}`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (guide.authExamples.length > 0) {
|
|
864
|
+
console.log("- authenticate");
|
|
865
|
+
for (const example of guide.authExamples) {
|
|
866
|
+
console.log(` ${example}`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
if (guide.verifyExamples.length > 0) {
|
|
870
|
+
console.log("- verify");
|
|
871
|
+
for (const example of guide.verifyExamples) {
|
|
872
|
+
console.log(` ${example}`);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
if (guide.docsUrl) {
|
|
876
|
+
console.log(`- docs: ${guide.docsUrl}`);
|
|
877
|
+
}
|
|
878
|
+
if (guide.note) {
|
|
879
|
+
console.log(`- note: ${guide.note}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
async function maybeShowWorkerSetupGuide(options, prereq) {
|
|
884
|
+
const guide = backendSetupGuide(prereq.workerCommand);
|
|
885
|
+
if (prereq.workerAvailable) {
|
|
886
|
+
return {
|
|
887
|
+
status: "not-needed",
|
|
888
|
+
reason: "",
|
|
889
|
+
installStatus: "not-needed",
|
|
890
|
+
installReason: "",
|
|
891
|
+
installer: "",
|
|
892
|
+
commands: [],
|
|
893
|
+
docsOpened: "not-needed",
|
|
894
|
+
guide
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
printWorkerSetupGuide(guide);
|
|
899
|
+
|
|
900
|
+
const installPlan = buildWorkerBackendInstallPlan(prereq.workerCommand);
|
|
901
|
+
if (!installPlan) {
|
|
902
|
+
console.log("\nACP does not know a safe automated install command for this worker on the current machine.");
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (options.installMissingBackend === false) {
|
|
906
|
+
return {
|
|
907
|
+
status: "shown",
|
|
908
|
+
reason: "backend-missing",
|
|
909
|
+
installStatus: "skipped",
|
|
910
|
+
installReason: "disabled",
|
|
911
|
+
installer: installPlan ? installPlan.installer : "",
|
|
912
|
+
commands: installPlan ? installPlan.commands : [],
|
|
913
|
+
docsOpened: installPlan ? "skipped" : "unsupported",
|
|
914
|
+
guide
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
let shouldInstall = options.installMissingBackend === true;
|
|
919
|
+
if (!shouldInstall && installPlan && options.installMissingBackend === null && options.interactive) {
|
|
920
|
+
console.log("\nACP can try to install the selected worker backend for you.");
|
|
921
|
+
console.log(`- installer: ${installPlan.installer}`);
|
|
922
|
+
console.log(`- command preview: ${installPlan.commands.map(formatCommand).join(" && ")}`);
|
|
923
|
+
const rl = createPromptInterface();
|
|
924
|
+
try {
|
|
925
|
+
shouldInstall = await promptYesNo(rl, `Install ${guide.title} now`, true);
|
|
926
|
+
} finally {
|
|
927
|
+
rl.close();
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
let installStatus = "unavailable";
|
|
932
|
+
let installReason = installPlan ? "" : "no-supported-installer";
|
|
933
|
+
if (installPlan && shouldInstall) {
|
|
934
|
+
console.log(`\nInstalling ${guide.title}...`);
|
|
935
|
+
installStatus = "ok";
|
|
936
|
+
for (const commandArgs of installPlan.commands) {
|
|
937
|
+
const [command, ...rest] = commandArgs;
|
|
938
|
+
console.log(`> ${formatCommand(commandArgs)}`);
|
|
939
|
+
const status = runInteractiveCommand(command, rest);
|
|
940
|
+
if (status !== 0) {
|
|
941
|
+
installStatus = "failed";
|
|
942
|
+
installReason = `command-failed:${command}`;
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
} else if (installPlan) {
|
|
947
|
+
installStatus = "skipped";
|
|
948
|
+
installReason = options.installMissingBackend === null ? "not-confirmed" : "disabled";
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const refreshedPrereq = collectPrereqStatus(prereq.workerCommand);
|
|
952
|
+
if (refreshedPrereq.workerAvailable) {
|
|
953
|
+
return {
|
|
954
|
+
status: "shown",
|
|
955
|
+
reason: "backend-missing",
|
|
956
|
+
installStatus,
|
|
957
|
+
installReason,
|
|
958
|
+
installer: installPlan ? installPlan.installer : "",
|
|
959
|
+
commands: installPlan ? installPlan.commands : [],
|
|
960
|
+
docsOpened: "not-needed",
|
|
961
|
+
guide
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (!guide.docsUrl) {
|
|
966
|
+
return {
|
|
967
|
+
status: "shown",
|
|
968
|
+
reason: "backend-missing",
|
|
969
|
+
installStatus,
|
|
970
|
+
installReason,
|
|
971
|
+
installer: installPlan ? installPlan.installer : "",
|
|
972
|
+
commands: installPlan ? installPlan.commands : [],
|
|
973
|
+
docsOpened: "unavailable",
|
|
974
|
+
guide
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
const opener = detectUrlOpener();
|
|
979
|
+
if (!opener) {
|
|
980
|
+
return {
|
|
981
|
+
status: "shown",
|
|
982
|
+
reason: "backend-missing",
|
|
983
|
+
installStatus,
|
|
984
|
+
installReason,
|
|
985
|
+
installer: installPlan ? installPlan.installer : "",
|
|
986
|
+
commands: installPlan ? installPlan.commands : [],
|
|
987
|
+
docsOpened: "unsupported",
|
|
988
|
+
guide
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (!options.interactive) {
|
|
993
|
+
return {
|
|
994
|
+
status: "shown",
|
|
995
|
+
reason: "backend-missing",
|
|
996
|
+
installStatus,
|
|
997
|
+
installReason,
|
|
998
|
+
installer: installPlan ? installPlan.installer : "",
|
|
999
|
+
commands: installPlan ? installPlan.commands : [],
|
|
1000
|
+
docsOpened: "skipped",
|
|
1001
|
+
guide
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
const rl = createPromptInterface();
|
|
1006
|
+
let shouldOpen = false;
|
|
1007
|
+
try {
|
|
1008
|
+
shouldOpen = await promptYesNo(rl, `Open ${guide.title} setup docs in your browser now`, false);
|
|
1009
|
+
} finally {
|
|
1010
|
+
rl.close();
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (!shouldOpen) {
|
|
1014
|
+
return {
|
|
1015
|
+
status: "shown",
|
|
1016
|
+
reason: "backend-missing",
|
|
1017
|
+
installStatus,
|
|
1018
|
+
installReason,
|
|
1019
|
+
installer: installPlan ? installPlan.installer : "",
|
|
1020
|
+
commands: installPlan ? installPlan.commands : [],
|
|
1021
|
+
docsOpened: "skipped",
|
|
1022
|
+
guide
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
const status = runInteractiveCommand(opener, [guide.docsUrl]);
|
|
1027
|
+
return {
|
|
1028
|
+
status: "shown",
|
|
1029
|
+
reason: "backend-missing",
|
|
1030
|
+
installStatus,
|
|
1031
|
+
installReason,
|
|
1032
|
+
installer: installPlan ? installPlan.installer : "",
|
|
1033
|
+
commands: installPlan ? installPlan.commands : [],
|
|
1034
|
+
docsOpened: status === 0 ? "yes" : "failed",
|
|
1035
|
+
guide
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
function printPrereqSummary(prereq) {
|
|
1040
|
+
console.log("\nPrerequisite check");
|
|
1041
|
+
console.log(`- core tools: ${prereq.coreToolsOk ? "ok" : `missing ${prereq.missingRequired.join(", ")}`}`);
|
|
1042
|
+
console.log(`- worker backend (${prereq.workerCommand}): ${prereq.workerAvailable ? "found" : "missing on PATH"}`);
|
|
1043
|
+
console.log(`- GitHub auth: ${prereq.ghAuthOk ? "ok" : "not ready"}`);
|
|
1044
|
+
console.log(`- backend note: ${backendReadinessHint(prereq.workerCommand)}`);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function profileExists(profileRegistryRoot, profileId) {
|
|
1048
|
+
return fs.existsSync(path.join(profileRegistryRoot, profileId, "control-plane.yaml"));
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function buildScopedContext(context, profileId) {
|
|
1052
|
+
return {
|
|
1053
|
+
...context,
|
|
1054
|
+
env: {
|
|
1055
|
+
...context.env,
|
|
1056
|
+
ACP_PROJECT_ID: profileId,
|
|
1057
|
+
AGENT_PROJECT_ID: profileId
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function renderSetupSummary(config) {
|
|
1063
|
+
console.log("\nSetup plan");
|
|
1064
|
+
console.log(`- profile id: ${config.profileId}`);
|
|
1065
|
+
console.log(`- repo slug: ${config.repoSlug}`);
|
|
1066
|
+
console.log(`- repo root: ${config.paths.repoRoot}`);
|
|
1067
|
+
console.log(`- agent root: ${config.paths.agentRoot}`);
|
|
1068
|
+
console.log(`- agent repo root: ${config.paths.agentRepoRoot}`);
|
|
1069
|
+
console.log(`- worktree root: ${config.paths.worktreeRoot}`);
|
|
1070
|
+
console.log(`- coding worker: ${config.codingWorker}`);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function planStatusWithReason(status, reason = "") {
|
|
1074
|
+
return {
|
|
1075
|
+
status,
|
|
1076
|
+
reason
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function buildSetupDryRunPlan(options, context, config) {
|
|
1081
|
+
const prereq = config.prereq;
|
|
1082
|
+
const dependencyPlan = buildDependencyInstallPlan(prereq.missingRequired);
|
|
1083
|
+
const workerInstallPlan = buildWorkerBackendInstallPlan(prereq.workerCommand);
|
|
1084
|
+
const workerGuide = backendSetupGuide(prereq.workerCommand);
|
|
1085
|
+
|
|
1086
|
+
let dependencyAction = planStatusWithReason("not-needed");
|
|
1087
|
+
if (!prereq.coreToolsOk) {
|
|
1088
|
+
if (options.installMissingDeps === false) {
|
|
1089
|
+
dependencyAction = planStatusWithReason("skipped", "disabled");
|
|
1090
|
+
} else if (!dependencyPlan) {
|
|
1091
|
+
dependencyAction = planStatusWithReason("unavailable", "no-supported-package-manager");
|
|
1092
|
+
} else if (options.installMissingDeps === true) {
|
|
1093
|
+
dependencyAction = planStatusWithReason("will-run");
|
|
1094
|
+
} else {
|
|
1095
|
+
dependencyAction = planStatusWithReason("would-prompt");
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
let workerAction = planStatusWithReason("not-needed");
|
|
1100
|
+
if (!prereq.workerAvailable) {
|
|
1101
|
+
if (options.installMissingBackend === false) {
|
|
1102
|
+
workerAction = planStatusWithReason("skipped", "disabled");
|
|
1103
|
+
} else if (!workerInstallPlan) {
|
|
1104
|
+
workerAction = planStatusWithReason("unavailable", "no-supported-installer");
|
|
1105
|
+
} else if (options.installMissingBackend === true) {
|
|
1106
|
+
workerAction = planStatusWithReason("will-run");
|
|
1107
|
+
} else {
|
|
1108
|
+
workerAction = planStatusWithReason("would-prompt");
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
let githubAuthAction = planStatusWithReason("not-needed");
|
|
1113
|
+
if (!prereq.ghAuthOk) {
|
|
1114
|
+
if (!commandExists("gh")) {
|
|
1115
|
+
githubAuthAction = planStatusWithReason("blocked", "gh-missing");
|
|
1116
|
+
} else if (options.ghAuthLogin === false) {
|
|
1117
|
+
githubAuthAction = planStatusWithReason("skipped", "disabled");
|
|
1118
|
+
} else if (options.ghAuthLogin === true) {
|
|
1119
|
+
githubAuthAction = planStatusWithReason("will-run");
|
|
1120
|
+
} else {
|
|
1121
|
+
githubAuthAction = planStatusWithReason("would-prompt");
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
let runtimeStartAction = planStatusWithReason("skipped", "not-requested");
|
|
1126
|
+
if (config.startRuntime) {
|
|
1127
|
+
if (!prereq.coreToolsOk) {
|
|
1128
|
+
runtimeStartAction = planStatusWithReason("blocked", `missing-tools:${prereq.missingRequired.join(",")}`);
|
|
1129
|
+
} else if (!prereq.workerAvailable) {
|
|
1130
|
+
runtimeStartAction = planStatusWithReason("blocked", `missing-worker:${prereq.workerCommand}`);
|
|
1131
|
+
} else if (!prereq.ghAuthOk) {
|
|
1132
|
+
runtimeStartAction = planStatusWithReason("blocked", "gh-auth-not-ready");
|
|
1133
|
+
} else {
|
|
1134
|
+
runtimeStartAction = planStatusWithReason("would-run");
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
let launchdAction = planStatusWithReason(process.platform === "darwin" ? "skipped" : "unsupported", process.platform === "darwin" ? "not-requested" : "non-macos");
|
|
1139
|
+
if (config.installLaunchd) {
|
|
1140
|
+
if (process.platform !== "darwin") {
|
|
1141
|
+
launchdAction = planStatusWithReason("unsupported", "non-macos");
|
|
1142
|
+
} else if (!config.startRuntime) {
|
|
1143
|
+
launchdAction = planStatusWithReason("blocked", "runtime-not-requested");
|
|
1144
|
+
} else if (runtimeStartAction.status !== "would-run") {
|
|
1145
|
+
launchdAction = planStatusWithReason("blocked", "runtime-not-ready");
|
|
1146
|
+
} else {
|
|
1147
|
+
launchdAction = planStatusWithReason("would-run");
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
return {
|
|
1152
|
+
profileExists: profileExists(context.profileRegistryRoot, config.profileId),
|
|
1153
|
+
prereq,
|
|
1154
|
+
dependencyPlan,
|
|
1155
|
+
dependencyAction,
|
|
1156
|
+
workerInstallPlan,
|
|
1157
|
+
workerAction,
|
|
1158
|
+
workerGuide,
|
|
1159
|
+
githubAuthAction,
|
|
1160
|
+
runtimeStartAction,
|
|
1161
|
+
launchdAction
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function printSetupDryRunPlan(context, config, plan) {
|
|
1166
|
+
console.log("\nDry run plan");
|
|
1167
|
+
console.log(`- mode: dry-run`);
|
|
1168
|
+
console.log(`- profile exists already: ${plan.profileExists ? "yes" : "no"}`);
|
|
1169
|
+
console.log(`- runtime home: ${context.runtimeHome}`);
|
|
1170
|
+
console.log(`- profile registry root: ${context.profileRegistryRoot}`);
|
|
1171
|
+
console.log(`- sync packaged runtime: would-run`);
|
|
1172
|
+
console.log(`- scaffold/adopt profile: would-run`);
|
|
1173
|
+
console.log(`- doctor: would-run`);
|
|
1174
|
+
console.log(`- dependency install: ${plan.dependencyAction.status}${plan.dependencyAction.reason ? ` (${plan.dependencyAction.reason})` : ""}`);
|
|
1175
|
+
if (plan.dependencyAction.status !== "not-needed" && plan.dependencyPlan && plan.dependencyPlan.commands.length > 0) {
|
|
1176
|
+
console.log(` command preview: ${plan.dependencyPlan.commands.map(formatCommand).join(" && ")}`);
|
|
1177
|
+
}
|
|
1178
|
+
console.log(`- worker backend install: ${plan.workerAction.status}${plan.workerAction.reason ? ` (${plan.workerAction.reason})` : ""}`);
|
|
1179
|
+
if (plan.workerAction.status !== "not-needed" && plan.workerInstallPlan && plan.workerInstallPlan.commands.length > 0) {
|
|
1180
|
+
console.log(` command preview: ${plan.workerInstallPlan.commands.map(formatCommand).join(" && ")}`);
|
|
1181
|
+
}
|
|
1182
|
+
console.log(`- GitHub auth step: ${plan.githubAuthAction.status}${plan.githubAuthAction.reason ? ` (${plan.githubAuthAction.reason})` : ""}`);
|
|
1183
|
+
console.log(`- runtime start: ${plan.runtimeStartAction.status}${plan.runtimeStartAction.reason ? ` (${plan.runtimeStartAction.reason})` : ""}`);
|
|
1184
|
+
console.log(`- launchd install: ${plan.launchdAction.status}${plan.launchdAction.reason ? ` (${plan.launchdAction.reason})` : ""}`);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
function buildSetupResultPayload(params) {
|
|
1188
|
+
return {
|
|
1189
|
+
setupStatus: params.setupStatus,
|
|
1190
|
+
setupMode: params.setupMode,
|
|
1191
|
+
profileId: params.profileId,
|
|
1192
|
+
repoSlug: params.repoSlug,
|
|
1193
|
+
codingWorker: params.codingWorker,
|
|
1194
|
+
profileExists: params.profileExists,
|
|
1195
|
+
paths: {
|
|
1196
|
+
repoRoot: params.repoRoot,
|
|
1197
|
+
agentRoot: params.agentRoot,
|
|
1198
|
+
agentRepoRoot: params.agentRepoRoot,
|
|
1199
|
+
worktreeRoot: params.worktreeRoot
|
|
1200
|
+
},
|
|
1201
|
+
coreTools: {
|
|
1202
|
+
status: params.coreToolsStatus,
|
|
1203
|
+
missingRequiredTools: params.missingRequiredTools
|
|
1204
|
+
},
|
|
1205
|
+
workerBackend: {
|
|
1206
|
+
command: params.workerBackendCommand,
|
|
1207
|
+
status: params.workerBackendStatus,
|
|
1208
|
+
setupGuideStatus: params.workerSetupGuideStatus,
|
|
1209
|
+
setupGuideReason: params.workerSetupGuideReason,
|
|
1210
|
+
installStatus: params.workerBackendInstallStatus,
|
|
1211
|
+
installReason: params.workerBackendInstallReason,
|
|
1212
|
+
installer: params.workerBackendInstaller,
|
|
1213
|
+
installCommand: params.workerBackendInstallCommand,
|
|
1214
|
+
docsOpened: params.workerSetupDocsOpened,
|
|
1215
|
+
docsUrl: params.workerBackendDocsUrl,
|
|
1216
|
+
installExample: params.workerBackendInstallExample,
|
|
1217
|
+
authExample: params.workerBackendAuthExample,
|
|
1218
|
+
verifyExample: params.workerBackendVerifyExample
|
|
1219
|
+
},
|
|
1220
|
+
githubAuth: {
|
|
1221
|
+
status: params.githubAuthStatus,
|
|
1222
|
+
stepStatus: params.githubAuthStepStatus,
|
|
1223
|
+
stepReason: params.githubAuthStepReason
|
|
1224
|
+
},
|
|
1225
|
+
dependencyInstall: {
|
|
1226
|
+
status: params.dependencyInstallStatus,
|
|
1227
|
+
reason: params.dependencyInstallReason,
|
|
1228
|
+
installer: params.dependencyInstaller,
|
|
1229
|
+
command: params.dependencyInstallCommand
|
|
1230
|
+
},
|
|
1231
|
+
projectInitStatus: params.projectInitStatus,
|
|
1232
|
+
doctorStatus: params.doctorStatus,
|
|
1233
|
+
runtime: {
|
|
1234
|
+
startStatus: params.runtimeStartStatus,
|
|
1235
|
+
startReason: params.runtimeStartReason,
|
|
1236
|
+
status: params.runtimeStatus
|
|
1237
|
+
},
|
|
1238
|
+
launchd: {
|
|
1239
|
+
status: params.launchdInstallStatus,
|
|
1240
|
+
reason: params.launchdInstallReason
|
|
1241
|
+
},
|
|
1242
|
+
finalFixup: {
|
|
1243
|
+
status: params.finalFixupStatus,
|
|
1244
|
+
actions: params.finalFixupActions
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function emitSetupJsonPayload(payload) {
|
|
1250
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
function collectFinalSetupIssues(config, prereq, doctorKv, runtimeStartStatus) {
|
|
1254
|
+
const issues = [];
|
|
1255
|
+
if (!prereq.coreToolsOk) {
|
|
1256
|
+
issues.push(`missing core tools: ${prereq.missingRequired.join(", ")}`);
|
|
1257
|
+
}
|
|
1258
|
+
if (!prereq.workerAvailable) {
|
|
1259
|
+
issues.push(`missing worker backend on PATH: ${prereq.workerCommand}`);
|
|
1260
|
+
}
|
|
1261
|
+
if (!prereq.ghAuthOk) {
|
|
1262
|
+
issues.push("GitHub CLI is not authenticated");
|
|
1263
|
+
}
|
|
1264
|
+
if ((doctorKv.DOCTOR_STATUS || "") !== "ok") {
|
|
1265
|
+
issues.push(`doctor status is ${doctorKv.DOCTOR_STATUS || "unknown"}`);
|
|
1266
|
+
}
|
|
1267
|
+
if (config.startRuntime && runtimeStartStatus !== "ok") {
|
|
1268
|
+
issues.push(`runtime start is ${runtimeStartStatus}`);
|
|
1269
|
+
}
|
|
1270
|
+
return issues;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
async function maybeRunFinalSetupFixups(options, scopedContext, config, currentState) {
|
|
1274
|
+
const issues = collectFinalSetupIssues(config, currentState.prereq, currentState.doctorKv, currentState.runtimeStartStatus);
|
|
1275
|
+
if (issues.length === 0) {
|
|
1276
|
+
return {
|
|
1277
|
+
status: "not-needed",
|
|
1278
|
+
actions: [],
|
|
1279
|
+
...currentState
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
console.log("\nRemaining setup items");
|
|
1284
|
+
for (const issue of issues) {
|
|
1285
|
+
console.log(`- ${issue}`);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
if (!options.interactive) {
|
|
1289
|
+
return {
|
|
1290
|
+
status: "skipped",
|
|
1291
|
+
actions: [],
|
|
1292
|
+
...currentState
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
const rl = createPromptInterface();
|
|
1297
|
+
let shouldFix = false;
|
|
1298
|
+
try {
|
|
1299
|
+
shouldFix = await promptYesNo(rl, "Run a final fix-up pass for the remaining items", true);
|
|
1300
|
+
} finally {
|
|
1301
|
+
rl.close();
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
if (!shouldFix) {
|
|
1305
|
+
return {
|
|
1306
|
+
status: "skipped",
|
|
1307
|
+
actions: [],
|
|
1308
|
+
...currentState
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
const actions = [];
|
|
1313
|
+
let prereq = currentState.prereq;
|
|
1314
|
+
let dependencyInstall = currentState.dependencyInstall;
|
|
1315
|
+
let githubAuthStep = currentState.githubAuthStep;
|
|
1316
|
+
let workerSetupStep = currentState.workerSetupStep;
|
|
1317
|
+
let doctorKv = currentState.doctorKv;
|
|
1318
|
+
let runtimeStartStatus = currentState.runtimeStartStatus;
|
|
1319
|
+
let runtimeStartReason = currentState.runtimeStartReason;
|
|
1320
|
+
let runtimeStatusKv = currentState.runtimeStatusKv;
|
|
1321
|
+
let launchdInstallStatus = currentState.launchdInstallStatus;
|
|
1322
|
+
let launchdInstallReason = currentState.launchdInstallReason;
|
|
1323
|
+
|
|
1324
|
+
if (!prereq.coreToolsOk) {
|
|
1325
|
+
actions.push("install-core-tools");
|
|
1326
|
+
dependencyInstall = await maybeInstallMissingDependencies({ ...options, installMissingDeps: true, interactive: false }, prereq);
|
|
1327
|
+
prereq = collectPrereqStatus(config.codingWorker);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
if (!prereq.workerAvailable) {
|
|
1331
|
+
actions.push("install-worker-backend");
|
|
1332
|
+
workerSetupStep = await maybeShowWorkerSetupGuide({ ...options, installMissingBackend: true, interactive: options.interactive }, prereq);
|
|
1333
|
+
prereq = collectPrereqStatus(config.codingWorker);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
if (!prereq.ghAuthOk) {
|
|
1337
|
+
actions.push("github-auth-login");
|
|
1338
|
+
githubAuthStep = await maybeRunGithubAuthLogin({ ...options, ghAuthLogin: true, interactive: false }, prereq);
|
|
1339
|
+
prereq = collectPrereqStatus(config.codingWorker);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if ((doctorKv.DOCTOR_STATUS || "") !== "ok") {
|
|
1343
|
+
actions.push("doctor-resync");
|
|
1344
|
+
runSetupStep(scopedContext, "Re-sync packaged runtime into ~/.agent-runtime", "tools/bin/sync-shared-agent-home.sh", []);
|
|
1345
|
+
const doctorOutput = runSetupStep(scopedContext, "Re-check runtime and profile health", "tools/bin/flow-runtime-doctor.sh", []);
|
|
1346
|
+
doctorKv = parseKvOutput(doctorOutput);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
if (config.startRuntime && runtimeStartStatus !== "ok") {
|
|
1350
|
+
if (!prereq.coreToolsOk) {
|
|
1351
|
+
runtimeStartStatus = "skipped";
|
|
1352
|
+
runtimeStartReason = `missing-tools:${prereq.missingRequired.join(",")}`;
|
|
1353
|
+
} else if (!prereq.workerAvailable) {
|
|
1354
|
+
runtimeStartStatus = "skipped";
|
|
1355
|
+
runtimeStartReason = `missing-worker:${prereq.workerCommand}`;
|
|
1356
|
+
} else if (!prereq.ghAuthOk) {
|
|
1357
|
+
runtimeStartStatus = "skipped";
|
|
1358
|
+
runtimeStartReason = "gh-auth-not-ready";
|
|
1359
|
+
} else if ((doctorKv.DOCTOR_STATUS || "") !== "ok") {
|
|
1360
|
+
runtimeStartStatus = "skipped";
|
|
1361
|
+
runtimeStartReason = `doctor-${doctorKv.DOCTOR_STATUS || "not-ok"}`;
|
|
1362
|
+
} else {
|
|
1363
|
+
actions.push("runtime-start");
|
|
1364
|
+
runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId]);
|
|
1365
|
+
const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId]);
|
|
1366
|
+
runtimeStatusKv = parseKvOutput(runtimeStatusOutput);
|
|
1367
|
+
runtimeStartStatus = "ok";
|
|
1368
|
+
runtimeStartReason = "";
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
if (config.installLaunchd && process.platform === "darwin" && launchdInstallStatus !== "ok" && runtimeStartStatus === "ok") {
|
|
1373
|
+
actions.push("launchd-install");
|
|
1374
|
+
runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId]);
|
|
1375
|
+
launchdInstallStatus = "ok";
|
|
1376
|
+
launchdInstallReason = "";
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
const finalIssues = collectFinalSetupIssues(config, prereq, doctorKv, runtimeStartStatus);
|
|
1380
|
+
let status = "ok";
|
|
1381
|
+
if (dependencyInstall.status === "failed" || githubAuthStep.status === "failed" || workerSetupStep.installStatus === "failed") {
|
|
1382
|
+
status = "failed";
|
|
1383
|
+
} else if (finalIssues.length > 0) {
|
|
1384
|
+
status = "remaining";
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
return {
|
|
1388
|
+
status,
|
|
1389
|
+
actions,
|
|
1390
|
+
prereq,
|
|
1391
|
+
dependencyInstall,
|
|
1392
|
+
githubAuthStep,
|
|
1393
|
+
workerSetupStep,
|
|
1394
|
+
doctorKv,
|
|
1395
|
+
runtimeStartStatus,
|
|
1396
|
+
runtimeStartReason,
|
|
1397
|
+
runtimeStatusKv,
|
|
1398
|
+
launchdInstallStatus,
|
|
1399
|
+
launchdInstallReason
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
async function collectSetupConfig(options, context) {
|
|
1404
|
+
const detectedRepoRoot = path.resolve(options.repoRoot || detectRepoRoot(process.cwd()) || process.cwd());
|
|
1405
|
+
const detectedRepoSlug = options.repoSlug || detectRepoSlug(detectedRepoRoot);
|
|
1406
|
+
const suggestedProfileId = options.profileId || sanitizeProfileId((detectedRepoSlug.split("/").pop() || path.basename(detectedRepoRoot)));
|
|
1407
|
+
const suggestedWorker = options.codingWorker || detectPreferredWorker();
|
|
1408
|
+
|
|
1409
|
+
let repoRoot = detectedRepoRoot;
|
|
1410
|
+
let repoSlug = detectedRepoSlug;
|
|
1411
|
+
let profileId = suggestedProfileId;
|
|
1412
|
+
let codingWorker = suggestedWorker;
|
|
1413
|
+
|
|
1414
|
+
if (!fs.existsSync(detectedRepoRoot)) {
|
|
1415
|
+
throw new Error(`setup repo root does not exist: ${detectedRepoRoot}`);
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
if (!options.interactive) {
|
|
1419
|
+
if (!repoSlug) {
|
|
1420
|
+
throw new Error("setup could not detect --repo-slug automatically; pass --repo-slug <owner/repo> or run interactively inside a git checkout with origin set");
|
|
1421
|
+
}
|
|
1422
|
+
} else {
|
|
1423
|
+
const rl = createPromptInterface();
|
|
1424
|
+
try {
|
|
1425
|
+
console.log("ACP setup will guide one repo profile from install to operator-ready defaults.");
|
|
1426
|
+
console.log("Press Enter to accept the suggested value shown in brackets.\n");
|
|
1427
|
+
|
|
1428
|
+
repoRoot = path.resolve(await promptText(rl, "Local repo root", detectedRepoRoot));
|
|
1429
|
+
repoSlug = await promptText(rl, "GitHub repo slug", repoSlug || "");
|
|
1430
|
+
profileId = sanitizeProfileId(await promptText(rl, "Profile id", profileId));
|
|
1431
|
+
codingWorker = await promptText(rl, "Coding worker (codex|claude|openclaw)", codingWorker);
|
|
1432
|
+
|
|
1433
|
+
if (!["codex", "claude", "openclaw"].includes(codingWorker)) {
|
|
1434
|
+
throw new Error(`unsupported coding worker: ${codingWorker}`);
|
|
1435
|
+
}
|
|
1436
|
+
} finally {
|
|
1437
|
+
rl.close();
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
if (!["codex", "claude", "openclaw"].includes(codingWorker)) {
|
|
1442
|
+
throw new Error(`unsupported coding worker: ${codingWorker}`);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
const paths = buildSetupPaths(context.platformHome, repoRoot, profileId, options);
|
|
1446
|
+
const prereq = collectPrereqStatus(codingWorker);
|
|
1447
|
+
const config = {
|
|
1448
|
+
profileId,
|
|
1449
|
+
repoSlug,
|
|
1450
|
+
repoRoot,
|
|
1451
|
+
codingWorker,
|
|
1452
|
+
paths,
|
|
1453
|
+
prereq
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
renderSetupSummary(config);
|
|
1457
|
+
|
|
1458
|
+
if (options.interactive) {
|
|
1459
|
+
printPrereqSummary(prereq);
|
|
1460
|
+
const rl = createPromptInterface();
|
|
1461
|
+
try {
|
|
1462
|
+
if (!prereq.coreToolsOk || !prereq.workerAvailable || !prereq.ghAuthOk) {
|
|
1463
|
+
console.log("\nACP can still scaffold the profile now, but runtime start may be skipped until these checks are green.");
|
|
1464
|
+
}
|
|
1465
|
+
const shouldContinue = await promptYesNo(rl, "Continue with these values", true);
|
|
1466
|
+
if (!shouldContinue) {
|
|
1467
|
+
throw new Error("setup cancelled");
|
|
1468
|
+
}
|
|
1469
|
+
if (options.startRuntime === null) {
|
|
1470
|
+
options.startRuntime = await promptYesNo(rl, "Start the runtime after setup", true);
|
|
1471
|
+
}
|
|
1472
|
+
if (process.platform === "darwin" && options.installLaunchd === null && options.startRuntime) {
|
|
1473
|
+
options.installLaunchd = await promptYesNo(rl, "Install macOS autostart for this profile", false);
|
|
1474
|
+
}
|
|
1475
|
+
} finally {
|
|
1476
|
+
rl.close();
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
if (options.startRuntime === null) {
|
|
1481
|
+
options.startRuntime = false;
|
|
1482
|
+
}
|
|
1483
|
+
if (options.installLaunchd === null) {
|
|
1484
|
+
options.installLaunchd = false;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
return {
|
|
1488
|
+
...config,
|
|
1489
|
+
startRuntime: Boolean(options.startRuntime),
|
|
1490
|
+
installLaunchd: Boolean(options.installLaunchd)
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
function runSetupStep(context, title, scriptRelativePath, args, options = {}) {
|
|
1495
|
+
console.log(`\n== ${title} ==`);
|
|
1496
|
+
const result = runScriptWithContext(context, scriptRelativePath, args, { stdio: "pipe", env: options.env, cwd: options.cwd });
|
|
1497
|
+
if (result.status !== 0) {
|
|
1498
|
+
printFailureDetails(result);
|
|
1499
|
+
throw new Error(`${title} failed`);
|
|
1500
|
+
}
|
|
1501
|
+
return result.stdout || "";
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
async function runSetupFlow(forwardedArgs) {
|
|
1505
|
+
const jsonRequested = forwardedArgs.includes("--json");
|
|
1506
|
+
let options;
|
|
1507
|
+
try {
|
|
1508
|
+
options = parseSetupArgs(forwardedArgs);
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
if (jsonRequested) {
|
|
1511
|
+
emitSetupJsonPayload({
|
|
1512
|
+
setupStatus: "error",
|
|
1513
|
+
setupMode: "unknown",
|
|
1514
|
+
error: error.message
|
|
1515
|
+
});
|
|
1516
|
+
} else {
|
|
1517
|
+
console.error(error.message);
|
|
1518
|
+
printSetupHelp();
|
|
1519
|
+
}
|
|
1520
|
+
return 64;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
if (options.help) {
|
|
1524
|
+
printSetupHelp();
|
|
1525
|
+
return 0;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
const originalConsoleLog = console.log;
|
|
1529
|
+
const originalConsoleError = console.error;
|
|
1530
|
+
if (options.json) {
|
|
1531
|
+
setupJsonOutputEnabled = true;
|
|
1532
|
+
console.log = (...args) => {
|
|
1533
|
+
process.stderr.write(`${util.format(...args)}\n`);
|
|
1534
|
+
};
|
|
1535
|
+
console.error = (...args) => {
|
|
1536
|
+
process.stderr.write(`${util.format(...args)}\n`);
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
const stage = stageSharedHome();
|
|
1541
|
+
const context = createExecutionContext(stage);
|
|
1542
|
+
|
|
1543
|
+
try {
|
|
1544
|
+
const config = await collectSetupConfig(options, context);
|
|
1545
|
+
if (options.dryRun) {
|
|
1546
|
+
const plan = buildSetupDryRunPlan(options, context, config);
|
|
1547
|
+
printSetupDryRunPlan(context, config, plan);
|
|
1548
|
+
const dryRunWorkerBackendInstallCommand = plan.workerAction.status !== "not-needed" && plan.workerInstallPlan && plan.workerInstallPlan.commands.length > 0
|
|
1549
|
+
? plan.workerInstallPlan.commands.map(formatCommand).join(" && ")
|
|
1550
|
+
: "";
|
|
1551
|
+
const dryRunDependencyInstallCommand = plan.dependencyAction.status !== "not-needed" && plan.dependencyPlan && plan.dependencyPlan.commands.length > 0
|
|
1552
|
+
? plan.dependencyPlan.commands.map(formatCommand).join(" && ")
|
|
1553
|
+
: "";
|
|
1554
|
+
const dryRunPayload = buildSetupResultPayload({
|
|
1555
|
+
setupStatus: "dry-run",
|
|
1556
|
+
setupMode: "dry-run",
|
|
1557
|
+
profileId: config.profileId,
|
|
1558
|
+
repoSlug: config.repoSlug,
|
|
1559
|
+
codingWorker: config.codingWorker,
|
|
1560
|
+
profileExists: plan.profileExists,
|
|
1561
|
+
repoRoot: config.paths.repoRoot,
|
|
1562
|
+
agentRoot: config.paths.agentRoot,
|
|
1563
|
+
agentRepoRoot: config.paths.agentRepoRoot,
|
|
1564
|
+
worktreeRoot: config.paths.worktreeRoot,
|
|
1565
|
+
coreToolsStatus: plan.prereq.coreToolsOk ? "ok" : "missing",
|
|
1566
|
+
missingRequiredTools: plan.prereq.missingRequired,
|
|
1567
|
+
workerBackendCommand: plan.prereq.workerCommand,
|
|
1568
|
+
workerBackendStatus: plan.prereq.workerAvailable ? "ok" : "missing",
|
|
1569
|
+
workerSetupGuideStatus: "planned",
|
|
1570
|
+
workerSetupGuideReason: plan.workerAction.reason || "",
|
|
1571
|
+
workerBackendInstallStatus: plan.workerAction.status,
|
|
1572
|
+
workerBackendInstallReason: plan.workerAction.reason || "",
|
|
1573
|
+
workerBackendInstaller: plan.workerInstallPlan ? plan.workerInstallPlan.installer : "",
|
|
1574
|
+
workerBackendInstallCommand: dryRunWorkerBackendInstallCommand,
|
|
1575
|
+
workerSetupDocsOpened: "planned",
|
|
1576
|
+
workerBackendDocsUrl: plan.workerGuide.docsUrl || "",
|
|
1577
|
+
workerBackendInstallExample: plan.workerGuide.installExamples[0] || "",
|
|
1578
|
+
workerBackendAuthExample: plan.workerGuide.authExamples[0] || "",
|
|
1579
|
+
workerBackendVerifyExample: plan.workerGuide.verifyExamples[0] || "",
|
|
1580
|
+
githubAuthStatus: plan.prereq.ghAuthOk ? "ok" : "not-ready",
|
|
1581
|
+
githubAuthStepStatus: plan.githubAuthAction.status,
|
|
1582
|
+
githubAuthStepReason: plan.githubAuthAction.reason || "",
|
|
1583
|
+
dependencyInstallStatus: plan.dependencyAction.status,
|
|
1584
|
+
dependencyInstallReason: plan.dependencyAction.reason || "",
|
|
1585
|
+
dependencyInstaller: plan.dependencyPlan ? plan.dependencyPlan.manager : "",
|
|
1586
|
+
dependencyInstallCommand: dryRunDependencyInstallCommand,
|
|
1587
|
+
projectInitStatus: "would-run",
|
|
1588
|
+
doctorStatus: "would-run",
|
|
1589
|
+
runtimeStartStatus: plan.runtimeStartAction.status,
|
|
1590
|
+
runtimeStartReason: plan.runtimeStartAction.reason || "",
|
|
1591
|
+
runtimeStatus: "",
|
|
1592
|
+
launchdInstallStatus: plan.launchdAction.status,
|
|
1593
|
+
launchdInstallReason: plan.launchdAction.reason || "",
|
|
1594
|
+
finalFixupStatus: "planned",
|
|
1595
|
+
finalFixupActions: ["review-plan"]
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
if (options.json) {
|
|
1599
|
+
emitSetupJsonPayload(dryRunPayload);
|
|
1600
|
+
} else {
|
|
1601
|
+
console.log(`SETUP_STATUS=dry-run`);
|
|
1602
|
+
console.log(`SETUP_MODE=dry-run`);
|
|
1603
|
+
console.log(`PROFILE_ID=${config.profileId}`);
|
|
1604
|
+
console.log(`REPO_SLUG=${config.repoSlug}`);
|
|
1605
|
+
console.log(`REPO_ROOT=${config.paths.repoRoot}`);
|
|
1606
|
+
console.log(`AGENT_ROOT=${config.paths.agentRoot}`);
|
|
1607
|
+
console.log(`AGENT_REPO_ROOT=${config.paths.agentRepoRoot}`);
|
|
1608
|
+
console.log(`WORKTREE_ROOT=${config.paths.worktreeRoot}`);
|
|
1609
|
+
console.log(`CODING_WORKER=${config.codingWorker}`);
|
|
1610
|
+
console.log(`PROFILE_EXISTS=${plan.profileExists ? "yes" : "no"}`);
|
|
1611
|
+
console.log(`CORE_TOOLS_STATUS=${plan.prereq.coreToolsOk ? "ok" : "missing"}`);
|
|
1612
|
+
console.log(`MISSING_REQUIRED_TOOLS=${plan.prereq.missingRequired.join(",")}`);
|
|
1613
|
+
console.log(`WORKER_BACKEND_COMMAND=${plan.prereq.workerCommand}`);
|
|
1614
|
+
console.log(`WORKER_BACKEND_STATUS=${plan.prereq.workerAvailable ? "ok" : "missing"}`);
|
|
1615
|
+
console.log(`GITHUB_AUTH_STATUS=${plan.prereq.ghAuthOk ? "ok" : "not-ready"}`);
|
|
1616
|
+
console.log(`DEPENDENCY_INSTALL_STATUS=${plan.dependencyAction.status}`);
|
|
1617
|
+
if (plan.dependencyAction.reason) {
|
|
1618
|
+
console.log(`DEPENDENCY_INSTALL_REASON=${plan.dependencyAction.reason}`);
|
|
1619
|
+
}
|
|
1620
|
+
if (plan.dependencyPlan && plan.dependencyPlan.commands.length > 0) {
|
|
1621
|
+
console.log(`DEPENDENCY_INSTALL_COMMAND=${plan.dependencyPlan.commands.map(formatCommand).join(" && ")}`);
|
|
1622
|
+
}
|
|
1623
|
+
console.log(`WORKER_BACKEND_INSTALL_STATUS=${plan.workerAction.status}`);
|
|
1624
|
+
if (plan.workerAction.reason) {
|
|
1625
|
+
console.log(`WORKER_BACKEND_INSTALL_REASON=${plan.workerAction.reason}`);
|
|
1626
|
+
}
|
|
1627
|
+
if (plan.workerInstallPlan && plan.workerInstallPlan.installer) {
|
|
1628
|
+
console.log(`WORKER_BACKEND_INSTALLER=${plan.workerInstallPlan.installer}`);
|
|
1629
|
+
}
|
|
1630
|
+
if (plan.workerInstallPlan && plan.workerInstallPlan.commands.length > 0) {
|
|
1631
|
+
console.log(`WORKER_BACKEND_INSTALL_COMMAND=${plan.workerInstallPlan.commands.map(formatCommand).join(" && ")}`);
|
|
1632
|
+
}
|
|
1633
|
+
if (plan.workerGuide.docsUrl) {
|
|
1634
|
+
console.log(`WORKER_BACKEND_DOCS_URL=${plan.workerGuide.docsUrl}`);
|
|
1635
|
+
}
|
|
1636
|
+
if (plan.workerGuide.installExamples[0]) {
|
|
1637
|
+
console.log(`WORKER_BACKEND_INSTALL_EXAMPLE=${plan.workerGuide.installExamples[0]}`);
|
|
1638
|
+
}
|
|
1639
|
+
if (plan.workerGuide.authExamples[0]) {
|
|
1640
|
+
console.log(`WORKER_BACKEND_AUTH_EXAMPLE=${plan.workerGuide.authExamples[0]}`);
|
|
1641
|
+
}
|
|
1642
|
+
if (plan.workerGuide.verifyExamples[0]) {
|
|
1643
|
+
console.log(`WORKER_BACKEND_VERIFY_EXAMPLE=${plan.workerGuide.verifyExamples[0]}`);
|
|
1644
|
+
}
|
|
1645
|
+
console.log(`GITHUB_AUTH_STEP_STATUS=${plan.githubAuthAction.status}`);
|
|
1646
|
+
if (plan.githubAuthAction.reason) {
|
|
1647
|
+
console.log(`GITHUB_AUTH_STEP_REASON=${plan.githubAuthAction.reason}`);
|
|
1648
|
+
}
|
|
1649
|
+
console.log(`PROJECT_INIT_STATUS=would-run`);
|
|
1650
|
+
console.log(`DOCTOR_STATUS=would-run`);
|
|
1651
|
+
console.log(`RUNTIME_START_STATUS=${plan.runtimeStartAction.status}`);
|
|
1652
|
+
if (plan.runtimeStartAction.reason) {
|
|
1653
|
+
console.log(`RUNTIME_START_REASON=${plan.runtimeStartAction.reason}`);
|
|
1654
|
+
}
|
|
1655
|
+
console.log(`LAUNCHD_INSTALL_STATUS=${plan.launchdAction.status}`);
|
|
1656
|
+
if (plan.launchdAction.reason) {
|
|
1657
|
+
console.log(`LAUNCHD_INSTALL_REASON=${plan.launchdAction.reason}`);
|
|
1658
|
+
}
|
|
1659
|
+
console.log(`FINAL_FIXUP_STATUS=planned`);
|
|
1660
|
+
console.log(`FINAL_FIXUP_ACTIONS=review-plan`);
|
|
1661
|
+
}
|
|
1662
|
+
return 0;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
if (profileExists(context.profileRegistryRoot, config.profileId) && !options.force) {
|
|
1666
|
+
console.error(`setup found an existing profile at ${path.join(context.profileRegistryRoot, config.profileId)}.`);
|
|
1667
|
+
console.error("Re-run with --force if you want to overwrite it.");
|
|
1668
|
+
return 1;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
let prereq = config.prereq;
|
|
1672
|
+
let dependencyInstall = await maybeInstallMissingDependencies(options, prereq);
|
|
1673
|
+
if (dependencyInstall.status === "failed") {
|
|
1674
|
+
console.error("dependency installation failed");
|
|
1675
|
+
return 1;
|
|
1676
|
+
}
|
|
1677
|
+
prereq = collectPrereqStatus(config.codingWorker);
|
|
1678
|
+
|
|
1679
|
+
let githubAuthStep = await maybeRunGithubAuthLogin(options, prereq);
|
|
1680
|
+
if (githubAuthStep.status === "failed") {
|
|
1681
|
+
console.error("GitHub authentication failed");
|
|
1682
|
+
return 1;
|
|
1683
|
+
}
|
|
1684
|
+
prereq = collectPrereqStatus(config.codingWorker);
|
|
1685
|
+
let workerSetupStep = await maybeShowWorkerSetupGuide(options, prereq);
|
|
1686
|
+
prereq = collectPrereqStatus(config.codingWorker);
|
|
1687
|
+
|
|
1688
|
+
const scopedContext = buildScopedContext(context, config.profileId);
|
|
1689
|
+
|
|
1690
|
+
runSetupStep(scopedContext, "Sync packaged runtime into ~/.agent-runtime", "tools/bin/sync-shared-agent-home.sh", []);
|
|
1691
|
+
|
|
1692
|
+
const initArgs = [
|
|
1693
|
+
"--profile-id", config.profileId,
|
|
1694
|
+
"--repo-slug", config.repoSlug,
|
|
1695
|
+
"--repo-root", config.paths.repoRoot,
|
|
1696
|
+
"--agent-root", config.paths.agentRoot,
|
|
1697
|
+
"--agent-repo-root", config.paths.agentRepoRoot,
|
|
1698
|
+
"--worktree-root", config.paths.worktreeRoot,
|
|
1699
|
+
"--retained-repo-root", config.paths.retainedRepoRoot,
|
|
1700
|
+
"--vscode-workspace-file", config.paths.vscodeWorkspaceFile,
|
|
1701
|
+
"--source-repo-root", config.paths.sourceRepoRoot,
|
|
1702
|
+
"--coding-worker", config.codingWorker
|
|
1703
|
+
];
|
|
1704
|
+
if (options.force) {
|
|
1705
|
+
initArgs.push("--force");
|
|
1706
|
+
}
|
|
1707
|
+
if (options.skipAnchorSync) {
|
|
1708
|
+
initArgs.push("--skip-anchor-sync");
|
|
1709
|
+
}
|
|
1710
|
+
if (options.skipWorkspaceSync) {
|
|
1711
|
+
initArgs.push("--skip-workspace-sync");
|
|
1712
|
+
}
|
|
1713
|
+
if (options.allowMissingRepo) {
|
|
1714
|
+
initArgs.push("--allow-missing-repo");
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
const initOutput = runSetupStep(scopedContext, "Scaffold and adopt the project profile", "tools/bin/project-init.sh", initArgs);
|
|
1718
|
+
const initKv = parseKvOutput(initOutput);
|
|
1719
|
+
|
|
1720
|
+
const doctorOutput = runSetupStep(scopedContext, "Check runtime and profile health", "tools/bin/flow-runtime-doctor.sh", []);
|
|
1721
|
+
let doctorKv = parseKvOutput(doctorOutput);
|
|
1722
|
+
|
|
1723
|
+
let runtimeStartStatus = "skipped";
|
|
1724
|
+
let runtimeStartReason = "not-requested";
|
|
1725
|
+
let runtimeStatusKv = {};
|
|
1726
|
+
|
|
1727
|
+
if (config.startRuntime) {
|
|
1728
|
+
if (prereq.missingRequired.length > 0) {
|
|
1729
|
+
runtimeStartReason = `missing-tools:${prereq.missingRequired.join(",")}`;
|
|
1730
|
+
console.log(`runtime start skipped: missing required tools (${prereq.missingRequired.join(", ")})`);
|
|
1731
|
+
} else if (!prereq.workerAvailable) {
|
|
1732
|
+
runtimeStartReason = `missing-worker:${prereq.workerCommand}`;
|
|
1733
|
+
console.log(`runtime start skipped: ${prereq.workerCommand} is not available on PATH`);
|
|
1734
|
+
} else if (!prereq.ghAuthOk) {
|
|
1735
|
+
runtimeStartReason = "gh-auth-not-ready";
|
|
1736
|
+
console.log("runtime start skipped: GitHub CLI is not authenticated yet. Run `gh auth login` and start the runtime afterwards.");
|
|
1737
|
+
} else {
|
|
1738
|
+
runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId]);
|
|
1739
|
+
const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId]);
|
|
1740
|
+
runtimeStatusKv = parseKvOutput(runtimeStatusOutput);
|
|
1741
|
+
runtimeStartStatus = "ok";
|
|
1742
|
+
runtimeStartReason = "";
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
let launchdInstallStatus = "skipped";
|
|
1747
|
+
let launchdInstallReason = process.platform === "darwin" ? "not-requested" : "non-macos";
|
|
1748
|
+
|
|
1749
|
+
if (config.installLaunchd) {
|
|
1750
|
+
if (process.platform !== "darwin") {
|
|
1751
|
+
console.log("launchd install skipped: this command is only relevant on macOS.");
|
|
1752
|
+
} else if (runtimeStartStatus !== "ok") {
|
|
1753
|
+
launchdInstallReason = "runtime-not-started";
|
|
1754
|
+
console.log("launchd install skipped: runtime was not started successfully in this setup run.");
|
|
1755
|
+
} else {
|
|
1756
|
+
runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId]);
|
|
1757
|
+
launchdInstallStatus = "ok";
|
|
1758
|
+
launchdInstallReason = "";
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
const finalFixup = await maybeRunFinalSetupFixups(options, scopedContext, config, {
|
|
1763
|
+
prereq,
|
|
1764
|
+
dependencyInstall,
|
|
1765
|
+
githubAuthStep,
|
|
1766
|
+
workerSetupStep,
|
|
1767
|
+
doctorKv,
|
|
1768
|
+
runtimeStartStatus,
|
|
1769
|
+
runtimeStartReason,
|
|
1770
|
+
runtimeStatusKv,
|
|
1771
|
+
launchdInstallStatus,
|
|
1772
|
+
launchdInstallReason
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
prereq = finalFixup.prereq;
|
|
1776
|
+
dependencyInstall = finalFixup.dependencyInstall;
|
|
1777
|
+
githubAuthStep = finalFixup.githubAuthStep;
|
|
1778
|
+
workerSetupStep = finalFixup.workerSetupStep;
|
|
1779
|
+
doctorKv = finalFixup.doctorKv;
|
|
1780
|
+
runtimeStartStatus = finalFixup.runtimeStartStatus;
|
|
1781
|
+
runtimeStartReason = finalFixup.runtimeStartReason;
|
|
1782
|
+
runtimeStatusKv = finalFixup.runtimeStatusKv;
|
|
1783
|
+
launchdInstallStatus = finalFixup.launchdInstallStatus;
|
|
1784
|
+
launchdInstallReason = finalFixup.launchdInstallReason;
|
|
1785
|
+
|
|
1786
|
+
const runPayload = buildSetupResultPayload({
|
|
1787
|
+
setupStatus: "ok",
|
|
1788
|
+
setupMode: "run",
|
|
1789
|
+
profileId: config.profileId,
|
|
1790
|
+
repoSlug: config.repoSlug,
|
|
1791
|
+
codingWorker: config.codingWorker,
|
|
1792
|
+
profileExists: true,
|
|
1793
|
+
repoRoot: config.paths.repoRoot,
|
|
1794
|
+
agentRoot: config.paths.agentRoot,
|
|
1795
|
+
agentRepoRoot: config.paths.agentRepoRoot,
|
|
1796
|
+
worktreeRoot: config.paths.worktreeRoot,
|
|
1797
|
+
coreToolsStatus: prereq.coreToolsOk ? "ok" : "missing",
|
|
1798
|
+
missingRequiredTools: prereq.missingRequired,
|
|
1799
|
+
workerBackendCommand: prereq.workerCommand,
|
|
1800
|
+
workerBackendStatus: prereq.workerAvailable ? "ok" : "missing",
|
|
1801
|
+
workerSetupGuideStatus: workerSetupStep.status,
|
|
1802
|
+
workerSetupGuideReason: workerSetupStep.reason || "",
|
|
1803
|
+
workerBackendInstallStatus: workerSetupStep.installStatus,
|
|
1804
|
+
workerBackendInstallReason: workerSetupStep.installReason || "",
|
|
1805
|
+
workerBackendInstaller: workerSetupStep.installer || "",
|
|
1806
|
+
workerBackendInstallCommand: workerSetupStep.commands.length > 0 ? workerSetupStep.commands.map(formatCommand).join(" && ") : "",
|
|
1807
|
+
workerSetupDocsOpened: workerSetupStep.docsOpened,
|
|
1808
|
+
workerBackendDocsUrl: workerSetupStep.guide.docsUrl || "",
|
|
1809
|
+
workerBackendInstallExample: workerSetupStep.guide.installExamples[0] || "",
|
|
1810
|
+
workerBackendAuthExample: workerSetupStep.guide.authExamples[0] || "",
|
|
1811
|
+
workerBackendVerifyExample: workerSetupStep.guide.verifyExamples[0] || "",
|
|
1812
|
+
githubAuthStatus: prereq.ghAuthOk ? "ok" : "not-ready",
|
|
1813
|
+
githubAuthStepStatus: githubAuthStep.status,
|
|
1814
|
+
githubAuthStepReason: githubAuthStep.reason || "",
|
|
1815
|
+
dependencyInstallStatus: dependencyInstall.status,
|
|
1816
|
+
dependencyInstallReason: dependencyInstall.reason || "",
|
|
1817
|
+
dependencyInstaller: dependencyInstall.installer || "",
|
|
1818
|
+
dependencyInstallCommand: dependencyInstall.commands.length > 0 ? dependencyInstall.commands.map(formatCommand).join(" && ") : "",
|
|
1819
|
+
projectInitStatus: initKv.PROJECT_INIT_STATUS || "ok",
|
|
1820
|
+
doctorStatus: doctorKv.DOCTOR_STATUS || "",
|
|
1821
|
+
runtimeStartStatus,
|
|
1822
|
+
runtimeStartReason: runtimeStartReason || "",
|
|
1823
|
+
runtimeStatus: runtimeStatusKv.RUNTIME_STATUS || "",
|
|
1824
|
+
launchdInstallStatus,
|
|
1825
|
+
launchdInstallReason: launchdInstallReason || "",
|
|
1826
|
+
finalFixupStatus: finalFixup.status,
|
|
1827
|
+
finalFixupActions: finalFixup.actions
|
|
1828
|
+
});
|
|
1829
|
+
|
|
1830
|
+
if (options.json) {
|
|
1831
|
+
emitSetupJsonPayload(runPayload);
|
|
1832
|
+
} else {
|
|
1833
|
+
console.log("\nSetup complete.");
|
|
1834
|
+
console.log(`- profile: ${config.profileId}`);
|
|
1835
|
+
console.log(`- repo: ${config.repoSlug}`);
|
|
1836
|
+
console.log(`- runtime home: ${context.runtimeHome}`);
|
|
1837
|
+
console.log(`- next status command: npx agent-control-plane@latest runtime status --profile-id ${config.profileId}`);
|
|
1838
|
+
|
|
1839
|
+
console.log(`SETUP_STATUS=ok`);
|
|
1840
|
+
console.log(`PROFILE_ID=${config.profileId}`);
|
|
1841
|
+
console.log(`REPO_SLUG=${config.repoSlug}`);
|
|
1842
|
+
console.log(`REPO_ROOT=${config.paths.repoRoot}`);
|
|
1843
|
+
console.log(`AGENT_ROOT=${config.paths.agentRoot}`);
|
|
1844
|
+
console.log(`AGENT_REPO_ROOT=${config.paths.agentRepoRoot}`);
|
|
1845
|
+
console.log(`WORKTREE_ROOT=${config.paths.worktreeRoot}`);
|
|
1846
|
+
console.log(`CODING_WORKER=${config.codingWorker}`);
|
|
1847
|
+
console.log(`CORE_TOOLS_STATUS=${prereq.coreToolsOk ? "ok" : "missing"}`);
|
|
1848
|
+
console.log(`MISSING_REQUIRED_TOOLS=${prereq.missingRequired.join(",")}`);
|
|
1849
|
+
console.log(`WORKER_BACKEND_COMMAND=${prereq.workerCommand}`);
|
|
1850
|
+
console.log(`WORKER_BACKEND_STATUS=${prereq.workerAvailable ? "ok" : "missing"}`);
|
|
1851
|
+
console.log(`WORKER_SETUP_GUIDE_STATUS=${workerSetupStep.status}`);
|
|
1852
|
+
if (workerSetupStep.reason) {
|
|
1853
|
+
console.log(`WORKER_SETUP_GUIDE_REASON=${workerSetupStep.reason}`);
|
|
1854
|
+
}
|
|
1855
|
+
console.log(`WORKER_BACKEND_INSTALL_STATUS=${workerSetupStep.installStatus}`);
|
|
1856
|
+
if (workerSetupStep.installReason) {
|
|
1857
|
+
console.log(`WORKER_BACKEND_INSTALL_REASON=${workerSetupStep.installReason}`);
|
|
1858
|
+
}
|
|
1859
|
+
if (workerSetupStep.installer) {
|
|
1860
|
+
console.log(`WORKER_BACKEND_INSTALLER=${workerSetupStep.installer}`);
|
|
1861
|
+
}
|
|
1862
|
+
if (workerSetupStep.commands.length > 0) {
|
|
1863
|
+
console.log(`WORKER_BACKEND_INSTALL_COMMAND=${workerSetupStep.commands.map(formatCommand).join(" && ")}`);
|
|
1864
|
+
}
|
|
1865
|
+
console.log(`WORKER_SETUP_DOCS_OPENED=${workerSetupStep.docsOpened}`);
|
|
1866
|
+
if (workerSetupStep.guide.docsUrl) {
|
|
1867
|
+
console.log(`WORKER_BACKEND_DOCS_URL=${workerSetupStep.guide.docsUrl}`);
|
|
1868
|
+
}
|
|
1869
|
+
if (workerSetupStep.guide.installExamples[0]) {
|
|
1870
|
+
console.log(`WORKER_BACKEND_INSTALL_EXAMPLE=${workerSetupStep.guide.installExamples[0]}`);
|
|
1871
|
+
}
|
|
1872
|
+
if (workerSetupStep.guide.authExamples[0]) {
|
|
1873
|
+
console.log(`WORKER_BACKEND_AUTH_EXAMPLE=${workerSetupStep.guide.authExamples[0]}`);
|
|
1874
|
+
}
|
|
1875
|
+
if (workerSetupStep.guide.verifyExamples[0]) {
|
|
1876
|
+
console.log(`WORKER_BACKEND_VERIFY_EXAMPLE=${workerSetupStep.guide.verifyExamples[0]}`);
|
|
1877
|
+
}
|
|
1878
|
+
console.log(`GITHUB_AUTH_STATUS=${prereq.ghAuthOk ? "ok" : "not-ready"}`);
|
|
1879
|
+
console.log(`FINAL_FIXUP_STATUS=${finalFixup.status}`);
|
|
1880
|
+
console.log(`FINAL_FIXUP_ACTIONS=${finalFixup.actions.join(",")}`);
|
|
1881
|
+
console.log(`DEPENDENCY_INSTALL_STATUS=${dependencyInstall.status}`);
|
|
1882
|
+
if (dependencyInstall.reason) {
|
|
1883
|
+
console.log(`DEPENDENCY_INSTALL_REASON=${dependencyInstall.reason}`);
|
|
1884
|
+
}
|
|
1885
|
+
if (dependencyInstall.installer) {
|
|
1886
|
+
console.log(`DEPENDENCY_INSTALLER=${dependencyInstall.installer}`);
|
|
1887
|
+
}
|
|
1888
|
+
if (dependencyInstall.commands.length > 0) {
|
|
1889
|
+
console.log(`DEPENDENCY_INSTALL_COMMAND=${dependencyInstall.commands.map(formatCommand).join(" && ")}`);
|
|
1890
|
+
}
|
|
1891
|
+
console.log(`GITHUB_AUTH_STEP_STATUS=${githubAuthStep.status}`);
|
|
1892
|
+
if (githubAuthStep.reason) {
|
|
1893
|
+
console.log(`GITHUB_AUTH_STEP_REASON=${githubAuthStep.reason}`);
|
|
1894
|
+
}
|
|
1895
|
+
console.log(`PROJECT_INIT_STATUS=${initKv.PROJECT_INIT_STATUS || "ok"}`);
|
|
1896
|
+
console.log(`DOCTOR_STATUS=${doctorKv.DOCTOR_STATUS || ""}`);
|
|
1897
|
+
console.log(`RUNTIME_START_STATUS=${runtimeStartStatus}`);
|
|
1898
|
+
if (runtimeStartReason) {
|
|
1899
|
+
console.log(`RUNTIME_START_REASON=${runtimeStartReason}`);
|
|
1900
|
+
}
|
|
1901
|
+
console.log(`LAUNCHD_INSTALL_STATUS=${launchdInstallStatus}`);
|
|
1902
|
+
if (launchdInstallReason) {
|
|
1903
|
+
console.log(`LAUNCHD_INSTALL_REASON=${launchdInstallReason}`);
|
|
1904
|
+
}
|
|
1905
|
+
if (runtimeStatusKv.RUNTIME_STATUS) {
|
|
1906
|
+
console.log(`RUNTIME_STATUS=${runtimeStatusKv.RUNTIME_STATUS}`);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
return 0;
|
|
1911
|
+
} catch (error) {
|
|
1912
|
+
if (options.json) {
|
|
1913
|
+
emitSetupJsonPayload({
|
|
1914
|
+
setupStatus: "error",
|
|
1915
|
+
setupMode: options.dryRun ? "dry-run" : "run",
|
|
1916
|
+
error: error && error.message ? error.message : String(error)
|
|
1917
|
+
});
|
|
1918
|
+
} else if (error && error.message) {
|
|
1919
|
+
console.error(error.message);
|
|
1920
|
+
}
|
|
1921
|
+
return 1;
|
|
1922
|
+
} finally {
|
|
1923
|
+
if (options.json) {
|
|
1924
|
+
console.log = originalConsoleLog;
|
|
1925
|
+
console.error = originalConsoleError;
|
|
1926
|
+
setupJsonOutputEnabled = false;
|
|
1927
|
+
}
|
|
1928
|
+
fs.rmSync(stage.stageRoot, { recursive: true, force: true });
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
async function main() {
|
|
1933
|
+
const command = process.argv[2] || "help";
|
|
1934
|
+
const forwardedArgs = process.argv.slice(3);
|
|
1935
|
+
|
|
1936
|
+
switch (command) {
|
|
1937
|
+
case "help":
|
|
1938
|
+
case "--help":
|
|
1939
|
+
case "-h":
|
|
1940
|
+
printHelp();
|
|
1941
|
+
return 0;
|
|
1942
|
+
case "version":
|
|
1943
|
+
case "--version":
|
|
1944
|
+
case "-v":
|
|
1945
|
+
console.log(packageJson.version);
|
|
1946
|
+
return 0;
|
|
1947
|
+
case "setup":
|
|
1948
|
+
return runSetupFlow(forwardedArgs);
|
|
1949
|
+
case "sync":
|
|
1950
|
+
case "install":
|
|
1951
|
+
return runCommand("tools/bin/sync-shared-agent-home.sh", forwardedArgs);
|
|
1952
|
+
case "init":
|
|
1953
|
+
return runCommand("tools/bin/project-init.sh", forwardedArgs);
|
|
1954
|
+
case "doctor":
|
|
1955
|
+
return runCommand("tools/bin/flow-runtime-doctor.sh", forwardedArgs);
|
|
1956
|
+
case "profile-smoke":
|
|
1957
|
+
return runCommand("tools/bin/profile-smoke.sh", forwardedArgs);
|
|
1958
|
+
case "dashboard":
|
|
1959
|
+
return runCommand("tools/bin/serve-dashboard.sh", forwardedArgs);
|
|
1960
|
+
case "launchd-install":
|
|
1961
|
+
return runCommand("tools/bin/install-project-launchd.sh", forwardedArgs);
|
|
1962
|
+
case "launchd-uninstall":
|
|
1963
|
+
return runCommand("tools/bin/uninstall-project-launchd.sh", forwardedArgs);
|
|
1964
|
+
case "runtime":
|
|
1965
|
+
return runCommand("tools/bin/project-runtimectl.sh", forwardedArgs);
|
|
1966
|
+
case "remove":
|
|
1967
|
+
return runCommand("tools/bin/project-remove.sh", forwardedArgs);
|
|
1968
|
+
case "smoke":
|
|
1969
|
+
return runCommand("tools/bin/test-smoke.sh", forwardedArgs);
|
|
1970
|
+
default:
|
|
1971
|
+
console.error(`unknown command: ${command}`);
|
|
1972
|
+
console.error("run `agent-control-plane help` for usage");
|
|
1973
|
+
return 64;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
main()
|
|
1978
|
+
.then((status) => {
|
|
1979
|
+
process.exit(status);
|
|
1980
|
+
})
|
|
1981
|
+
.catch((error) => {
|
|
1982
|
+
console.error(error && error.message ? error.message : String(error));
|
|
1983
|
+
process.exit(1);
|
|
1984
|
+
});
|