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,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude account loading, session/OAuth resolution.
|
|
3
|
+
* Depends on: lib/constants.js, lib/container.js, lib/paths.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { CLAUDE_CREDENTIALS_PATH, CLAUDE_MULTI_ACCOUNT_PATHS } from "./constants.js";
|
|
8
|
+
import { readMultiAccountContainer, writeMultiAccountContainer } from "./container.js";
|
|
9
|
+
import { getOpencodeAuthPath } from "./paths.js";
|
|
10
|
+
|
|
11
|
+
export function isClaudeSessionKey(value) {
|
|
12
|
+
return typeof value === "string" && value.startsWith("sk-ant-");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function findClaudeSessionKey(value) {
|
|
16
|
+
if (isClaudeSessionKey(value)) return value;
|
|
17
|
+
if (typeof value === "string") {
|
|
18
|
+
const match = value.match(/sk-ant-[a-z0-9_-]+/i);
|
|
19
|
+
if (match) return match[0];
|
|
20
|
+
}
|
|
21
|
+
if (!value || typeof value !== "object") return null;
|
|
22
|
+
|
|
23
|
+
const direct = value.sessionKey
|
|
24
|
+
?? value.session_key
|
|
25
|
+
?? value.token
|
|
26
|
+
?? value.sessionToken
|
|
27
|
+
?? value.accessToken
|
|
28
|
+
?? value.access_token
|
|
29
|
+
?? value.oauthAccessToken;
|
|
30
|
+
if (isClaudeSessionKey(direct)) return direct;
|
|
31
|
+
|
|
32
|
+
for (const child of Object.values(value)) {
|
|
33
|
+
const found = findClaudeSessionKey(child);
|
|
34
|
+
if (found) return found;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function normalizeClaudeAccount(raw, source) {
|
|
40
|
+
if (!raw || typeof raw !== "object") return null;
|
|
41
|
+
const label = raw.label ?? null;
|
|
42
|
+
const sessionKey = raw.sessionKey ?? raw.session_key ?? null;
|
|
43
|
+
const oauthToken = raw.oauthToken ?? raw.oauth_token ?? raw.accessToken ?? raw.access_token ?? null;
|
|
44
|
+
const cfClearance = raw.cfClearance ?? raw.cf_clearance ?? null;
|
|
45
|
+
const orgId = raw.orgId ?? raw.org_id ?? null;
|
|
46
|
+
const cookies = raw.cookies && typeof raw.cookies === "object" ? raw.cookies : null;
|
|
47
|
+
const oauthRefreshToken = raw.oauthRefreshToken ?? raw.oauth_refresh_token ?? null;
|
|
48
|
+
const oauthExpiresAt = raw.oauthExpiresAt ?? raw.oauth_expires_at ?? null;
|
|
49
|
+
const oauthScopes = raw.oauthScopes ?? raw.oauth_scopes ?? null;
|
|
50
|
+
return {
|
|
51
|
+
label,
|
|
52
|
+
sessionKey,
|
|
53
|
+
oauthToken,
|
|
54
|
+
cfClearance,
|
|
55
|
+
orgId,
|
|
56
|
+
cookies,
|
|
57
|
+
oauthRefreshToken,
|
|
58
|
+
oauthExpiresAt,
|
|
59
|
+
oauthScopes,
|
|
60
|
+
source,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isValidClaudeAccount(account) {
|
|
65
|
+
if (!account?.label) return false;
|
|
66
|
+
const sessionKey = account.sessionKey ?? findClaudeSessionKey(account.cookies);
|
|
67
|
+
const oauthToken = account.oauthToken ?? null;
|
|
68
|
+
return Boolean(sessionKey || oauthToken);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function loadClaudeAccountsFromEnv() {
|
|
72
|
+
const envAccounts = process.env.CLAUDE_ACCOUNTS;
|
|
73
|
+
if (!envAccounts) return [];
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(envAccounts);
|
|
77
|
+
const accounts = Array.isArray(parsed) ? parsed : parsed?.accounts ?? [];
|
|
78
|
+
return accounts
|
|
79
|
+
.map(a => normalizeClaudeAccount(a, "env"))
|
|
80
|
+
.filter(a => a && isValidClaudeAccount(a));
|
|
81
|
+
} catch {
|
|
82
|
+
console.error("Warning: CLAUDE_ACCOUNTS env var is not valid JSON");
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function loadClaudeAccountsFromFile(filePath) {
|
|
88
|
+
const container = readMultiAccountContainer(filePath);
|
|
89
|
+
if (!container.exists) return [];
|
|
90
|
+
return container.accounts
|
|
91
|
+
.map(a => normalizeClaudeAccount(a, filePath))
|
|
92
|
+
.filter(a => a && isValidClaudeAccount(a));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function loadClaudeAccounts() {
|
|
96
|
+
const all = [];
|
|
97
|
+
all.push(...loadClaudeAccountsFromEnv());
|
|
98
|
+
for (const path of CLAUDE_MULTI_ACCOUNT_PATHS) {
|
|
99
|
+
all.push(...loadClaudeAccountsFromFile(path));
|
|
100
|
+
}
|
|
101
|
+
return all;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the multi-account file that stores activeLabel for Claude.
|
|
106
|
+
* @returns {string}
|
|
107
|
+
*/
|
|
108
|
+
export function resolveClaudeActiveStorePath() {
|
|
109
|
+
const firstPath = CLAUDE_MULTI_ACCOUNT_PATHS[0];
|
|
110
|
+
if (firstPath && existsSync(firstPath)) return firstPath;
|
|
111
|
+
return firstPath;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Read the active-label store container for Claude.
|
|
116
|
+
* @returns {{ path: string, container: ReturnType<typeof readMultiAccountContainer> }}
|
|
117
|
+
*/
|
|
118
|
+
export function readClaudeActiveStoreContainer() {
|
|
119
|
+
const path = resolveClaudeActiveStorePath();
|
|
120
|
+
const container = readMultiAccountContainer(path);
|
|
121
|
+
return { path, container };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get the activeLabel stored for Claude (if any).
|
|
126
|
+
* @returns {{ activeLabel: string | null, path: string, schemaVersion: number }}
|
|
127
|
+
*/
|
|
128
|
+
export function getClaudeActiveLabelInfo() {
|
|
129
|
+
const { path, container } = readClaudeActiveStoreContainer();
|
|
130
|
+
return {
|
|
131
|
+
activeLabel: container.activeLabel ?? null,
|
|
132
|
+
path,
|
|
133
|
+
schemaVersion: container.schemaVersion ?? 0,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function saveClaudeAccounts(accounts) {
|
|
138
|
+
const targetPath = resolveClaudeActiveStorePath();
|
|
139
|
+
const container = readMultiAccountContainer(targetPath);
|
|
140
|
+
const filtered = accounts.filter(account => !(account?.source && account.source.startsWith("env")));
|
|
141
|
+
const sanitized = filtered.map(account => {
|
|
142
|
+
if (!account || typeof account !== "object") return account;
|
|
143
|
+
const { source, ...rest } = account;
|
|
144
|
+
return rest;
|
|
145
|
+
});
|
|
146
|
+
const result = writeMultiAccountContainer(targetPath, container, sanitized, {}, { mode: 0o600 });
|
|
147
|
+
return result.path;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function loadClaudeSessionFromCredentials() {
|
|
151
|
+
const credentialsPath = process.env.CLAUDE_CREDENTIALS_PATH || CLAUDE_CREDENTIALS_PATH;
|
|
152
|
+
if (!existsSync(credentialsPath)) {
|
|
153
|
+
return {
|
|
154
|
+
sessionKey: null,
|
|
155
|
+
source: credentialsPath,
|
|
156
|
+
error: `Claude credentials not found at ${credentialsPath}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const raw = readFileSync(credentialsPath, "utf-8");
|
|
162
|
+
const parsed = JSON.parse(raw);
|
|
163
|
+
const sessionKey = findClaudeSessionKey(parsed);
|
|
164
|
+
if (!sessionKey) {
|
|
165
|
+
return {
|
|
166
|
+
sessionKey: null,
|
|
167
|
+
source: credentialsPath,
|
|
168
|
+
error: "No Claude sessionKey found in credentials file",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return { sessionKey, source: credentialsPath };
|
|
172
|
+
} catch (err) {
|
|
173
|
+
return {
|
|
174
|
+
sessionKey: null,
|
|
175
|
+
source: credentialsPath,
|
|
176
|
+
error: `Failed to read Claude credentials: ${err?.message ?? String(err)}`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function loadClaudeOAuthToken() {
|
|
182
|
+
const credentialsPath = process.env.CLAUDE_CREDENTIALS_PATH || CLAUDE_CREDENTIALS_PATH;
|
|
183
|
+
if (!existsSync(credentialsPath)) {
|
|
184
|
+
return { token: null, source: credentialsPath, error: `Claude credentials not found at ${credentialsPath}` };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const raw = readFileSync(credentialsPath, "utf-8");
|
|
189
|
+
const parsed = JSON.parse(raw);
|
|
190
|
+
const token =
|
|
191
|
+
parsed?.claudeAiOauth?.accessToken
|
|
192
|
+
?? parsed?.claude_ai_oauth?.accessToken
|
|
193
|
+
?? parsed?.accessToken
|
|
194
|
+
?? parsed?.access_token
|
|
195
|
+
?? null;
|
|
196
|
+
if (!token) {
|
|
197
|
+
return { token: null, source: credentialsPath, error: "No Claude OAuth accessToken found" };
|
|
198
|
+
}
|
|
199
|
+
return { token, source: credentialsPath };
|
|
200
|
+
} catch (err) {
|
|
201
|
+
return {
|
|
202
|
+
token: null,
|
|
203
|
+
source: credentialsPath,
|
|
204
|
+
error: `Failed to read Claude credentials: ${err?.message ?? String(err)}`,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Find a Claude account by label from supported sources
|
|
211
|
+
* @param {string} label
|
|
212
|
+
* @returns {object | null}
|
|
213
|
+
*/
|
|
214
|
+
export function findClaudeAccountByLabel(label) {
|
|
215
|
+
const accounts = loadClaudeAccounts();
|
|
216
|
+
return accounts.find(account => account.label === label) ?? null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get all Claude labels from supported sources
|
|
221
|
+
* @returns {string[]}
|
|
222
|
+
*/
|
|
223
|
+
export function getClaudeLabels() {
|
|
224
|
+
const accounts = loadClaudeAccounts();
|
|
225
|
+
return [...new Set(accounts.map(account => account.label))];
|
|
226
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude OAuth browser flow.
|
|
3
|
+
* Depends on: lib/constants.js, lib/oauth.js, lib/claude-tokens.js, lib/prompts.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
CLAUDE_OAUTH_AUTHORIZE_URL,
|
|
8
|
+
CLAUDE_OAUTH_TOKEN_URL,
|
|
9
|
+
CLAUDE_OAUTH_REDIRECT_URI,
|
|
10
|
+
CLAUDE_OAUTH_CLIENT_ID,
|
|
11
|
+
CLAUDE_OAUTH_SCOPES,
|
|
12
|
+
CLAUDE_OAUTH_REFRESH_BUFFER_MS,
|
|
13
|
+
OAUTH_TIMEOUT_MS,
|
|
14
|
+
} from "./constants.js";
|
|
15
|
+
import { generatePKCE, generateState, openBrowser } from "./oauth.js";
|
|
16
|
+
import { refreshClaudeToken, persistClaudeOAuthTokens } from "./claude-tokens.js";
|
|
17
|
+
import { promptInput } from "./prompts.js";
|
|
18
|
+
|
|
19
|
+
export function buildClaudeAuthUrl(codeChallenge, state) {
|
|
20
|
+
const params = new URLSearchParams({
|
|
21
|
+
response_type: "code",
|
|
22
|
+
client_id: CLAUDE_OAUTH_CLIENT_ID,
|
|
23
|
+
redirect_uri: CLAUDE_OAUTH_REDIRECT_URI,
|
|
24
|
+
scope: CLAUDE_OAUTH_SCOPES,
|
|
25
|
+
code_challenge: codeChallenge,
|
|
26
|
+
code_challenge_method: "S256",
|
|
27
|
+
state: state,
|
|
28
|
+
code: "true", // Display code in browser for user to copy
|
|
29
|
+
});
|
|
30
|
+
// Use %20 instead of + for spaces
|
|
31
|
+
return `${CLAUDE_OAUTH_AUTHORIZE_URL}?${params.toString().replace(/\+/g, "%20")}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse user input containing Claude OAuth code and state
|
|
36
|
+
* Accepts formats:
|
|
37
|
+
* - "code#state" (code with state suffix)
|
|
38
|
+
* - "code" (code only, state validation skipped)
|
|
39
|
+
* - Full callback URL: https://console.anthropic.com/oauth/code/callback?code=...&state=...
|
|
40
|
+
* @param {string} input - User input string
|
|
41
|
+
* @param {string} expectedState - Expected state for CSRF validation
|
|
42
|
+
* @returns {{ code: string, state: string | null }} Parsed code and optional state
|
|
43
|
+
*/
|
|
44
|
+
export function parseClaudeCodeState(input) {
|
|
45
|
+
const trimmed = (input ?? "").trim();
|
|
46
|
+
if (!trimmed) {
|
|
47
|
+
return { code: null, state: null };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check if it's a full callback URL
|
|
51
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
52
|
+
try {
|
|
53
|
+
const url = new URL(trimmed);
|
|
54
|
+
const code = url.searchParams.get("code");
|
|
55
|
+
const state = url.searchParams.get("state");
|
|
56
|
+
return { code: code || null, state: state || null };
|
|
57
|
+
} catch {
|
|
58
|
+
return { code: null, state: null };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check for code#state format
|
|
63
|
+
if (trimmed.includes("#")) {
|
|
64
|
+
const [code, state] = trimmed.split("#", 2);
|
|
65
|
+
return { code: code || null, state: state || null };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Plain code only
|
|
69
|
+
return { code: trimmed, state: null };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Exchange Claude authorization code for tokens
|
|
74
|
+
* @param {string} code - Authorization code from callback
|
|
75
|
+
* @param {string} codeVerifier - PKCE code verifier
|
|
76
|
+
* @param {string} state - OAuth state for CSRF validation
|
|
77
|
+
* @returns {Promise<{ accessToken: string, refreshToken: string, expiresIn: number }>}
|
|
78
|
+
*/
|
|
79
|
+
export async function exchangeClaudeCodeForTokens(code, codeVerifier, state) {
|
|
80
|
+
const controller = new AbortController();
|
|
81
|
+
const timeout = setTimeout(() => controller.abort(), OAUTH_TIMEOUT_MS);
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const body = {
|
|
85
|
+
grant_type: "authorization_code",
|
|
86
|
+
code: code,
|
|
87
|
+
state: state,
|
|
88
|
+
redirect_uri: CLAUDE_OAUTH_REDIRECT_URI,
|
|
89
|
+
client_id: CLAUDE_OAUTH_CLIENT_ID,
|
|
90
|
+
code_verifier: codeVerifier,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const response = await fetch(CLAUDE_OAUTH_TOKEN_URL, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: {
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify(body),
|
|
99
|
+
signal: controller.signal,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
const text = await response.text();
|
|
104
|
+
throw new Error(`Token exchange failed: ${response.status} ${text}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const data = await response.json();
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
accessToken: data.access_token,
|
|
111
|
+
refreshToken: data.refresh_token || null,
|
|
112
|
+
expiresIn: data.expires_in || 3600,
|
|
113
|
+
};
|
|
114
|
+
} finally {
|
|
115
|
+
clearTimeout(timeout);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// refreshClaudeToken, isClaudeOauthTokenExpiring, resolveClaudeOauthAccountFields,
|
|
120
|
+
// ensureFreshClaudeOAuthToken are in ./claude-tokens.js
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Run the Claude OAuth browser flow to get tokens
|
|
124
|
+
* @param {{ noBrowser: boolean }} flags - CLI flags
|
|
125
|
+
* @returns {Promise<{ accessToken: string, refreshToken: string, expiresAt: number, scopes: string }>}
|
|
126
|
+
*/
|
|
127
|
+
export async function handleClaudeOAuthFlow(flags) {
|
|
128
|
+
// 1. Generate PKCE code verifier and challenge
|
|
129
|
+
const { verifier, challenge } = generatePKCE();
|
|
130
|
+
|
|
131
|
+
// 2. Generate random state for CSRF protection
|
|
132
|
+
const state = generateState();
|
|
133
|
+
|
|
134
|
+
// 3. Build authorization URL
|
|
135
|
+
const authUrl = buildClaudeAuthUrl(challenge, state);
|
|
136
|
+
|
|
137
|
+
// 4. Print instructions
|
|
138
|
+
console.log("\nStarting Claude OAuth authentication...\n");
|
|
139
|
+
|
|
140
|
+
// 5. Open browser or print URL
|
|
141
|
+
openBrowser(authUrl, { noBrowser: flags.noBrowser });
|
|
142
|
+
|
|
143
|
+
// 6. Prompt user to paste code
|
|
144
|
+
console.log("After authenticating in the browser, you will see a code.");
|
|
145
|
+
console.log("Copy the entire code (including any #state portion) and paste it below.\n");
|
|
146
|
+
|
|
147
|
+
const input = await promptInput("Paste code#state here: ");
|
|
148
|
+
const { code, state: returnedState } = parseClaudeCodeState(input);
|
|
149
|
+
|
|
150
|
+
if (!code) {
|
|
151
|
+
throw new Error("No authorization code provided. Authentication cancelled.");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 7. Validate state if provided (CSRF protection)
|
|
155
|
+
if (returnedState && returnedState !== state) {
|
|
156
|
+
throw new Error("State mismatch. Possible CSRF attack. Please try again.");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 8. Exchange code for tokens
|
|
160
|
+
console.log("\nExchanging code for tokens...");
|
|
161
|
+
const stateToSend = returnedState ?? state;
|
|
162
|
+
const tokens = await exchangeClaudeCodeForTokens(code, verifier, stateToSend);
|
|
163
|
+
|
|
164
|
+
// 9. Calculate expiry timestamp
|
|
165
|
+
const expiresAt = Date.now() + (tokens.expiresIn * 1000);
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
accessToken: tokens.accessToken,
|
|
169
|
+
refreshToken: tokens.refreshToken,
|
|
170
|
+
expiresAt: expiresAt,
|
|
171
|
+
scopes: CLAUDE_OAUTH_SCOPES,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|