@iislee/opencodex 2.11.0 → 2.35.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 (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,317 @@
1
+ import { isAbsolute } from "node:path";
2
+ import { codexExecInvocation } from "../codex/exec-invocation";
3
+ import { resolveAndPersistCodexRuntime, type ResolveCodexRuntimeDeps } from "../codex/runtime";
4
+ import type { ResolveDeps, SpawnInvocation } from "../lib/win-exec";
5
+ import {
6
+ apiError,
7
+ apiJson,
8
+ proxyUnreachable,
9
+ resolveBaseUrl,
10
+ type AccountDeps,
11
+ type NativeMainLoginChild,
12
+ type StageLeaseClock,
13
+ } from "./account-api";
14
+
15
+ const USAGE = `Usage:
16
+ ocx account main doctor [--json]
17
+ ocx account main list [--json]
18
+ ocx account main register <label> [--json]
19
+ ocx account main add <label>
20
+ ocx account main switch <profile-id-or-label> --yes [--json]
21
+ ocx account main recover [--rollback --yes] [--json]
22
+
23
+ Native main login profiles change the physical Codex App/CLI login in the effective CODEX_HOME.
24
+ They are independent from the OpenCodex Pool selected by 'ocx account use openai'.`;
25
+
26
+ const STAGE_LEASE_SAFETY_MARGIN_MS = 30_000;
27
+
28
+ const systemStageLeaseClock: StageLeaseClock = {
29
+ now: Date.now,
30
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
31
+ clearTimeout: timer => clearTimeout(timer),
32
+ };
33
+
34
+ interface PublicProfile {
35
+ id: string;
36
+ label: string;
37
+ identityHint: string;
38
+ state: "active" | "inactive";
39
+ }
40
+
41
+ export type NativeMainCodexLoginInvocationDeps =
42
+ & ResolveDeps
43
+ & Pick<ResolveCodexRuntimeDeps, "existsSync" | "execFileSync" | "configDir" | "readFileSync">;
44
+
45
+ function flag(args: string[], name: string): boolean {
46
+ const index = args.indexOf(name);
47
+ if (index < 0) return false;
48
+ args.splice(index, 1);
49
+ return true;
50
+ }
51
+
52
+ function reject(args: string[]): number {
53
+ if (args.length > 0) console.error(`Unexpected argument(s): ${args.join(", ")}`);
54
+ console.error(USAGE);
55
+ return 1;
56
+ }
57
+
58
+ function effectiveCodexHome(result: Record<string, unknown>): string {
59
+ return typeof result.effectiveCodexHome === "string" && result.effectiveCodexHome.length > 0
60
+ ? result.effectiveCodexHome
61
+ : "the effective CODEX_HOME";
62
+ }
63
+
64
+ function printProfiles(profiles: PublicProfile[]): void {
65
+ if (profiles.length === 0) { console.log("No native main login profiles registered."); return; }
66
+ const rows = profiles.map(profile => [profile.state === "active" ? "*" : " ", profile.label, profile.id, profile.identityHint]);
67
+ const header = ["", "LABEL", "PROFILE ID", "IDENTITY"];
68
+ const widths = header.map((value, index) => Math.max(value.length, ...rows.map(row => row[index]!.length)));
69
+ const line = (columns: string[]) => columns.map((value, index) => value.padEnd(widths[index]!)).join(" ").trimEnd();
70
+ console.log([line(header), ...rows.map(line)].join("\n"));
71
+ }
72
+
73
+ export function nativeMainCodexLoginInvocation(
74
+ platform: NodeJS.Platform = process.platform,
75
+ deps: NativeMainCodexLoginInvocationDeps = {},
76
+ ): SpawnInvocation {
77
+ const command = resolveAndPersistCodexRuntime({
78
+ env: deps.env ?? process.env,
79
+ platform,
80
+ existsSync: deps.existsSync,
81
+ execFileSync: deps.execFileSync,
82
+ configDir: deps.configDir,
83
+ readFileSync: deps.readFileSync,
84
+ discoverAlternatives: false,
85
+ }).runtime.command || "codex";
86
+ return codexExecInvocation(command, ["login"], platform, deps);
87
+ }
88
+
89
+ function spawnOfficialCodexLogin(codexHome: string): NativeMainLoginChild {
90
+ const invocation = nativeMainCodexLoginInvocation();
91
+ const child = Bun.spawn([invocation.file, ...invocation.args], {
92
+ env: { ...process.env, CODEX_HOME: codexHome },
93
+ stdin: "inherit",
94
+ stdout: "inherit",
95
+ stderr: "inherit",
96
+ ...(invocation.options.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
97
+ });
98
+ return child;
99
+ }
100
+
101
+ function abortableDelay(ms: number, signal: AbortSignal, clock: StageLeaseClock): Promise<void> {
102
+ if (signal.aborted) return Promise.resolve();
103
+ return new Promise(resolve => {
104
+ const timer = clock.setTimeout(done, ms);
105
+ signal.addEventListener("abort", done, { once: true });
106
+ function done(): void {
107
+ clock.clearTimeout(timer);
108
+ signal.removeEventListener("abort", done);
109
+ resolve();
110
+ }
111
+ });
112
+ }
113
+
114
+ async function maintainStageLease(
115
+ deps: AccountDeps,
116
+ baseUrl: string,
117
+ stageId: string,
118
+ writerToken: string,
119
+ initialLeaseExpiresAt: number,
120
+ heartbeatIntervalMs: number,
121
+ child: NativeMainLoginChild,
122
+ signal: AbortSignal,
123
+ clock: StageLeaseClock,
124
+ ): Promise<{ lost: boolean }> {
125
+ let leaseExpiresAt = initialLeaseExpiresAt;
126
+ for (;;) {
127
+ if (signal.aborted) return { lost: false };
128
+ const deadlineAt = leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS;
129
+ if (clock.now() >= deadlineAt) {
130
+ try { child.kill(); } catch { /* child exit below is authoritative */ }
131
+ return { lost: true };
132
+ }
133
+
134
+ const heartbeatAbort = new AbortController();
135
+ const deadlineReached = Symbol("heartbeat-deadline");
136
+ const parentCancelled = Symbol("heartbeat-parent-cancelled");
137
+ let resolveInterruption!: (result: typeof deadlineReached | typeof parentCancelled) => void;
138
+ const interruption = new Promise<typeof deadlineReached | typeof parentCancelled>(resolve => {
139
+ resolveInterruption = resolve;
140
+ });
141
+ const cancelHeartbeat = () => {
142
+ heartbeatAbort.abort();
143
+ resolveInterruption(parentCancelled);
144
+ };
145
+ signal.addEventListener("abort", cancelHeartbeat, { once: true });
146
+ const deadlineTimer = clock.setTimeout(() => {
147
+ heartbeatAbort.abort();
148
+ resolveInterruption(deadlineReached);
149
+ }, Math.max(0, deadlineAt - clock.now()));
150
+ const heartbeatRequest = apiJson(
151
+ deps,
152
+ baseUrl,
153
+ "POST",
154
+ "/api/native-main-profiles/stage/heartbeat",
155
+ { stageId, writerToken },
156
+ { signal: heartbeatAbort.signal },
157
+ );
158
+ const heartbeat = await Promise.race([heartbeatRequest, interruption]);
159
+ clock.clearTimeout(deadlineTimer);
160
+ signal.removeEventListener("abort", cancelHeartbeat);
161
+ if (signal.aborted) return { lost: false };
162
+ if (heartbeat === parentCancelled) return { lost: false };
163
+ if (heartbeat === deadlineReached) {
164
+ try { child.kill(); } catch { /* child exit below is authoritative */ }
165
+ return { lost: true };
166
+ }
167
+ if (heartbeat.status === 200 && typeof heartbeat.json.leaseExpiresAt === "number") {
168
+ leaseExpiresAt = heartbeat.json.leaseExpiresAt;
169
+ await abortableDelay(Math.min(heartbeatIntervalMs, Math.max(0, leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS - clock.now())), signal, clock);
170
+ continue;
171
+ }
172
+ if (heartbeat.status === 0 && clock.now() < leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS) {
173
+ await abortableDelay(Math.min(5_000, heartbeatIntervalMs, leaseExpiresAt - STAGE_LEASE_SAFETY_MARGIN_MS - clock.now()), signal, clock);
174
+ continue;
175
+ }
176
+ try { child.kill(); } catch { /* child exit below is authoritative */ }
177
+ return { lost: true };
178
+ }
179
+ }
180
+
181
+ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): Promise<number> {
182
+ const sub = args.shift();
183
+ const wantsJson = flag(args, "--json");
184
+ const confirmed = flag(args, "--yes");
185
+ const rollback = flag(args, "--rollback");
186
+ const baseUrl = await resolveBaseUrl(deps);
187
+ if (!baseUrl) return proxyUnreachable();
188
+
189
+ if (sub === "doctor" || sub === "list") {
190
+ if (args.length > 0 || confirmed || rollback) return reject(args);
191
+ const path = sub === "doctor" ? "/api/native-main-profiles/doctor" : "/api/native-main-profiles";
192
+ const result = await apiJson(deps, baseUrl, "GET", path);
193
+ if (result.status === 0) return proxyUnreachable(result.transportError);
194
+ if (result.status !== 200) return apiError(result.json, `failed to ${sub} native profiles`, result.status);
195
+ if (wantsJson || sub === "doctor") console.log(JSON.stringify(result.json, null, 2));
196
+ else printProfiles(Array.isArray(result.json.profiles) ? result.json.profiles as PublicProfile[] : []);
197
+ return 0;
198
+ }
199
+
200
+ if (sub === "register") {
201
+ const label = args.shift();
202
+ if (!label || args.length > 0 || confirmed || rollback) return reject(args);
203
+ const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/register", { label });
204
+ if (result.status === 0) return proxyUnreachable(result.transportError);
205
+ if (result.status !== 200) return apiError(result.json, "failed to register the current native login", result.status);
206
+ if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
207
+ else console.log(`Registered '${label}' for ${effectiveCodexHome(result.json)}.`);
208
+ return 0;
209
+ }
210
+
211
+ if (sub === "add") {
212
+ const label = args.shift();
213
+ if (!label || args.length > 0 || wantsJson || confirmed || rollback) return reject(args);
214
+ const stage = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage", {});
215
+ if (stage.status === 0) return proxyUnreachable(stage.transportError);
216
+ if (stage.status !== 200) return apiError(stage.json, "failed to prepare native login staging", stage.status);
217
+ const stageId = typeof stage.json.stageId === "string" ? stage.json.stageId : "";
218
+ const writerToken = typeof stage.json.writerToken === "string" ? stage.json.writerToken : "";
219
+ const stagingHome = typeof stage.json.stagingCodexHome === "string" ? stage.json.stagingCodexHome : "";
220
+ const leaseExpiresAt = typeof stage.json.leaseExpiresAt === "number" ? stage.json.leaseExpiresAt : 0;
221
+ const heartbeatIntervalMs = typeof stage.json.heartbeatIntervalMs === "number"
222
+ ? Math.min(5 * 60_000, Math.max(deps.stageHeartbeatIntervalMinMs ?? 5_000, stage.json.heartbeatIntervalMs))
223
+ : 60_000;
224
+ const stageLeaseClock = deps.stageLeaseClock ?? systemStageLeaseClock;
225
+ const stagedEffectiveHome = effectiveCodexHome(stage.json);
226
+ let exitCode = 1;
227
+ let finished = false;
228
+ let leaseLost = false;
229
+ try {
230
+ if (!stageId || !writerToken || !isAbsolute(stagingHome) || leaseExpiresAt <= stageLeaseClock.now()) {
231
+ throw new Error("The proxy returned an invalid staging session.");
232
+ }
233
+ console.error(`Effective CODEX_HOME: ${stagedEffectiveHome}`);
234
+ console.error(`Starting official Codex login in restricted staging home: ${stagingHome}`);
235
+ const child = deps.spawnCodexLoginImpl
236
+ ? deps.spawnCodexLoginImpl(stagingHome)
237
+ : deps.runCodexLoginImpl
238
+ ? { exited: deps.runCodexLoginImpl(stagingHome), kill: () => {} }
239
+ : spawnOfficialCodexLogin(stagingHome);
240
+ const heartbeatAbort = new AbortController();
241
+ const heartbeat = maintainStageLease(
242
+ deps,
243
+ baseUrl,
244
+ stageId,
245
+ writerToken,
246
+ leaseExpiresAt,
247
+ heartbeatIntervalMs,
248
+ child,
249
+ heartbeatAbort.signal,
250
+ stageLeaseClock,
251
+ );
252
+ try {
253
+ exitCode = await child.exited;
254
+ } finally {
255
+ heartbeatAbort.abort();
256
+ leaseLost = (await heartbeat).lost;
257
+ }
258
+ if (leaseLost) throw new Error("The native-login staging lease was lost before login completed.");
259
+ if (exitCode !== 0) throw new Error("Official Codex login did not complete successfully.");
260
+ const finish = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage/finish", { stageId, writerToken, label });
261
+ if (finish.status === 0) return proxyUnreachable(finish.transportError);
262
+ if (finish.status !== 200) return apiError(finish.json, "failed to encrypt the staged native login", finish.status);
263
+ finished = true;
264
+ console.log(`Added encrypted native profile '${label}' for ${effectiveCodexHome(finish.json)}.`);
265
+ return 0;
266
+ } catch (error) {
267
+ console.error(`Error: ${error instanceof Error ? error.message : "Official Codex login failed."}`);
268
+ return exitCode === 0 ? 1 : exitCode;
269
+ } finally {
270
+ if (stageId && !finished) {
271
+ const cleanup = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/stage/cancel", { stageId, writerToken });
272
+ if (cleanup.status !== 200 || cleanup.json.plaintextMayRemain === true) {
273
+ console.error("Error: the proxy could not confirm native-login staging cleanup; run account main doctor.");
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+ if (sub === "switch") {
280
+ const target = args.shift();
281
+ if (!target || args.length > 0 || rollback || !confirmed) {
282
+ if (!confirmed) console.error("Close Codex App/CLI, then pass --yes to confirm it is stopped.");
283
+ return reject(args);
284
+ }
285
+ const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/switch", { target, confirmedStopped: true });
286
+ if (result.status === 0) return proxyUnreachable(result.transportError);
287
+ if (result.status !== 200) return apiError(result.json, "failed to switch the native login", result.status);
288
+ if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
289
+ else {
290
+ const profile = result.json.activeProfile as PublicProfile | undefined;
291
+ console.log(`Native Codex login for ${effectiveCodexHome(result.json)} is now '${profile?.label ?? target}'. Restart Codex App/CLI before continuing.`);
292
+ }
293
+ return 0;
294
+ }
295
+
296
+ if (sub === "recover") {
297
+ if (args.length > 0 || (rollback && !confirmed)) {
298
+ if (rollback && !confirmed) console.error("--rollback changes the native login and requires --yes.");
299
+ return reject(args);
300
+ }
301
+ const result = await apiJson(deps, baseUrl, "POST", "/api/native-main-profiles/recover", rollback
302
+ ? { rollback: true, confirmedStopped: true }
303
+ : { rollback: false });
304
+ if (result.status === 0) return proxyUnreachable(result.transportError);
305
+ if (result.status !== 200) return apiError(result.json, "failed to recover the native-profile transaction", result.status);
306
+ if (wantsJson) console.log(JSON.stringify(result.json, null, 2));
307
+ else {
308
+ const home = effectiveCodexHome(result.json);
309
+ console.log(result.json.recovered === true
310
+ ? `Recovery completed for ${home}: ${String(result.json.action ?? "converged")}.`
311
+ : `No recovery journal is pending for ${home}.`);
312
+ }
313
+ return 0;
314
+ }
315
+
316
+ return reject(args);
317
+ }
@@ -2,7 +2,20 @@
2
2
  import { loadConfig } from "../config";
3
3
  import { providerCodexAccountMode } from "../providers/registry";
4
4
  import type { OcxConfig } from "../types";
5
- import { cmdAddKey, cmdAlias, cmdAutoSwitch, cmdClearCooldown, cmdRefresh, cmdRemove } from "./account-extended";
5
+ import {
6
+ cmdAddKey,
7
+ cmdAlias,
8
+ cmdAutoSwitch,
9
+ cmdClearCooldown,
10
+ cmdImport,
11
+ cmdPause,
12
+ cmdPauseExhausted,
13
+ cmdPriority,
14
+ cmdRefresh,
15
+ cmdRemove,
16
+ cmdSticky,
17
+ cmdStrategy,
18
+ } from "./account-extended";
6
19
  import { apiError, apiJson, classifyAccount, fetchRows, proxyUnreachable, resolveBaseUrl, type AccountDeps, type AccountRow, type AccountType, type ApiResult }
7
20
  from "./account-api";
8
21
 
@@ -16,19 +29,27 @@ const MAIN_CODEX_ID = "__main__";
16
29
  const REPLACEMENT_STYLE_OAUTH = new Set(["kiro"]);
17
30
 
18
31
  const ACCOUNT_USAGE = `Usage:
19
- ocx account list [provider] [--json] [--all]
32
+ ocx account list [provider] [--json] [--all] [--quota [--refresh]]
20
33
  ocx account current <provider> [--json]
21
34
  ocx account use <provider> <account-or-key-id|main> [--json]
22
35
  ocx account refresh <provider> [--json]
23
36
  ocx account auto-switch <provider> <on|off|status|threshold <0-100>> [--json]
24
37
  ocx account alias <provider> <account-or-key-id> <display-name|-> [--json]
38
+ ocx account priority <provider> <account-id|main> [<-100..100|first|earlier|normal|later|last|reset>] [--json]
39
+ ocx account pause <provider> <account-id|main> [--json]
40
+ ocx account resume <provider> <account-id|main> [--json]
41
+ ocx account pause-exhausted <provider> [--json]
42
+ ocx account strategy <provider> [<quota|round-robin|fill-first>] [--json]
43
+ ocx account sticky <provider> [<1-100>] [--json]
25
44
  ocx account remove <provider> <account-or-key-id|main> --yes [--json]
26
45
  ocx account clear-cooldown <provider> <account-id|main> [--json]
27
46
  ocx account add-key <provider> [--label <label>] [--json]
47
+ ocx account import <provider> --format <format> (--file <path>|--stdin) [--json]
28
48
  ocx account login <provider> [--id <account-id>] [--reauth] [--code -] [--no-wait] [--json]
29
49
  ocx account code <provider> [--flow <flow-id>] [--json] (reads the code from stdin)
30
50
  ocx account cancel <provider> [--flow <flow-id>] [--json]
31
51
  ocx account reset-credits <account-id|main> [--consume --yes] [--json]
52
+ ocx account main <doctor|list|register|add|switch|recover> ...
32
53
 
33
54
  List and switch provider accounts and API-key pools (masked output only).
34
55
  'main' selects the Codex App login for the openai account pool.`;
@@ -61,16 +82,53 @@ function displayId(id: string): string {
61
82
 
62
83
  function statusText(row: AccountRow): string {
63
84
  const parts: string[] = [];
85
+ // `paused` leads, and does NOT replace `selected`. A paused-but-selected account is the
86
+ // state an operator most needs named -- requests route to it while the pool believes it is
87
+ // held out -- so printing only one of the two would hide exactly the confusing case (#2703).
88
+ if (row.paused) parts.push("paused");
64
89
  if (row.active) parts.push(row.type === "codex" ? "selected" : "active");
65
90
  if (row.needsReauth) parts.push("needs-reauth");
66
91
  return parts.join(" ");
67
92
  }
68
93
 
69
- export function formatAccountTable(rows: AccountRow[]): string {
70
- const header = ["PROVIDER", "TYPE", "ID", "PLAN/LABEL", "STATUS"];
94
+ /** Signed so the sort direction reads off the column; "-" where ordering does not apply. */
95
+ function priorityText(row: AccountRow): string {
96
+ if (row.priority === undefined) return "-";
97
+ return row.priority > 0 ? `+${row.priority}` : String(row.priority);
98
+ }
99
+
100
+ /**
101
+ * Compact per-account quota for the opt-in QUOTA column: the two windows an operator actually
102
+ * decides on before a long session. The full breakdown stays in `--json`.
103
+ */
104
+ function quotaText(row: AccountRow): string {
105
+ if ((row as { quotaUnavailable?: boolean }).quotaUnavailable) return "unavailable";
106
+ const quota = row.quota;
107
+ if (!quota) return "-";
108
+ const parts: string[] = [];
109
+ // Two spellings reach this DTO: the per-account provider probe reports `fiveHourPercent`,
110
+ // while the Codex pool reports the same idea as `shortPercent`.
111
+ const short = quota.fiveHourPercent ?? quota.shortPercent;
112
+ if (typeof short === "number") parts.push(`5h ${short}%`);
113
+ if (typeof quota.weeklyPercent === "number") parts.push(`wk ${quota.weeklyPercent}%`);
114
+ return parts.length > 0 ? parts.join(" ") : "-";
115
+ }
116
+
117
+ export function formatAccountTable(rows: AccountRow[], withQuota = false): string {
118
+ const header = ["PROVIDER", "TYPE", "ID", "PLAN/LABEL", "PRIORITY", "STATUS"];
119
+ if (withQuota) header.push("QUOTA");
71
120
  const data = rows.map(r => {
72
121
  const keyLabel = r.masked && r.label !== r.masked ? `${r.masked} (${r.label})` : r.masked;
73
- return [r.provider, r.type, displayId(r.id), r.type === "api-key" ? keyLabel ?? "-" : r.label ?? "-", statusText(r)];
122
+ const cols = [
123
+ r.provider,
124
+ r.type,
125
+ displayId(r.id),
126
+ r.type === "api-key" ? keyLabel ?? "-" : r.label ?? "-",
127
+ priorityText(r),
128
+ statusText(r),
129
+ ];
130
+ if (withQuota) cols.push(quotaText(r));
131
+ return cols;
74
132
  });
75
133
  const widths = header.map((h, i) => Math.max(h.length, ...data.map(d => d[i]!.length)));
76
134
  const line = (cols: string[]) => cols.map((c, i) => c.padEnd(widths[i]!)).join(" ").trimEnd();
@@ -80,6 +138,10 @@ export function formatAccountTable(rows: AccountRow[]): string {
80
138
  async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
81
139
  const wantsJson = consumeFlag(rest, "--json");
82
140
  const showAll = consumeFlag(rest, "--all");
141
+ // Opt-in: the server probes the upstream once per stored credential, so the default listing
142
+ // stays a cheap local read (#2566). --refresh bypasses the server-side TTL.
143
+ const wantsQuota = consumeFlag(rest, "--quota");
144
+ const refreshQuota = consumeFlag(rest, "--refresh");
83
145
  const name = rest.shift();
84
146
  const leftover = leftoverArgsError(rest);
85
147
  if (leftover) {
@@ -110,8 +172,8 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
110
172
  };
111
173
  push("openai", "codex");
112
174
  const providersRes = await apiJson(deps, baseUrl, "GET", "/api/oauth/providers");
113
- if (providersRes.status === 0) return proxyUnreachable();
114
- if (providersRes.status !== 200) return apiError(providersRes.json, "failed to list OAuth providers");
175
+ if (providersRes.status === 0) return proxyUnreachable(providersRes.transportError);
176
+ if (providersRes.status !== 200) return apiError(providersRes.json, "failed to list OAuth providers", providersRes.status);
115
177
  if (Array.isArray(providersRes.json.providers)) {
116
178
  for (const p of providersRes.json.providers) {
117
179
  if (typeof p === "string") push(p, "live-oauth-list");
@@ -123,10 +185,11 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
123
185
  const rows: AccountRow[] = [];
124
186
  const notes: string[] = [];
125
187
  for (const t of targets) {
126
- const r = await fetchRows(deps, baseUrl, t.name, t.type);
127
- if (r.networkDown) return proxyUnreachable();
188
+ const r = await fetchRows(deps, baseUrl, t.name, t.type, wantsQuota ? { refresh: refreshQuota } : undefined);
189
+ if (r.networkDown) return proxyUnreachable(r.transportError);
128
190
  if (r.errorJson) {
129
- if (name) return apiError(r.errorJson, `failed to list ${t.name}`);
191
+ if (name) return apiError(r.errorJson, `failed to list ${t.name}`, r.status);
192
+
130
193
  const errorText = typeof r.errorJson.error === "string" ? r.errorJson.error : "";
131
194
  const skipUnknownKey = t.type === "api-key"
132
195
  && r.status === 404
@@ -136,7 +199,7 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
136
199
  && r.status === 400
137
200
  && errorText.includes("unknown oauth provider");
138
201
  if (skipUnknownKey || skipConfigOAuth) continue;
139
- return apiError(r.errorJson, `failed to list ${t.name}`);
202
+ return apiError(r.errorJson, `failed to list ${t.name}`, r.status);
140
203
  }
141
204
  if (r.rows.length === 0) {
142
205
  if (showAll) notes.push(`${t.name}: no stored accounts or keys`);
@@ -158,7 +221,7 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
158
221
  console.log(JSON.stringify({ accounts: rows, notes }, null, 2));
159
222
  return 0;
160
223
  }
161
- if (rows.length > 0) console.log(formatAccountTable(rows));
224
+ if (rows.length > 0) console.log(formatAccountTable(rows, wantsQuota));
162
225
  for (const n of notes) console.log(n);
163
226
  if (rows.length === 0 && notes.length === 0) console.log("No stored accounts or keys.");
164
227
  return 0;
@@ -182,8 +245,8 @@ async function cmdCurrent(rest: string[], deps: AccountDeps): Promise<number> {
182
245
  const baseUrl = await resolveBaseUrl(deps);
183
246
  if (!baseUrl) return proxyUnreachable();
184
247
  const r = await fetchRows(deps, baseUrl, name, c.type);
185
- if (r.networkDown) return proxyUnreachable();
186
- if (r.errorJson) return apiError(r.errorJson, `failed to read ${name}`);
248
+ if (r.networkDown) return proxyUnreachable(r.transportError);
249
+ if (r.errorJson) return apiError(r.errorJson, `failed to read ${name}`, r.status);
187
250
 
188
251
  const activeRow = r.rows.find(row => row.active) ?? null;
189
252
  if (wantsJson) {
@@ -237,15 +300,17 @@ async function cmdUse(rest: string[], deps: AccountDeps): Promise<number> {
237
300
  activeId = id;
238
301
  res = await apiJson(deps, baseUrl, "PUT", "/api/providers/keys/active", { name, id });
239
302
  }
240
- if (res.status === 0) return proxyUnreachable();
241
- if (res.status !== 200) return apiError(res.json, `failed to switch ${name}`);
303
+ if (res.status === 0) return proxyUnreachable(res.transportError);
304
+ if (res.status !== 200) return apiError(res.json, `failed to switch ${name}`, res.status);
242
305
 
243
306
  if (wantsJson) console.log(JSON.stringify({ ok: true, provider: name, type: c.type, activeId }, null, 2));
244
307
  else console.log(`${name}: active ${c.type === "api-key" ? "key" : "account"} is now ${displayId(activeId)}`);
245
308
  if (c.type === "codex") {
246
- console.error("Applies to the next request after clearing existing pool affinity; in-flight requests keep their captured account.");
247
- console.error("Note: pool strategy, quota/cooldown/reauthentication state, and failure recovery may later select another eligible account.");
248
- console.error("Conversation context is replayed after account changes, but the provider-side prompt cache may be cold.");
309
+ console.error("Takes effect immediately; running threads move on their next request, and in-flight requests keep the account they captured.");
310
+ const active = await apiJson(deps, baseUrl, "GET", "/api/codex-auth/active");
311
+ if (active.status === 200 && typeof active.json.autoSwitchThreshold === "number" && active.json.autoSwitchThreshold > 0) {
312
+ console.error(`Note: auto-switch (threshold ${active.json.autoSwitchThreshold}%) may override this pin.`);
313
+ }
249
314
  }
250
315
  return 0;
251
316
  }
@@ -259,9 +324,22 @@ export async function cmdAccount(args: string[], deps: AccountDeps = {}): Promis
259
324
  if (sub === "refresh") return await cmdRefresh(rest, deps);
260
325
  if (sub === "auto-switch") return await cmdAutoSwitch(rest, deps);
261
326
  if (sub === "alias" || sub === "rename") return await cmdAlias(rest, deps);
327
+ if (sub === "priority") return await cmdPriority(rest, deps);
328
+ // #2702: the server routes existed and only the CLI caller was missing, so these were
329
+ // dashboard-only capabilities.
330
+ if (sub === "pause") return await cmdPause(rest, deps, true);
331
+ if (sub === "resume") return await cmdPause(rest, deps, false);
332
+ if (sub === "pause-exhausted") return await cmdPauseExhausted(rest, deps);
333
+ if (sub === "strategy") return await cmdStrategy(rest, deps);
334
+ if (sub === "sticky") return await cmdSticky(rest, deps);
262
335
  if (sub === "remove") return await cmdRemove(rest, deps);
263
336
  if (sub === "clear-cooldown") return await cmdClearCooldown(rest, deps);
264
337
  if (sub === "add-key") return await cmdAddKey(rest, deps);
338
+ if (sub === "import") return await cmdImport(rest, deps);
339
+ if (sub === "main") {
340
+ const { cmdNativeMainAccount } = await import("./account-main");
341
+ return await cmdNativeMainAccount(rest, deps);
342
+ }
265
343
  if (["login", "reauth", "code", "cancel", "reset-credits"].includes(sub ?? "")) {
266
344
  const { handleAccountAuthCommand } = await import("./account-auth");
267
345
  return await handleAccountAuthCommand(sub!, rest, deps) ?? 1;
package/src/cli/agent.ts CHANGED
@@ -13,6 +13,13 @@ import {
13
13
  type RuntimeApiDeps,
14
14
  } from "./runtime-api";
15
15
 
16
+ interface WebSearchModelOption {
17
+ value: string;
18
+ model: string;
19
+ backend: "openai" | "anthropic";
20
+ authSlot?: boolean;
21
+ }
22
+
16
23
  const USAGE = `Usage:
17
24
  ocx agent [status] [--json]
18
25
  ocx agent injection <status|set> [--model <id|->] [--effort <level|->]
@@ -20,8 +27,10 @@ const USAGE = `Usage:
20
27
  ocx agent effort <status|set> [--main <level|->] [--subagent <level|->] [--json]
21
28
  ocx agent subagents <status|set|clear> [model,model...] [--json]
22
29
  ocx agent fallback <status|set|clear> [model,model...] [--poll-ms <5000-600000>] [--json]
23
- ocx agent sidecar <status|web|vision> [--model <id|->] [--backend <openai|anthropic|->]
24
- [--reasoning <level>] [--max-descriptions <n>] [--json]`;
30
+ ocx agent sidecar <status|web|vision> [--list] [--model <id|->]
31
+ [--backend web:<openai|anthropic|xai|gemini|exa|-> vision:<openai|anthropic|routed|->]
32
+ [--reasoning <level>] [--max-descriptions <n>] [--json]
33
+ ocx agent request-user-input [on|off] [--json]`;
25
34
 
26
35
  function clearable(value: string | undefined): string | null | undefined {
27
36
  return value === "-" ? null : value;
@@ -152,6 +161,29 @@ async function sidecar(argv: string[], deps: RuntimeApiDeps): Promise<void> {
152
161
  return;
153
162
  }
154
163
  if (section !== "web" && section !== "vision") throw new CliUsageError("sidecar must be web, vision, or status", USAGE);
164
+ // --list must be consumed BEFORE rejectArgs sees it. It prints the server's
165
+ // candidate set — the exact list the GUI picker shows (#2188): the server
166
+ // computes it once and every surface consumes it, so the CLI cannot drift.
167
+ const wantsList = takeFlag(args, "--list");
168
+ if (wantsList) {
169
+ rejectArgs(args, USAGE);
170
+ const settings = await runtimeRequest("/api/sidecar-settings", {}, deps) as {
171
+ webSearchModels?: WebSearchModelOption[];
172
+ visionModels?: Array<{ value: string; backend?: string; baseline?: boolean }>;
173
+ };
174
+ if (section === "web") {
175
+ const options = settings.webSearchModels ?? [];
176
+ printData(options, wantsJson, options.length === 0
177
+ ? ["no runnable web-search sidecar models (log in to ChatGPT or Anthropic)"]
178
+ : options.map(option => `${option.value} [${option.backend}]${option.authSlot ? " (auth slot)" : ""}`));
179
+ } else {
180
+ const options = settings.visionModels ?? [];
181
+ printData(options, wantsJson, options.length === 0
182
+ ? ["no eligible vision describers"]
183
+ : options.map(option => `${option.value}${option.backend ? ` [${option.backend}]` : ""}${option.baseline ? " (baseline)" : ""}`));
184
+ }
185
+ return;
186
+ }
155
187
  const model = takeOption(args, "--model");
156
188
  const backend = takeOption(args, "--backend");
157
189
  const reasoning = takeOption(args, "--reasoning");
@@ -163,6 +195,19 @@ async function sidecar(argv: string[], deps: RuntimeApiDeps): Promise<void> {
163
195
  if (reasoning !== undefined) settings.reasoning = reasoning;
164
196
  if (maxDescriptionsPerTurn !== undefined) settings.maxDescriptionsPerTurn = maxDescriptionsPerTurn;
165
197
  if (Object.keys(settings).length === 0) throw new CliUsageError("at least one sidecar option is required", USAGE);
198
+ if (section === "web" && model !== undefined && model !== "-") {
199
+ const offered = await runtimeRequest("/api/sidecar-settings", {}, deps) as {
200
+ webSearchModels?: WebSearchModelOption[];
201
+ };
202
+ const requestedBackend = backend === "-" ? "openai" : backend;
203
+ const option = offered.webSearchModels?.find(candidate =>
204
+ (candidate.value === model || candidate.model === model)
205
+ && (requestedBackend === undefined || candidate.backend === requestedBackend));
206
+ if (option) {
207
+ settings.model = option.model;
208
+ if (backend !== "-") settings.backend = option.backend;
209
+ }
210
+ }
166
211
  const body = section === "web" ? { webSearch: settings } : { vision: settings };
167
212
  const result = await runtimeRequest("/api/sidecar-settings", { method: "PUT", body: JSON.stringify(body) }, deps);
168
213
  printData(result, wantsJson, [`${section} sidecar settings updated.`]);
@@ -177,6 +222,12 @@ export async function handleAgentCommand(argv: string[], deps: RuntimeApiDeps =
177
222
  else if (sub === "subagents" || sub === "roster") await subagents(rest, deps);
178
223
  else if (sub === "fallback") await fallback(rest, deps);
179
224
  else if (sub === "sidecar") await sidecar(rest, deps);
225
+ // Lives here rather than as a top-level verb because it is an agent-behavior feature flag:
226
+ // it controls whether default mode may ask the operator a question mid-task.
227
+ else if (sub === "request-user-input") {
228
+ const { requestUserInputAction } = await import("./inspect");
229
+ await requestUserInputAction(rest, deps);
230
+ }
180
231
  else throw new CliUsageError(`unknown agent command ${sub}`, USAGE);
181
232
  });
182
233
  }