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,459 @@
1
+ # codex-quota
2
+
3
+ Multi-account manager for OpenAI Codex CLI and OpenCode. Add, switch, list, and remove accounts with OAuth browser authentication. Seamlessly switch between both tools with shared credentials.
4
+
5
+ Zero dependencies - uses Node.js built-ins only.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g codex-quota
11
+ ```
12
+
13
+ Or with bun:
14
+
15
+ ```bash
16
+ bun add -g codex-quota
17
+ ```
18
+
19
+ After installation, both `codex-quota` and `cq` commands are available.
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # Add a new account (opens browser for OAuth)
25
+ codex-quota codex add personal
26
+
27
+ # Add a Claude credential (interactive)
28
+ codex-quota claude add work
29
+
30
+ # Check quota for all accounts
31
+ codex-quota
32
+
33
+ # Switch active Codex account
34
+ codex-quota codex switch personal
35
+
36
+ # Switch Claude credentials
37
+ codex-quota claude switch work
38
+
39
+ # Sync activeLabel to CLI auth files
40
+ codex-quota codex sync
41
+ codex-quota claude sync
42
+
43
+ # Preview sync without writing files
44
+ codex-quota codex sync --dry-run
45
+ codex-quota claude sync --dry-run
46
+
47
+ # List accounts
48
+ codex-quota codex list
49
+ codex-quota claude list
50
+
51
+ # Remove an account
52
+ codex-quota codex remove old-account
53
+ codex-quota claude remove old-account
54
+ ```
55
+
56
+ ## Commands
57
+
58
+ Run `codex-quota` with no namespace to check combined Codex + Claude usage.
59
+
60
+ ### codex quota
61
+
62
+ Check usage quota for Codex accounts.
63
+
64
+ ```bash
65
+ codex-quota codex quota # All Codex accounts
66
+ codex-quota codex quota personal # Specific account
67
+ codex-quota codex quota --json # JSON output
68
+ ```
69
+
70
+ ### claude quota
71
+
72
+ Check usage quota for Claude accounts.
73
+
74
+ ```bash
75
+ codex-quota claude quota # All Claude accounts
76
+ codex-quota claude quota work # Specific credential
77
+ codex-quota claude quota --json # JSON output
78
+ ```
79
+
80
+ ### codex add
81
+
82
+ Add a new Codex account via OAuth browser authentication.
83
+
84
+ ```bash
85
+ codex-quota codex add # Label derived from email
86
+ codex-quota codex add work # With explicit label
87
+ codex-quota codex add --no-browser # Print URL (for SSH/headless)
88
+ ```
89
+
90
+ ### claude add
91
+
92
+ Add a Claude credential interactively.
93
+
94
+ ```bash
95
+ codex-quota claude add # Prompt for label + credentials
96
+ codex-quota claude add work # With explicit label
97
+ codex-quota claude add work --json # JSON output
98
+ ```
99
+
100
+ ### codex switch
101
+
102
+ Switch the active account for Codex CLI, OpenCode, and pi.
103
+
104
+ ```bash
105
+ codex-quota codex switch personal
106
+ ```
107
+
108
+ When you run `codex switch`:
109
+
110
+ 1. **Codex CLI** - Updates `~/.codex/auth.json` with the selected account tokens
111
+ 2. **OpenCode** - If `~/.local/share/opencode/auth.json` exists, updates the `openai` provider entry
112
+ 3. **pi** - If `~/.pi/agent/auth.json` exists, updates the `openai-codex` provider entry
113
+
114
+ It also updates `activeLabel` in `~/.codex-accounts.json` when available.
115
+
116
+ ### claude switch
117
+
118
+ Switch Claude Code, OpenCode, and pi to a stored Claude credential.
119
+
120
+ ```bash
121
+ codex-quota claude switch work
122
+ ```
123
+
124
+ This updates `activeLabel` in `~/.claude-accounts.json` when available. OAuth-based
125
+ credentials are required to update CLI auth files.
126
+
127
+ ### codex list
128
+
129
+ List all Codex accounts from all sources with status indicators.
130
+
131
+ ```bash
132
+ codex-quota codex list
133
+ codex-quota codex list --json
134
+ ```
135
+
136
+ Output shows:
137
+ - `*` = active account (from `activeLabel`)
138
+ - `~` = CLI auth account when it diverges from `activeLabel`
139
+ - Email, plan type, token expiry
140
+ - Source file for each account
141
+
142
+ If CLI auth diverges from the tracked `activeLabel`, `list` and `quota` print a warning and
143
+ suggest `codex-quota codex sync` to realign.
144
+
145
+ ### claude list
146
+
147
+ List Claude credentials from `CLAUDE_ACCOUNTS` or `~/.claude-accounts.json`.
148
+
149
+ ```bash
150
+ codex-quota claude list
151
+ codex-quota claude list --json
152
+ ```
153
+
154
+ Output shows:
155
+ - `*` = active account (from `activeLabel`)
156
+ - Source file for each credential
157
+
158
+ For OAuth-based accounts, `list` and `quota` warn when stored tokens diverge from the
159
+ `activeLabel` account. Session-key-only accounts are skipped.
160
+
161
+ ### codex remove
162
+
163
+ Remove a Codex account from storage.
164
+
165
+ ```bash
166
+ codex-quota codex remove old-account
167
+ ```
168
+
169
+ Note: Accounts from `CODEX_ACCOUNTS` env var cannot be removed via CLI.
170
+
171
+ ### claude remove
172
+
173
+ Remove a Claude credential from storage.
174
+
175
+ ```bash
176
+ codex-quota claude remove old-account
177
+ ```
178
+
179
+ Note: Accounts from `CLAUDE_ACCOUNTS` env var cannot be removed via CLI.
180
+
181
+ ### codex sync
182
+
183
+ Sync the `activeLabel` Codex account to CLI auth files.
184
+
185
+ ```bash
186
+ codex-quota codex sync
187
+ codex-quota codex sync --dry-run
188
+ codex-quota codex sync --json
189
+ ```
190
+
191
+ This updates:
192
+ 1. `~/.codex/auth.json`
193
+ 2. `~/.local/share/opencode/auth.json` (if it exists)
194
+ 3. `~/.pi/agent/auth.json` (if it exists)
195
+
196
+ ### claude sync
197
+
198
+ Sync the `activeLabel` Claude account to CLI auth files.
199
+
200
+ ```bash
201
+ codex-quota claude sync
202
+ codex-quota claude sync --dry-run
203
+ codex-quota claude sync --json
204
+ ```
205
+
206
+ Only OAuth-based Claude accounts can be synced. Session-key-only accounts are skipped with
207
+ a warning.
208
+
209
+ ## Options
210
+
211
+ | Option | Description |
212
+ |--------|-------------|
213
+ | `--json` | Output in JSON format |
214
+ | `--dry-run` | Preview sync without writing files |
215
+ | `--no-browser` | Print auth URL instead of opening browser |
216
+ | `--no-color` | Disable colored output |
217
+ | `--version, -v` | Show version number |
218
+ | `--help, -h` | Show help |
219
+
220
+ ## Account Sources
221
+
222
+ Accounts are loaded from these locations (in order). Read/write indicates whether the CLI
223
+ reads from or writes to each path.
224
+
225
+ | Source | Purpose | Read | Write |
226
+ |--------|---------|------|-------|
227
+ | `CODEX_ACCOUNTS` env var | JSON array of accounts | Yes | No |
228
+ | `~/.codex-accounts.json` | Primary multi-account file (shared with OpenCode) | Yes | Yes (`add`, `remove`) |
229
+ | `~/.opencode/openai-codex-auth-accounts.json` | OpenCode accounts | Yes | No |
230
+ | `~/.codex/auth.json` | Codex CLI single-account (label `codex-cli`) | Yes | Yes (`switch`) |
231
+ | `~/.local/share/opencode/auth.json` | OpenCode auth file (`openai` provider) | No | Yes (`switch` if it exists) |
232
+ | `~/.pi/agent/auth.json` | pi auth file (`openai-codex` provider) | No | Yes (`switch` if it exists) |
233
+
234
+ New accounts added via `codex-quota codex add` are saved to `~/.codex-accounts.json`, which is
235
+ shared with OpenCode.
236
+
237
+ Claude sources (in order):
238
+
239
+ | Source | Purpose | Read | Write |
240
+ |--------|---------|------|-------|
241
+ | `CLAUDE_ACCOUNTS` env var | JSON array of credentials | Yes | No |
242
+ | `~/.claude-accounts.json` | Claude multi-account file | Yes | Yes (`add`, `remove`) |
243
+ | `~/.claude/.credentials.json` | Claude Code credentials | Yes | Yes (`switch`, `sync`) |
244
+ | `~/.local/share/opencode/auth.json` | OpenCode auth file (`anthropic` provider) | No | Yes (`switch`, `sync` if it exists) |
245
+ | `~/.pi/agent/auth.json` | pi auth file (`anthropic` provider) | No | Yes (`switch`, `sync` if it exists) |
246
+
247
+ ## Multi-Account JSON Schema
248
+
249
+ File: `~/.codex-accounts.json`
250
+
251
+ ```json
252
+ {
253
+ "schemaVersion": 1,
254
+ "activeLabel": "personal",
255
+ "accounts": [
256
+ {
257
+ "label": "personal",
258
+ "accountId": "chatgpt-account-uuid",
259
+ "access": "access-token",
260
+ "refresh": "refresh-token",
261
+ "idToken": "id-token-or-null",
262
+ "expires": 1234567890000
263
+ }
264
+ ]
265
+ }
266
+ ```
267
+
268
+ | Field | Type | Description |
269
+ |-------|------|-------------|
270
+ | `schemaVersion` | number | Schema version marker (root field) |
271
+ | `activeLabel` | string\|null | Active account label (root field) |
272
+ | `label` | string | Unique identifier for the account |
273
+ | `accountId` | string | ChatGPT account UUID |
274
+ | `access` | string | OAuth access token |
275
+ | `refresh` | string | OAuth refresh token |
276
+ | `idToken` | string\|null | OAuth ID token (optional, for email extraction) |
277
+ | `expires` | number | Token expiry timestamp in milliseconds |
278
+
279
+ Root-level fields are preserved on write; unknown root fields are kept intact.
280
+
281
+ Claude multi-account files (`~/.claude-accounts.json`) use the same root fields
282
+ (`schemaVersion`, `activeLabel`) and store account entries that include a
283
+ `sessionKey` or OAuth tokens.
284
+
285
+ ## OAuth Flow
286
+
287
+ The `codex add` command uses OAuth 2.0 with PKCE for secure browser authentication:
288
+
289
+ 1. Generates PKCE code verifier and challenge
290
+ 2. Starts local callback server on `http://127.0.0.1:1455`
291
+ 3. Opens browser to OpenAI authorization page
292
+ 4. User authenticates in browser
293
+ 5. Callback server receives authorization code
294
+ 6. Exchanges code for tokens using PKCE verifier
295
+ 7. Saves tokens to `~/.codex-accounts.json`
296
+
297
+ ### Headless/SSH Mode
298
+
299
+ In SSH sessions or headless environments (detected via `SSH_CLIENT`, `SSH_TTY`, or missing `DISPLAY`), the auth URL is printed instead of opening a browser:
300
+
301
+ ```bash
302
+ codex-quota codex add --no-browser
303
+ # Prints: Open this URL in your browser: https://auth.openai.com/authorize?...
304
+ ```
305
+
306
+ Copy the URL to a browser on another machine, complete authentication, and the callback will be received by the local server.
307
+
308
+ ## Troubleshooting
309
+
310
+ ### Port 1455 in use
311
+
312
+ ```
313
+ Error: Port 1455 is in use. Close other codex-quota instances and retry.
314
+ ```
315
+
316
+ Another process is using port 1455. Check for:
317
+ - Other `codex-quota codex add` commands running
318
+ - OpenCode or Codex CLI auth processes
319
+
320
+ Find and kill the process:
321
+ ```bash
322
+ lsof -i :1455
323
+ kill <pid>
324
+ ```
325
+
326
+ ### SSH/Headless authentication
327
+
328
+ If browser doesn't open in SSH session:
329
+
330
+ 1. Use `--no-browser` flag: `codex-quota codex add --no-browser`
331
+ 2. Copy the printed URL to a browser on another machine
332
+ 3. Complete authentication in browser
333
+ 4. The callback is received by the server running over SSH
334
+
335
+ ### Token refresh failures
336
+
337
+ If token refresh fails:
338
+ ```
339
+ Error: Failed to refresh token. Re-authenticate with 'codex-quota codex add'.
340
+ ```
341
+
342
+ The refresh token may have expired. Add the account again:
343
+ ```bash
344
+ codex-quota codex remove expired-account
345
+ codex-quota codex add new-label
346
+ ```
347
+
348
+ ### Environment variable accounts
349
+
350
+ Accounts from `CODEX_ACCOUNTS` env var cannot be removed via CLI:
351
+ ```
352
+ Error: Cannot remove account from CODEX_ACCOUNTS env var. Modify the env var directly.
353
+ ```
354
+
355
+ Edit your shell configuration to remove the account from the env var.
356
+
357
+ ## JSON Output
358
+
359
+ All commands support `--json` for scripting:
360
+
361
+ ```bash
362
+ # Quota (combined)
363
+ codex-quota --json
364
+ # {"codex":[{"label":"personal","email":"user@example.com","usage":{...}}],"claude":[...]}
365
+
366
+ # List (Codex)
367
+ codex-quota codex list --json
368
+ # {"accounts":[{"label":"personal","isActive":true,"email":"...","source":"..."}]}
369
+
370
+ # Add (Codex, success)
371
+ codex-quota codex add work --json
372
+ # {"success":true,"label":"work","email":"user@example.com","accountId":"...","source":"~/.codex-accounts.json"}
373
+
374
+ # Switch (Codex)
375
+ codex-quota codex switch personal --json
376
+ # {"success":true,"label":"personal","email":"...","authPath":"~/.codex/auth.json"}
377
+
378
+ # Sync (Codex)
379
+ codex-quota codex sync --json
380
+ # {"success":true,"activeLabel":"work","updated":["~/.codex/auth.json",...],"skipped":[...]}
381
+
382
+ # Errors include structured data
383
+ codex-quota codex switch nonexistent --json
384
+ # {"success":false,"error":"Account not found","availableLabels":["personal","work"]}
385
+ ```
386
+
387
+ ## Claude Code Usage (Optional)
388
+
389
+ Use the `claude` namespace to check Claude usage alongside OpenAI quotas:
390
+
391
+ ```bash
392
+ codex-quota claude quota
393
+ ```
394
+
395
+ If multiple Claude accounts are configured, each account is fetched and displayed separately.
396
+
397
+ To add a Claude credential interactively:
398
+
399
+ ```bash
400
+ codex-quota claude add
401
+ ```
402
+
403
+ This uses your local Claude session to call:
404
+ - `https://claude.ai/api/organizations`
405
+ - `https://claude.ai/api/organizations/{orgId}/usage`
406
+ - `https://claude.ai/api/organizations/{orgId}/overage_spend_limit`
407
+ - `https://claude.ai/api/account`
408
+
409
+ Authentication sources (in order):
410
+ 1. `CLAUDE_ACCOUNTS` env var (JSON array or `{ accounts: [...] }`)
411
+ 2. `~/.claude-accounts.json` (multi-account format)
412
+ 3. Browser cookies (Chromium/Chrome) to read `sessionKey` and `lastActiveOrg`
413
+ 4. `~/.claude/.credentials.json` OAuth `accessToken`
414
+
415
+ Multi-account format (Claude):
416
+ ```json
417
+ {
418
+ "accounts": [
419
+ {
420
+ "label": "personal",
421
+ "sessionKey": "sk-ant-oat...",
422
+ "cfClearance": "cf_clearance...",
423
+ "oauthToken": "claude-ai-access-token",
424
+ "orgId": "org_uuid_optional"
425
+ }
426
+ ]
427
+ }
428
+ ```
429
+
430
+ Notes:
431
+ - Only `label` plus one of `sessionKey` or `oauthToken` is required.
432
+ - `cfClearance`, `orgId`, and `cookies` are optional.
433
+
434
+ Environment overrides:
435
+ - `CLAUDE_ACCOUNTS` to supply multi-account JSON directly
436
+ - `CLAUDE_CREDENTIALS_PATH` to point to a different credentials file
437
+ - `CLAUDE_COOKIE_DB_PATH` to point to a specific Chromium/Chrome Cookies DB
438
+
439
+ Codex overrides:
440
+ - `CODEX_ACCOUNTS` to supply multi-account JSON directly (read-only)
441
+ - `CODEX_AUTH_PATH` to point to a different Codex CLI auth file
442
+ - `XDG_DATA_HOME` to relocate OpenCode auth paths
443
+ - `PI_AUTH_PATH` to point to a different pi auth file
444
+
445
+ Notes:
446
+ - On Linux, cookie access requires `sqlite3` and `secret-tool` (libsecret) to decrypt cookies.
447
+ - For best results, keep `claude.ai` logged in within your Chromium/Chrome profile.
448
+
449
+ ## Releasing
450
+
451
+ - Run `bun test` and `bun run preflight` before publishing.
452
+ - Bump version with `bun pm version patch|minor|major`.
453
+ - Dry-run the package with `bun run release:pack`.
454
+ - Publish with `bun run release:publish` (local publish, no provenance).
455
+ - Ensure the git working tree is clean.
456
+
457
+ ## License
458
+
459
+ MIT
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Standalone Codex quota checker for multiple OAuth accounts
5
+ * Zero dependencies - uses Node.js built-ins only
6
+ *
7
+ * This is a thin entry point. All logic lives in lib/ modules.
8
+ * Barrel re-exports below maintain backward compatibility for tests and consumers.
9
+ */
10
+
11
+ import { realpathSync } from "node:fs";
12
+
13
+ // ─── Imports from lib modules ────────────────────────────────────────────────
14
+
15
+ import { PRIMARY_CMD, MULTI_ACCOUNT_PATHS, CODEX_CLI_AUTH_PATH, CLAUDE_MULTI_ACCOUNT_PATHS } from "./lib/constants.js";
16
+ import { GREEN, RED, YELLOW, setNoColorFlag, supportsColor, colorize, getPackageVersion } from "./lib/color.js";
17
+ import { decodeJWT, extractAccountId, extractProfile } from "./lib/jwt.js";
18
+ import {
19
+ printHelp, printHelpCodex, printHelpClaude,
20
+ printHelpAdd, printHelpCodexReauth, printHelpSwitch, printHelpCodexSync,
21
+ printHelpList, printHelpRemove, printHelpQuota,
22
+ printHelpClaudeAdd, printHelpClaudeReauth, printHelpClaudeSwitch, printHelpClaudeSync,
23
+ printHelpClaudeList, printHelpClaudeRemove, printHelpClaudeQuota,
24
+ } from "./lib/display.js";
25
+ import { handleCodex, handleClaude, handleQuota } from "./lib/handlers.js";
26
+
27
+ // ─── Main ────────────────────────────────────────────────────────────────────
28
+
29
+ async function main() {
30
+ const args = process.argv.slice(2);
31
+
32
+ // Parse flags
33
+ const flags = {
34
+ json: args.includes("--json"),
35
+ noBrowser: args.includes("--no-browser"),
36
+ noColor: args.includes("--no-color"),
37
+ oauth: args.includes("--oauth"),
38
+ manual: args.includes("--manual"),
39
+ dryRun: args.includes("--dry-run"),
40
+ local: args.includes("--local"),
41
+ };
42
+
43
+ // Set global noColorFlag for supportsColor() function
44
+ setNoColorFlag(flags.noColor);
45
+
46
+ const legacyFlagUsed = args.includes("--claude") || args.includes("--codex");
47
+ if (legacyFlagUsed) {
48
+ console.error(colorize("Error: --claude/--codex flags were replaced by namespaces.", RED));
49
+ console.error(`Use '${PRIMARY_CMD} claude' or '${PRIMARY_CMD} codex' instead.`);
50
+ process.exit(1);
51
+ }
52
+
53
+ // Extract non-flag arguments
54
+ const nonFlagArgs = args.filter(a => !a.startsWith("--") && a !== "-h");
55
+ const firstArg = nonFlagArgs[0];
56
+ const namespace = firstArg === "codex" || firstArg === "claude" ? firstArg : null;
57
+ const namespaceArgs = namespace ? nonFlagArgs.slice(1) : nonFlagArgs;
58
+ const subcommand = namespace ? namespaceArgs[0] : null;
59
+
60
+ // Handle --version flag
61
+ if (args.includes("--version") || args.includes("-v")) {
62
+ console.log(getPackageVersion());
63
+ return;
64
+ }
65
+
66
+ const legacyCommands = ["add", "reauth", "switch", "list", "remove", "quota", "sync"];
67
+ if (!namespace && firstArg && legacyCommands.includes(firstArg)) {
68
+ console.error(colorize(`Error: '${firstArg}' now requires a namespace.`, RED));
69
+ console.error(`Use '${PRIMARY_CMD} codex ${firstArg}' or '${PRIMARY_CMD} claude ${firstArg}'.`);
70
+ process.exit(1);
71
+ }
72
+
73
+ // Handle --help: show main help or subcommand-specific help
74
+ if (args.includes("--help") || args.includes("-h")) {
75
+ if (!namespace) {
76
+ printHelp();
77
+ return;
78
+ }
79
+ if (namespace === "codex") {
80
+ switch (subcommand) {
81
+ case "add": printHelpAdd(); break;
82
+ case "reauth": printHelpCodexReauth(); break;
83
+ case "switch": printHelpSwitch(); break;
84
+ case "sync": printHelpCodexSync(); break;
85
+ case "list": printHelpList(); break;
86
+ case "remove": printHelpRemove(); break;
87
+ case "quota": printHelpQuota(); break;
88
+ default: printHelpCodex(); break;
89
+ }
90
+ return;
91
+ }
92
+ switch (subcommand) {
93
+ case "add": printHelpClaudeAdd(); break;
94
+ case "reauth": printHelpClaudeReauth(); break;
95
+ case "switch": printHelpClaudeSwitch(); break;
96
+ case "sync": printHelpClaudeSync(); break;
97
+ case "list": printHelpClaudeList(); break;
98
+ case "remove": printHelpClaudeRemove(); break;
99
+ case "quota": printHelpClaudeQuota(); break;
100
+ default: printHelpClaude(); break;
101
+ }
102
+ return;
103
+ }
104
+
105
+ // Route to appropriate handler based on subcommand
106
+ if (namespace === "codex") {
107
+ await handleCodex(namespaceArgs, flags);
108
+ return;
109
+ }
110
+ if (namespace === "claude") {
111
+ await handleClaude(namespaceArgs, flags);
112
+ return;
113
+ }
114
+
115
+ // Default behavior: run combined quota command
116
+ await handleQuota(nonFlagArgs, flags, "all");
117
+ }
118
+
119
+ // Only run main() when executed directly (not imported for testing)
120
+ function getResolvedArgv1() {
121
+ try {
122
+ const arg = process.argv[1];
123
+ if (!arg) return null;
124
+ return realpathSync(arg);
125
+ } catch {
126
+ return process.argv[1] || null;
127
+ }
128
+ }
129
+ const resolvedArgv1 = getResolvedArgv1();
130
+ const isMain = resolvedArgv1 && (
131
+ import.meta.url === `file://${resolvedArgv1}` ||
132
+ import.meta.url === `file://${process.argv[1]}`
133
+ );
134
+ if (isMain) {
135
+ main().catch(e => {
136
+ console.error(e.message);
137
+ process.exit(1);
138
+ });
139
+ }
140
+
141
+ // ─── Barrel re-exports for backward compatibility (tests + external consumers) ──
142
+
143
+ // Account loading functions
144
+ export {
145
+ loadAccountsFromEnv,
146
+ loadAccountsFromFile,
147
+ loadAccountFromCodexCli,
148
+ loadAllAccounts,
149
+ loadAllAccountsNoDedup,
150
+ findAccountByLabel,
151
+ getAllLabels,
152
+ isValidAccount,
153
+ } from "./lib/codex-accounts.js";
154
+
155
+ export {
156
+ loadClaudeAccountsFromEnv,
157
+ loadClaudeAccountsFromFile,
158
+ loadClaudeAccounts,
159
+ isValidClaudeAccount,
160
+ } from "./lib/claude-accounts.js";
161
+
162
+ // Deduplication functions
163
+ export { deduplicateAccountsByEmail } from "./lib/codex-accounts.js";
164
+ export { deduplicateClaudeOAuthAccounts } from "./lib/claude-usage.js";
165
+
166
+ // Claude OAuth functions
167
+ export {
168
+ loadClaudeOAuthFromClaudeCode,
169
+ loadClaudeOAuthFromOpenCode,
170
+ loadClaudeOAuthFromEnv,
171
+ loadAllClaudeOAuthAccounts,
172
+ fetchClaudeOAuthUsage,
173
+ fetchClaudeOAuthUsageForAccount,
174
+ } from "./lib/claude-usage.js";
175
+
176
+ export {
177
+ ensureFreshClaudeOAuthToken,
178
+ persistClaudeOAuthTokens,
179
+ refreshClaudeToken,
180
+ } from "./lib/claude-tokens.js";
181
+
182
+ export {
183
+ ensureFreshToken,
184
+ persistOpenAiOAuthTokens,
185
+ } from "./lib/codex-tokens.js";
186
+
187
+ // OAuth PKCE utilities
188
+ export {
189
+ generatePKCE,
190
+ generateState,
191
+ buildAuthUrl,
192
+ checkPortAvailable,
193
+ isHeadlessEnvironment,
194
+ openBrowser,
195
+ startCallbackServer,
196
+ exchangeCodeForTokens,
197
+ } from "./lib/oauth.js";
198
+
199
+ // Claude OAuth browser flow
200
+ export {
201
+ buildClaudeAuthUrl,
202
+ parseClaudeCodeState,
203
+ exchangeClaudeCodeForTokens,
204
+ handleClaudeOAuthFlow,
205
+ } from "./lib/claude-oauth.js";
206
+
207
+ // JWT utilities
208
+ export { decodeJWT, extractAccountId, extractProfile } from "./lib/jwt.js";
209
+
210
+ // Divergence helpers (for testing)
211
+ export {
212
+ detectCodexDivergence,
213
+ detectClaudeDivergence,
214
+ findFresherOpenAiOAuthStore,
215
+ findFresherClaudeOAuthStore,
216
+ readOpencodeOpenAiOauthStore,
217
+ readPiOpenAiOauthStore,
218
+ readCodexCliOpenAiOauthStore,
219
+ getActiveAccountId,
220
+ getActiveAccountInfo,
221
+ handleCodexSync,
222
+ handleClaudeSync,
223
+ } from "./lib/sync.js";
224
+
225
+ // Display helpers (for testing)
226
+ export {
227
+ shortenPath,
228
+ formatExpiryStatus,
229
+ normalizePercentUsed,
230
+ parseClaudeUtilizationWindow,
231
+ drawBox,
232
+ printHelp,
233
+ printHelpAdd,
234
+ printHelpCodexReauth,
235
+ printHelpClaude,
236
+ printHelpClaudeAdd,
237
+ printHelpClaudeReauth,
238
+ printHelpClaudeSync,
239
+ printHelpSwitch,
240
+ printHelpCodexSync,
241
+ printHelpList,
242
+ printHelpRemove,
243
+ printHelpQuota,
244
+ } from "./lib/display.js";
245
+
246
+ // Subcommand handlers (for testing)
247
+ export {
248
+ handleSwitch,
249
+ handleCodexReauth,
250
+ handleRemove,
251
+ handleClaudeAdd,
252
+ handleClaudeReauth,
253
+ handleClaudeSwitch,
254
+ handleClaudeRemove,
255
+ } from "./lib/handlers.js";
256
+
257
+ // Color utilities
258
+ export { supportsColor, colorize, setNoColorFlag } from "./lib/color.js";
259
+
260
+ // Constants (for testing)
261
+ export { MULTI_ACCOUNT_PATHS, CODEX_CLI_AUTH_PATH, PRIMARY_CMD, CLAUDE_MULTI_ACCOUNT_PATHS } from "./lib/constants.js";