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.
Files changed (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +589 -0
  3. package/SKILL.md +149 -0
  4. package/assets/workflow-catalog.json +57 -0
  5. package/bin/audit-issue-routing.sh +74 -0
  6. package/bin/issue-resource-class.sh +58 -0
  7. package/bin/label-follow-up-issues.sh +114 -0
  8. package/bin/pr-risk.sh +532 -0
  9. package/bin/sync-pr-labels.sh +112 -0
  10. package/hooks/heartbeat-hooks.sh +573 -0
  11. package/hooks/issue-reconcile-hooks.sh +217 -0
  12. package/hooks/pr-reconcile-hooks.sh +225 -0
  13. package/npm/bin/agent-control-plane.js +1984 -0
  14. package/npm/public-bin/agent-control-plane +3 -0
  15. package/package.json +61 -0
  16. package/tools/bin/agent-cleanup-worktree +247 -0
  17. package/tools/bin/agent-github-update-labels +66 -0
  18. package/tools/bin/agent-init-worktree +216 -0
  19. package/tools/bin/agent-project-archive-run +52 -0
  20. package/tools/bin/agent-project-capture-worker +46 -0
  21. package/tools/bin/agent-project-catch-up-merged-prs +137 -0
  22. package/tools/bin/agent-project-cleanup-session +244 -0
  23. package/tools/bin/agent-project-detached-launch +107 -0
  24. package/tools/bin/agent-project-heartbeat-loop +2347 -0
  25. package/tools/bin/agent-project-open-issue-worktree +89 -0
  26. package/tools/bin/agent-project-open-pr-worktree +80 -0
  27. package/tools/bin/agent-project-publish-issue-pr +349 -0
  28. package/tools/bin/agent-project-reconcile-issue-session +1128 -0
  29. package/tools/bin/agent-project-reconcile-pr-session +1005 -0
  30. package/tools/bin/agent-project-retry-state +147 -0
  31. package/tools/bin/agent-project-run-claude-session +657 -0
  32. package/tools/bin/agent-project-run-codex-resilient +718 -0
  33. package/tools/bin/agent-project-run-codex-session +316 -0
  34. package/tools/bin/agent-project-run-kilo-session +27 -0
  35. package/tools/bin/agent-project-run-openclaw-session +984 -0
  36. package/tools/bin/agent-project-run-opencode-session +27 -0
  37. package/tools/bin/agent-project-sync-anchor-repo +128 -0
  38. package/tools/bin/agent-project-worker-status +143 -0
  39. package/tools/bin/audit-agent-worktrees.sh +310 -0
  40. package/tools/bin/audit-issue-routing.sh +11 -0
  41. package/tools/bin/audit-retained-layout.sh +58 -0
  42. package/tools/bin/audit-retained-overlap.sh +135 -0
  43. package/tools/bin/audit-retained-worktrees.sh +228 -0
  44. package/tools/bin/branch-verification-guard.sh +351 -0
  45. package/tools/bin/capture-worker.sh +18 -0
  46. package/tools/bin/check-skill-contracts.sh +324 -0
  47. package/tools/bin/cleanup-worktree.sh +44 -0
  48. package/tools/bin/codex-quota +31 -0
  49. package/tools/bin/create-follow-up-issue.sh +114 -0
  50. package/tools/bin/dashboard-launchd-bootstrap.sh +38 -0
  51. package/tools/bin/flow-config-lib.sh +2127 -0
  52. package/tools/bin/flow-resident-worker-lib.sh +683 -0
  53. package/tools/bin/flow-runtime-doctor.sh +97 -0
  54. package/tools/bin/flow-shell-lib.sh +266 -0
  55. package/tools/bin/heartbeat-recovery-preflight.sh +106 -0
  56. package/tools/bin/heartbeat-safe-auto.sh +551 -0
  57. package/tools/bin/install-dashboard-launchd.sh +152 -0
  58. package/tools/bin/install-project-launchd.sh +219 -0
  59. package/tools/bin/issue-publish-scope-guard.sh +242 -0
  60. package/tools/bin/issue-requires-local-workspace-install.sh +31 -0
  61. package/tools/bin/issue-resource-class.sh +12 -0
  62. package/tools/bin/kick-scheduler.sh +75 -0
  63. package/tools/bin/label-follow-up-issues.sh +14 -0
  64. package/tools/bin/new-pr-worktree.sh +50 -0
  65. package/tools/bin/new-worktree.sh +49 -0
  66. package/tools/bin/pr-risk.sh +12 -0
  67. package/tools/bin/prepare-worktree.sh +140 -0
  68. package/tools/bin/profile-activate.sh +109 -0
  69. package/tools/bin/profile-adopt.sh +219 -0
  70. package/tools/bin/profile-smoke.sh +461 -0
  71. package/tools/bin/project-init.sh +189 -0
  72. package/tools/bin/project-launchd-bootstrap.sh +54 -0
  73. package/tools/bin/project-remove.sh +155 -0
  74. package/tools/bin/project-runtime-supervisor.sh +56 -0
  75. package/tools/bin/project-runtimectl.sh +586 -0
  76. package/tools/bin/provider-cooldown-state.sh +166 -0
  77. package/tools/bin/publish-issue-worker.sh +31 -0
  78. package/tools/bin/reconcile-issue-worker.sh +34 -0
  79. package/tools/bin/reconcile-pr-worker.sh +34 -0
  80. package/tools/bin/record-verification.sh +71 -0
  81. package/tools/bin/render-architecture-infographics.sh +110 -0
  82. package/tools/bin/render-dashboard-demo-media.sh +333 -0
  83. package/tools/bin/render-dashboard-snapshot.py +16 -0
  84. package/tools/bin/render-flow-config.sh +86 -0
  85. package/tools/bin/retry-state.sh +31 -0
  86. package/tools/bin/reuse-issue-worktree.sh +75 -0
  87. package/tools/bin/run-codex-bypass.sh +3 -0
  88. package/tools/bin/run-codex-safe.sh +3 -0
  89. package/tools/bin/run-codex-task.sh +231 -0
  90. package/tools/bin/scaffold-profile.sh +374 -0
  91. package/tools/bin/serve-dashboard.sh +5 -0
  92. package/tools/bin/split-retained-slice.sh +124 -0
  93. package/tools/bin/start-issue-worker.sh +796 -0
  94. package/tools/bin/start-pr-fix-worker.sh +458 -0
  95. package/tools/bin/start-pr-merge-repair-worker.sh +8 -0
  96. package/tools/bin/start-pr-review-worker.sh +227 -0
  97. package/tools/bin/start-resident-issue-loop.sh +908 -0
  98. package/tools/bin/sync-agent-repo.sh +52 -0
  99. package/tools/bin/sync-dependency-baseline.sh +247 -0
  100. package/tools/bin/sync-pr-labels.sh +12 -0
  101. package/tools/bin/sync-recurring-issue-checklist.sh +274 -0
  102. package/tools/bin/sync-shared-agent-home.sh +214 -0
  103. package/tools/bin/sync-vscode-workspace.sh +157 -0
  104. package/tools/bin/test-smoke.sh +63 -0
  105. package/tools/bin/uninstall-project-launchd.sh +55 -0
  106. package/tools/bin/update-github-labels.sh +14 -0
  107. package/tools/bin/worker-status.sh +19 -0
  108. package/tools/bin/workflow-catalog.sh +77 -0
  109. package/tools/dashboard/app.js +286 -0
  110. package/tools/dashboard/dashboard_snapshot.py +466 -0
  111. package/tools/dashboard/index.html +41 -0
  112. package/tools/dashboard/server.py +64 -0
  113. package/tools/dashboard/styles.css +351 -0
  114. package/tools/templates/issue-prompt-template.md +109 -0
  115. package/tools/templates/pr-fix-template.md +120 -0
  116. package/tools/templates/pr-merge-repair-template.md +91 -0
  117. package/tools/templates/pr-review-template.md +62 -0
  118. package/tools/templates/scheduled-issue-prompt-template.md +62 -0
  119. package/tools/tests/test-agent-control-plane-npm-cli.sh +279 -0
  120. package/tools/tests/test-agent-github-update-labels-falls-back-to-repository-id.sh +56 -0
  121. package/tools/tests/test-agent-project-claude-session-wrapper-clears-stale-sandbox-artifacts.sh +89 -0
  122. package/tools/tests/test-agent-project-claude-session-wrapper-does-not-retry-provider-quota.sh +82 -0
  123. package/tools/tests/test-agent-project-claude-session-wrapper-retries-transient-failures.sh +90 -0
  124. package/tools/tests/test-agent-project-claude-session-wrapper-times-out.sh +73 -0
  125. package/tools/tests/test-agent-project-claude-session-wrapper.sh +103 -0
  126. package/tools/tests/test-agent-project-cleanup-session-orphan-fallback.sh +90 -0
  127. package/tools/tests/test-agent-project-cleanup-session-skip-worktree-cleanup.sh +90 -0
  128. package/tools/tests/test-agent-project-codex-live-thread-persist.sh +76 -0
  129. package/tools/tests/test-agent-project-codex-recovery.sh +731 -0
  130. package/tools/tests/test-agent-project-codex-session-wrapper-clears-stale-sandbox-artifacts.sh +105 -0
  131. package/tools/tests/test-agent-project-codex-session-wrapper.sh +97 -0
  132. package/tools/tests/test-agent-project-open-pr-worktree-config-prefix.sh +81 -0
  133. package/tools/tests/test-agent-project-openclaw-session-wrapper-clears-stale-sandbox-artifacts.sh +109 -0
  134. package/tools/tests/test-agent-project-openclaw-session-wrapper-infers-blocked-result-contract.sh +89 -0
  135. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-literal-env-artifacts.sh +113 -0
  136. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-version-mismatch.sh +135 -0
  137. package/tools/tests/test-agent-project-openclaw-session-wrapper-resident.sh +179 -0
  138. package/tools/tests/test-agent-project-openclaw-session-wrapper-reuses-existing-agent-after-add-race.sh +119 -0
  139. package/tools/tests/test-agent-project-openclaw-session-wrapper-terminates-rate-limit-hang.sh +91 -0
  140. package/tools/tests/test-agent-project-openclaw-session-wrapper.sh +117 -0
  141. package/tools/tests/test-agent-project-publish-issue-pr-prunes-stale-worktree-entry.sh +148 -0
  142. package/tools/tests/test-agent-project-publish-issue-pr-reads-archived-session.sh +146 -0
  143. package/tools/tests/test-agent-project-publish-issue-pr-recovers-final-head.sh +145 -0
  144. package/tools/tests/test-agent-project-publish-issue-pr-reuses-existing-worktree.sh +147 -0
  145. package/tools/tests/test-agent-project-reconcile-failure-reason.sh +456 -0
  146. package/tools/tests/test-agent-project-reconcile-issue-archived-session-fallback.sh +96 -0
  147. package/tools/tests/test-agent-project-reconcile-issue-before-blocked.sh +90 -0
  148. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery-uses-recovered-worktree.sh +212 -0
  149. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery.sh +207 -0
  150. package/tools/tests/test-agent-project-reconcile-issue-provider-quota-schedules-provider-cooldown.sh +101 -0
  151. package/tools/tests/test-agent-project-reconcile-issue-session-backfills-lane-metadata-from-worker-key.sh +113 -0
  152. package/tools/tests/test-agent-project-reconcile-issue-session-clears-stale-failed-summary.sh +117 -0
  153. package/tools/tests/test-agent-project-reconcile-issue-session-initializes-shared-agent-home.sh +55 -0
  154. package/tools/tests/test-agent-project-reconcile-issue-session-normalizes-runner-state.sh +125 -0
  155. package/tools/tests/test-agent-project-reconcile-issue-session-records-invalid-contract-summary.sh +118 -0
  156. package/tools/tests/test-agent-project-reconcile-issue-session-skips-duplicate-blocked-comment.sh +144 -0
  157. package/tools/tests/test-agent-project-reconcile-issue-session-standardizes-no-commits-blocker.sh +145 -0
  158. package/tools/tests/test-agent-project-reconcile-issue-session-synthesizes-blocked-comment.sh +139 -0
  159. package/tools/tests/test-agent-project-reconcile-pr-blocked-host-recovery.sh +242 -0
  160. package/tools/tests/test-agent-project-reconcile-pr-guard-blocked-no-commit.sh +142 -0
  161. package/tools/tests/test-agent-project-reconcile-pr-provider-quota-schedules-provider-cooldown.sh +106 -0
  162. package/tools/tests/test-agent-project-reconcile-pr-session-initializes-shared-agent-home.sh +66 -0
  163. package/tools/tests/test-agent-project-reconcile-pr-updated-branch-noop.sh +129 -0
  164. package/tools/tests/test-audit-agent-worktrees-active-launch-skips-git-inspection.sh +69 -0
  165. package/tools/tests/test-audit-agent-worktrees-broken-worktree.sh +43 -0
  166. package/tools/tests/test-audit-agent-worktrees-pending-launch-owner.sh +46 -0
  167. package/tools/tests/test-audit-agent-worktrees-unreconciled-owner.sh +79 -0
  168. package/tools/tests/test-audit-issue-routing-managed-branch-globs.sh +56 -0
  169. package/tools/tests/test-branch-verification-guard-generated-artifacts.sh +72 -0
  170. package/tools/tests/test-branch-verification-guard-targeted-coverage.sh +125 -0
  171. package/tools/tests/test-codex-quota-manager-failure-driven-rotation.sh +178 -0
  172. package/tools/tests/test-codex-quota-wrapper.sh +37 -0
  173. package/tools/tests/test-contribution-docs.sh +18 -0
  174. package/tools/tests/test-control-plane-dashboard-runtime-smoke.sh +343 -0
  175. package/tools/tests/test-create-follow-up-issue.sh +73 -0
  176. package/tools/tests/test-dashboard-launchd-bootstrap.sh +55 -0
  177. package/tools/tests/test-flow-export-execution-env-exports-repo-id.sh +30 -0
  178. package/tools/tests/test-flow-export-github-cli-auth-env-prefers-git-credential.sh +48 -0
  179. package/tools/tests/test-flow-github-api-repo-fallback-preserves-input.sh +85 -0
  180. package/tools/tests/test-flow-github-api-repo-prefers-explicit-repository-id.sh +60 -0
  181. package/tools/tests/test-flow-github-issue-list-falls-back-to-repository-id.sh +64 -0
  182. package/tools/tests/test-flow-github-pr-list-falls-back-to-repository-id.sh +77 -0
  183. package/tools/tests/test-flow-resident-can-reuse-does-not-leak-metadata.sh +52 -0
  184. package/tools/tests/test-flow-resident-reap-stale-controllers.sh +63 -0
  185. package/tools/tests/test-flow-resolve-codex-quota-tools.sh +104 -0
  186. package/tools/tests/test-flow-runtime-doctor-profile-selection.sh +27 -0
  187. package/tools/tests/test-heartbeat-codex-pr-linked-issue-exclusion.sh +79 -0
  188. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-idle-controller.sh +115 -0
  189. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-live-lane-controller.sh +117 -0
  190. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-claude.sh +96 -0
  191. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-codex.sh +96 -0
  192. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop.sh +96 -0
  193. package/tools/tests/test-heartbeat-loop-auth-wait-does-not-consume-capacity.sh +170 -0
  194. package/tools/tests/test-heartbeat-loop-blocked-recovery-lane.sh +201 -0
  195. package/tools/tests/test-heartbeat-loop-blocked-recovery-vs-pr-reservation.sh +201 -0
  196. package/tools/tests/test-heartbeat-loop-idle-resident-controller-does-not-block-launches.sh +160 -0
  197. package/tools/tests/test-heartbeat-loop-pr-launch-dedup.sh +133 -0
  198. package/tools/tests/test-heartbeat-loop-provider-cooldown-suppresses-launches.sh +157 -0
  199. package/tools/tests/test-heartbeat-loop-reaps-stale-resident-controller.sh +181 -0
  200. package/tools/tests/test-heartbeat-loop-waiting-provider-resident-controller-does-not-block-launches.sh +160 -0
  201. package/tools/tests/test-heartbeat-ready-issues-blocked-recovery.sh +134 -0
  202. package/tools/tests/test-heartbeat-safe-auto-dynamic-concurrency.sh +162 -0
  203. package/tools/tests/test-heartbeat-safe-auto-no-tmux-sessions.sh +136 -0
  204. package/tools/tests/test-heartbeat-safe-auto-openclaw-skips-codex-quota.sh +139 -0
  205. package/tools/tests/test-heartbeat-safe-auto-quota-health-signal.sh +119 -0
  206. package/tools/tests/test-heartbeat-safe-auto-stale-shared-loop-pid-does-not-skip.sh +140 -0
  207. package/tools/tests/test-heartbeat-safe-auto-static-capacity-without-quota-cache.sh +142 -0
  208. package/tools/tests/test-heartbeat-safe-auto-zero-healthy-pools.sh +141 -0
  209. package/tools/tests/test-heartbeat-sync-issue-labels-empty-schedule.sh +65 -0
  210. package/tools/tests/test-heartbeat-sync-open-agent-prs-terminal-clears-running.sh +179 -0
  211. package/tools/tests/test-install-dashboard-launchd.sh +78 -0
  212. package/tools/tests/test-install-project-launchd-adds-tool-paths.sh +87 -0
  213. package/tools/tests/test-install-project-launchd.sh +110 -0
  214. package/tools/tests/test-issue-local-workspace-install-policy.sh +81 -0
  215. package/tools/tests/test-issue-publish-scope-guard-docs-signal.sh +70 -0
  216. package/tools/tests/test-issue-reconcile-hooks-success-clears-blocked.sh +36 -0
  217. package/tools/tests/test-kick-scheduler-requires-explicit-profile.sh +47 -0
  218. package/tools/tests/test-label-follow-up-issues-falls-back-to-repository-id.sh +132 -0
  219. package/tools/tests/test-manual-operator-entrypoints-require-explicit-profile.sh +64 -0
  220. package/tools/tests/test-package-funding-metadata.sh +21 -0
  221. package/tools/tests/test-package-public-metadata.sh +62 -0
  222. package/tools/tests/test-placeholder-worker-adapters.sh +38 -0
  223. package/tools/tests/test-pr-reconcile-hooks-refreshes-recurring-issue-checklist.sh +110 -0
  224. package/tools/tests/test-pr-risk-cohesive-mobile-locale-scope.sh +70 -0
  225. package/tools/tests/test-pr-risk-fix-label-semantics.sh +114 -0
  226. package/tools/tests/test-pr-risk-local-first-no-checks.sh +70 -0
  227. package/tools/tests/test-prepare-worktree-simple-repo-baseline.sh +67 -0
  228. package/tools/tests/test-profile-activate.sh +33 -0
  229. package/tools/tests/test-profile-adopt-allow-missing-repo.sh +68 -0
  230. package/tools/tests/test-profile-adopt-skip-workspace-sync-missing-file.sh +61 -0
  231. package/tools/tests/test-profile-adopt-syncs-anchor-and-workspace.sh +90 -0
  232. package/tools/tests/test-profile-smoke-collision.sh +44 -0
  233. package/tools/tests/test-profile-smoke-invalid-claude-config.sh +31 -0
  234. package/tools/tests/test-profile-smoke-invalid-provider-pool.sh +68 -0
  235. package/tools/tests/test-profile-smoke-repo-slug-mismatch.sh +36 -0
  236. package/tools/tests/test-profile-smoke.sh +45 -0
  237. package/tools/tests/test-project-init-force-and-skip-sync.sh +61 -0
  238. package/tools/tests/test-project-init-repo-slug-mismatch.sh +29 -0
  239. package/tools/tests/test-project-init.sh +66 -0
  240. package/tools/tests/test-project-launchd-bootstrap.sh +66 -0
  241. package/tools/tests/test-project-remove.sh +150 -0
  242. package/tools/tests/test-project-runtime-supervisor.sh +47 -0
  243. package/tools/tests/test-project-runtimectl-launchd.sh +115 -0
  244. package/tools/tests/test-project-runtimectl-missing-profile.sh +54 -0
  245. package/tools/tests/test-project-runtimectl-start-falls-back-to-bootstrap.sh +108 -0
  246. package/tools/tests/test-project-runtimectl-status-reports-supervisor-as-heartbeat-parent.sh +95 -0
  247. package/tools/tests/test-project-runtimectl-status-supervisor-running.sh +59 -0
  248. package/tools/tests/test-project-runtimectl-stop-cancels-pending-kick.sh +85 -0
  249. package/tools/tests/test-project-runtimectl-stop-clears-running-labels.sh +78 -0
  250. package/tools/tests/test-project-runtimectl.sh +212 -0
  251. package/tools/tests/test-provider-cooldown-state-prefers-runtime-worker-context.sh +39 -0
  252. package/tools/tests/test-provider-cooldown-state.sh +59 -0
  253. package/tools/tests/test-public-repo-docs.sh +159 -0
  254. package/tools/tests/test-reconcile-pr-worker-acp-config-routing.sh +75 -0
  255. package/tools/tests/test-render-dashboard-snapshot.sh +149 -0
  256. package/tools/tests/test-render-flow-config-demo-profile.sh +36 -0
  257. package/tools/tests/test-render-flow-config-provider-pool-fallback.sh +81 -0
  258. package/tools/tests/test-render-flow-config.sh +52 -0
  259. package/tools/tests/test-run-codex-task-claude-routing.sh +125 -0
  260. package/tools/tests/test-run-codex-task-codex-resident-routing.sh +108 -0
  261. package/tools/tests/test-run-codex-task-kilo-routing.sh +98 -0
  262. package/tools/tests/test-run-codex-task-openclaw-resident-routing.sh +117 -0
  263. package/tools/tests/test-run-codex-task-openclaw-routing.sh +113 -0
  264. package/tools/tests/test-run-codex-task-opencode-routing.sh +98 -0
  265. package/tools/tests/test-run-codex-task-provider-pool-fallback-routing.sh +146 -0
  266. package/tools/tests/test-scaffold-profile.sh +108 -0
  267. package/tools/tests/test-serve-dashboard.sh +93 -0
  268. package/tools/tests/test-start-issue-worker-blocked-context.sh +129 -0
  269. package/tools/tests/test-start-issue-worker-blocks-complete-recurring-checklist.sh +189 -0
  270. package/tools/tests/test-start-issue-worker-local-install-routing.sh +157 -0
  271. package/tools/tests/test-start-issue-worker-profile-template-routing.sh +149 -0
  272. package/tools/tests/test-start-issue-worker-recurring-resident-reuse-codex.sh +212 -0
  273. package/tools/tests/test-start-issue-worker-recurring-resident-reuse.sh +219 -0
  274. package/tools/tests/test-start-issue-worker-renders-verification-snippet.sh +155 -0
  275. package/tools/tests/test-start-issue-worker-resident-reuse-falls-back-to-new-worktree.sh +199 -0
  276. package/tools/tests/test-start-pr-fix-worker-host-blocker-context.sh +275 -0
  277. package/tools/tests/test-start-resident-issue-loop-adopts-next-recurring-issue.sh +185 -0
  278. package/tools/tests/test-start-resident-issue-loop-clears-pending-while-waiting-due.sh +152 -0
  279. package/tools/tests/test-start-resident-issue-loop-consumes-queued-lease.sh +186 -0
  280. package/tools/tests/test-start-resident-issue-loop-fails-over-provider-pool.sh +212 -0
  281. package/tools/tests/test-start-resident-issue-loop-immediate-cycles.sh +148 -0
  282. package/tools/tests/test-start-resident-issue-loop-waits-for-provider.sh +194 -0
  283. package/tools/tests/test-start-resident-issue-loop-waits-for-terminal-reconcile-status.sh +198 -0
  284. package/tools/tests/test-start-resident-issue-loop-yields-to-live-lane-controller.sh +145 -0
  285. package/tools/tests/test-sync-pr-labels-fix-lane-uses-repair-queued.sh +67 -0
  286. package/tools/tests/test-sync-recurring-issue-checklist-backfills-workflow-complete-blocker.sh +70 -0
  287. package/tools/tests/test-sync-recurring-issue-checklist.sh +95 -0
  288. package/tools/tests/test-sync-shared-agent-home-local-source-root.sh +66 -0
  289. package/tools/tests/test-sync-shared-agent-home-preserves-unrelated-workflow-catalog-skill.sh +47 -0
  290. package/tools/tests/test-test-smoke.sh +86 -0
  291. package/tools/tests/test-uninstall-project-launchd.sh +37 -0
  292. package/tools/tests/test-update-github-labels-prefers-sibling-helper.sh +49 -0
  293. package/tools/tests/test-workflow-catalog.sh +43 -0
  294. package/tools/vendor/codex-quota/LICENSE +21 -0
  295. package/tools/vendor/codex-quota/README.md +459 -0
  296. package/tools/vendor/codex-quota/codex-quota.js +261 -0
  297. package/tools/vendor/codex-quota/lib/claude-accounts.js +226 -0
  298. package/tools/vendor/codex-quota/lib/claude-oauth.js +174 -0
  299. package/tools/vendor/codex-quota/lib/claude-tokens.js +471 -0
  300. package/tools/vendor/codex-quota/lib/claude-usage.js +929 -0
  301. package/tools/vendor/codex-quota/lib/codex-accounts.js +205 -0
  302. package/tools/vendor/codex-quota/lib/codex-tokens.js +326 -0
  303. package/tools/vendor/codex-quota/lib/codex-usage.js +32 -0
  304. package/tools/vendor/codex-quota/lib/color.js +72 -0
  305. package/tools/vendor/codex-quota/lib/constants.js +57 -0
  306. package/tools/vendor/codex-quota/lib/container.js +143 -0
  307. package/tools/vendor/codex-quota/lib/display.js +1111 -0
  308. package/tools/vendor/codex-quota/lib/fs.js +63 -0
  309. package/tools/vendor/codex-quota/lib/handlers.js +2060 -0
  310. package/tools/vendor/codex-quota/lib/jwt.js +33 -0
  311. package/tools/vendor/codex-quota/lib/oauth.js +486 -0
  312. package/tools/vendor/codex-quota/lib/paths.js +34 -0
  313. package/tools/vendor/codex-quota/lib/prompts.js +44 -0
  314. package/tools/vendor/codex-quota/lib/sync.js +1438 -0
  315. package/tools/vendor/codex-quota/lib/token-match.js +96 -0
  316. package/tools/vendor/codex-quota-manager/scripts/auto-switch.sh +500 -0
  317. 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
+