@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
package/src/cli/lab.ts ADDED
@@ -0,0 +1,607 @@
1
+ /**
2
+ * `ocx lab` — Compatibility Lab inspection and explicit CL-08 automation controls.
3
+ *
4
+ * Read commands use the local SQLite projection. Automation mutations and manual runs are
5
+ * explicit operator actions; read commands never start probes or scheduler ticks.
6
+ */
7
+ import { getConfigDir, readConfigDiagnostics } from "../config";
8
+ import {
9
+ ARTIFACT_CLASSES,
10
+ EVIDENCE_LAYERS,
11
+ EVENT_KINDS,
12
+ EXECUTION_MODES,
13
+ OUTCOMES,
14
+ VERDICTS,
15
+ type ArtifactClass,
16
+ type CompatibilityVerdict,
17
+ type EvidenceLayer,
18
+ type ExecutionMode,
19
+ type LabEventKind,
20
+ type ObservationOutcome,
21
+ } from "../lab/constants";
22
+ import {
23
+ InvalidCursorError,
24
+ LabProjectionIncompatibleError,
25
+ LabProjectionUnavailableError,
26
+ queryLabArtifactByDigest,
27
+ queryLabArtifacts,
28
+ queryLabCatalogEntries,
29
+ queryLabEventById,
30
+ queryLabEvents,
31
+ queryLabObservations,
32
+ queryLabStatus,
33
+ queryLabSubjectById,
34
+ queryLabSubjects,
35
+ queryLabVerdicts,
36
+ queryPassiveProductionSignals,
37
+ type PassiveProductionQueryResultV1,
38
+ } from "../lab/query";
39
+ import { isLabRouteSubjectId } from "../usage/log";
40
+ import {
41
+ CliUsageError,
42
+ RuntimeApiError,
43
+ printData,
44
+ rejectArgs,
45
+ runCliAction,
46
+ takeFlag,
47
+ takeIntegerOption,
48
+ takeOption,
49
+ } from "./runtime-api";
50
+ import {
51
+ buildLabAutomationStatus,
52
+ enqueueManualLabRun,
53
+ reconcileLabAutomationQueue,
54
+ setLabAutomationDispatchDeps,
55
+ startLabAutomationScheduler,
56
+ stopLabAutomationScheduler,
57
+ } from "../lab/automation/orchestrator";
58
+ import { loadLabAutomationState } from "../lab/automation/persistence";
59
+ import {
60
+ loadLabAutomationConfig,
61
+ saveLabAutomationPolicyConfig,
62
+ } from "../lab/automation/config-persistence";
63
+ import { planManualLabRun } from "../lab/automation/planner";
64
+ import { listLabAutomationRuns } from "../lab/automation/runs-query";
65
+ import { LabAutomationError, type LabAutomationLayer } from "../lab/automation/types";
66
+ import { createProductionLabRouteExecutor } from "../lib/lab-live-route-production";
67
+ import {
68
+ exportLocalPublicEvidence,
69
+ importCommunityEvidenceFile,
70
+ listCommunityEvidenceContext,
71
+ previewLocalPublicEvidence,
72
+ verifyPublicEvidenceFile,
73
+ type PublicVerificationSummaryV1,
74
+ } from "../lab/public";
75
+
76
+ const USAGE = `Usage:
77
+ ocx lab status [--json]
78
+ ocx lab production-signals --subject <id> [--limit <n>] [--json]
79
+ ocx lab verdicts [--subject <id>] [--layer <layer>] [--suite <id>] [--verdict <v>] [--from <ms>] [--to <ms>] [--limit <n>] [--cursor <c>] [--json]
80
+ ocx lab subjects [--kind <kind>] [--limit <n>] [--cursor <c>] [--json]
81
+ ocx lab subject <subjectId> [--json]
82
+ ocx lab observations [--subject <id>] [--layer <layer>] [--suite <id>] [--scenario <id>] [--outcome <o>] [--execution-mode <m>] [--from <ms>] [--to <ms>] [--limit <n>] [--cursor <c>] [--json]
83
+ ocx lab events [--event-kind <k>] [--subject <id>] [--from <ms>] [--to <ms>] [--excluded <true|false>] [--limit <n>] [--cursor <c>] [--json]
84
+ ocx lab event <eventId> [--json]
85
+ ocx lab artifacts [--status <s>] [--artifact-class <c>] [--limit <n>] [--cursor <c>] [--json]
86
+ ocx lab artifact <digest> [--json]
87
+ ocx lab catalog [--layer <layer>] [--suite <id>] [--json]
88
+ ocx lab public preview --event <eventId> [--event <eventId> ...] [--json]
89
+ ocx lab public export --event <eventId> [--event <eventId> ...] [--json]
90
+ ocx lab public verify --file <bundle.json> [--json]
91
+ ocx lab public import --file <bundle.json> [--json]
92
+ ocx lab public community [--json]
93
+ ocx lab automation status [--json]
94
+ ocx lab automation enable [--protocol] [--live] [--json]
95
+ ocx lab automation disable [--json]
96
+ ocx lab automation runs [--limit <n>] [--cursor <c>] [--json]
97
+ ocx lab run --layer <layer> --scenario <id> [--provider <name>] [--model <id>] [--json]`;
98
+
99
+ const ARTIFACT_STATUSES = ["present", "corrupt", "purged_unavailable"] as const;
100
+ type ArtifactStatus = (typeof ARTIFACT_STATUSES)[number];
101
+
102
+ export interface LabCliDeps {
103
+ configDir?: string;
104
+ }
105
+
106
+ class LabStateError extends RuntimeApiError {
107
+ constructor(message: string) {
108
+ super(message, 503, null);
109
+ this.name = "LabStateError";
110
+ }
111
+ }
112
+
113
+ function labErrorMessage(err: unknown): string {
114
+ if (err instanceof LabProjectionUnavailableError) return "lab projection is not available";
115
+ if (err instanceof LabProjectionIncompatibleError) return "lab projection schema or spec version is incompatible";
116
+ if (err instanceof InvalidCursorError) return "invalid cursor";
117
+ if (err instanceof LabAutomationError && err.code === "invalid_cursor") return "invalid cursor";
118
+ return "lab read failed";
119
+ }
120
+
121
+ function takeEnumOption<T extends string>(
122
+ args: string[],
123
+ flag: string,
124
+ values: readonly T[],
125
+ message: string,
126
+ ): T | undefined {
127
+ const raw = takeOption(args, flag);
128
+ if (raw === undefined) return undefined;
129
+ if (!(values as readonly string[]).includes(raw)) {
130
+ throw new CliUsageError(message, USAGE);
131
+ }
132
+ return raw as T;
133
+ }
134
+
135
+ function assertRange(from: number | undefined, to: number | undefined): void {
136
+ if (from !== undefined && to !== undefined && from > to) {
137
+ throw new CliUsageError("--from must not be greater than --to", USAGE);
138
+ }
139
+ }
140
+
141
+ function appendPaginationHint(
142
+ lines: string[],
143
+ page: { hasMore: boolean; nextCursor?: string | null },
144
+ ): void {
145
+ if (page.hasMore) lines.push(`(more available; pass --cursor ${page.nextCursor ?? ""})`);
146
+ }
147
+
148
+ function statusSummary(status: ReturnType<typeof queryLabStatus>): string[] {
149
+ if (!status.projectionAvailable) {
150
+ if (status.projectionIncompatible) return ["Lab projection: incompatible"];
151
+ return ["Lab projection: unavailable"];
152
+ }
153
+ return [
154
+ "Lab projection: available",
155
+ `SQLite schema: ${status.sqliteSchemaVersion}`,
156
+ `Projection spec: ${status.projectionSpecVersion}`,
157
+ `Built at: ${status.builtAtMs}`,
158
+ `Events: ${status.eventCount} | Subjects: ${status.subjectCount} | Observations: ${status.observationCount}`,
159
+ `Claims: ${status.claimCount} | Verdicts: ${status.verdictCount} | Artifacts: ${status.artifactCount}`,
160
+ `Corruption rows: ${status.corruptionCount}`,
161
+ ];
162
+ }
163
+
164
+ function verdictLines(page: ReturnType<typeof queryLabVerdicts>): string[] {
165
+ const lines = page.items.map((v) =>
166
+ `${v.verdict} ${v.evidenceLayer} ${v.suiteId} subject=${v.subjectId} asOf=${v.asOf}`,
167
+ );
168
+ appendPaginationHint(lines, page);
169
+ return lines.length > 0 ? lines : ["No verdicts"];
170
+ }
171
+
172
+ function subjectListLines(page: ReturnType<typeof queryLabSubjects>): string[] {
173
+ const lines = page.items.map((s) => `${s.subjectId} (${s.subjectKind})`);
174
+ appendPaginationHint(lines, page);
175
+ return lines.length > 0 ? lines : ["No subjects"];
176
+ }
177
+
178
+ function observationLines(page: ReturnType<typeof queryLabObservations>): string[] {
179
+ const lines = page.items.map((o) =>
180
+ `${o.outcome} ${o.evidenceLayer} ${o.scenarioId} event=${o.eventId} completed=${o.completedAt}`,
181
+ );
182
+ appendPaginationHint(lines, page);
183
+ return lines.length > 0 ? lines : ["No observations"];
184
+ }
185
+
186
+ function eventListLines(page: ReturnType<typeof queryLabEvents>): string[] {
187
+ const lines = page.items.map((e) =>
188
+ `${e.eventKind} ${e.eventId} recorded=${e.recordedAt}${e.excluded ? " excluded" : ""}`,
189
+ );
190
+ appendPaginationHint(lines, page);
191
+ return lines.length > 0 ? lines : ["No events"];
192
+ }
193
+
194
+ function artifactLines(page: ReturnType<typeof queryLabArtifacts>): string[] {
195
+ const lines = page.items.map((a) => `${a.status} ${a.digest} class=${a.artifactClass ?? "unknown"}`);
196
+ appendPaginationHint(lines, page);
197
+ return lines.length > 0 ? lines : ["No artifacts"];
198
+ }
199
+
200
+ function catalogLines(scenarios: ReturnType<typeof queryLabCatalogEntries>): string[] {
201
+ const lines = scenarios.map((s) =>
202
+ `${s.evidenceLayer} ${s.suiteId} ${s.scenarioId} digest=${s.scenarioManifestDigest.slice(0, 12)}…`,
203
+ );
204
+ return lines.length > 0 ? lines : ["No catalog scenarios"];
205
+ }
206
+
207
+ function passiveProductionLines(result: PassiveProductionQueryResultV1): string[] {
208
+ const summary = result.summary;
209
+ return [
210
+ "Observed production traffic (not Lab verification)",
211
+ `Attempts: ${summary.recentProductionAttempts} | Successes: ${summary.recentSuccessfulAttempts} | Route errors: ${summary.recentRouteErrorSignals}`,
212
+ ...(summary.lastObservedProductionAttempt !== undefined
213
+ ? [`Last observed: ${summary.lastObservedProductionAttempt}`]
214
+ : []),
215
+ ];
216
+ }
217
+
218
+ function automationStatusLines(status: ReturnType<typeof buildLabAutomationStatus>): string[] {
219
+ return [
220
+ `Automation enabled: ${status.policy.enabled}`,
221
+ `Scheduler intent: ${status.policy.enabled ? "enabled" : "disabled"}`,
222
+ `Layers: protocol=${status.policy.layers.protocolConformance} live=${status.policy.layers.liveRouteCompatibility} task=${status.policy.layers.taskEffectiveness}`,
223
+ `Queued: ${status.counters.queued} | Running: ${status.counters.running}`,
224
+ `Run budget remaining: ${status.counters.remainingRunBudget} | Live request budget: ${status.counters.remainingLiveRequestBudget}`,
225
+ ];
226
+ }
227
+
228
+ function automationCliStatus(status: ReturnType<typeof buildLabAutomationStatus>) {
229
+ const { schedulerRunning: _processLocalSchedulerState, ...stable } = status;
230
+ return { ...stable, schedulerIntentEnabled: status.policy.enabled };
231
+ }
232
+
233
+ function runListLines(page: ReturnType<typeof listLabAutomationRuns>): string[] {
234
+ const lines = page.items.map((row) =>
235
+ `${row.runId} ${row.state} ${row.evidenceLayer} ${row.scenarioId} trigger=${row.trigger}`,
236
+ );
237
+ return lines.length > 0 ? lines : ["No automation runs"];
238
+ }
239
+
240
+ function takeRepeatedOptions(args: string[], flag: string): string[] {
241
+ const values: string[] = [];
242
+ while (true) {
243
+ const index = args.indexOf(flag);
244
+ if (index < 0) break;
245
+ const value = args[index + 1];
246
+ if (!value || value.startsWith("--")) {
247
+ throw new CliUsageError(`${flag} requires a value`, USAGE);
248
+ }
249
+ values.push(value);
250
+ args.splice(index, 2);
251
+ }
252
+ return values;
253
+ }
254
+
255
+ function publicPreviewLines(result: ReturnType<typeof previewLocalPublicEvidence>): string[] {
256
+ return [
257
+ `Public evidence preview: ${result.bundle.records.length} exportable record(s)`,
258
+ `Excluded: ${result.excluded.length}`,
259
+ "Unsigned local preview; no publisher key or remote publish is created.",
260
+ ];
261
+ }
262
+
263
+ function publicExportLines(result: ReturnType<typeof exportLocalPublicEvidence>): string[] {
264
+ return [
265
+ "Public evidence exported locally",
266
+ `Bundle: ${result.bundle.bundleId}`,
267
+ `Publisher: ${result.bundle.publisher.keyId}`,
268
+ `Path: ${result.stored.path}`,
269
+ `Excluded: ${result.excluded.length}`,
270
+ "No remote publish occurred.",
271
+ ];
272
+ }
273
+
274
+ function publicVerificationLines(result: PublicVerificationSummaryV1): string[] {
275
+ if (result.status !== "cryptographically_valid") {
276
+ return [
277
+ `Public evidence verification: ${result.status}`,
278
+ "Not locally verified.",
279
+ ];
280
+ }
281
+ return [
282
+ "Public evidence verification: cryptographically valid",
283
+ `Bundle: ${result.bundleId}`,
284
+ `Publisher: ${result.publisherKeyId}`,
285
+ "Not locally verified. Signature validity proves integrity/continuity only.",
286
+ ];
287
+ }
288
+
289
+ function handlePublicLabCommand(
290
+ argv: string[],
291
+ wantsJson: boolean,
292
+ configDir: string,
293
+ ): void {
294
+ const [action, ...restInput] = argv;
295
+ const rest = [...restInput];
296
+ switch (action) {
297
+ case "preview": {
298
+ const eventIds = takeRepeatedOptions(rest, "--event");
299
+ rejectArgs(rest, USAGE);
300
+ const result = previewLocalPublicEvidence({ eventIds }, configDir);
301
+ printData(result, wantsJson, publicPreviewLines(result));
302
+ return;
303
+ }
304
+ case "export": {
305
+ const eventIds = takeRepeatedOptions(rest, "--event");
306
+ rejectArgs(rest, USAGE);
307
+ const result = exportLocalPublicEvidence({ eventIds }, configDir);
308
+ printData(result, wantsJson, publicExportLines(result));
309
+ return;
310
+ }
311
+ case "verify": {
312
+ const path = takeOption(rest, "--file");
313
+ if (!path) throw new CliUsageError("public verify requires --file", USAGE);
314
+ rejectArgs(rest, USAGE);
315
+ const result = verifyPublicEvidenceFile(path);
316
+ printData(result, wantsJson, publicVerificationLines(result));
317
+ if (result.status !== "cryptographically_valid") {
318
+ throw new RuntimeApiError(`public evidence verification failed: ${result.status}`, 422, result);
319
+ }
320
+ return;
321
+ }
322
+ case "import": {
323
+ const path = takeOption(rest, "--file");
324
+ if (!path) throw new CliUsageError("public import requires --file", USAGE);
325
+ rejectArgs(rest, USAGE);
326
+ const result = importCommunityEvidenceFile(path, configDir);
327
+ printData(result, wantsJson, [
328
+ `Community evidence imported: ${result.bundleId}`,
329
+ `Publisher: ${result.publisherKeyId}`,
330
+ "Trust: community_untrusted_v1; not locally verified.",
331
+ ]);
332
+ return;
333
+ }
334
+ case "community": {
335
+ rejectArgs(rest, USAGE);
336
+ const result = listCommunityEvidenceContext(configDir);
337
+ const lines = result.evidence.length > 0
338
+ ? result.evidence.map((row) =>
339
+ `${row.bundleId} publisher=${row.publisherKeyId} active=${row.activeRecordCount} revoked=${row.revokedRecordCount}`,
340
+ )
341
+ : ["No community evidence"];
342
+ printData(result, wantsJson, [
343
+ "Community evidence (untrusted, read-only context; not locally verified)",
344
+ ...lines,
345
+ ]);
346
+ return;
347
+ }
348
+ default:
349
+ throw new CliUsageError("unknown public subcommand", USAGE);
350
+ }
351
+ }
352
+
353
+ export async function handleLabCommand(argv: string[], deps: LabCliDeps = {}): Promise<number> {
354
+ return runCliAction(async () => {
355
+ const configDir = deps.configDir ?? getConfigDir();
356
+ const argvCopy = [...argv];
357
+ const wantsJson = takeFlag(argvCopy, "--json");
358
+ const [sub = "status", ...rest] = argvCopy;
359
+
360
+ try {
361
+ switch (sub) {
362
+ case "public": {
363
+ handlePublicLabCommand(rest, wantsJson, configDir);
364
+ return;
365
+ }
366
+ case "status": {
367
+ rejectArgs(rest, USAGE);
368
+ const status = queryLabStatus(configDir);
369
+ printData(status, wantsJson, statusSummary(status));
370
+ return;
371
+ }
372
+ case "production-signals": {
373
+ const subjectId = takeOption(rest, "--subject");
374
+ const limit = takeIntegerOption(rest, "--limit", { min: 1 });
375
+ rejectArgs(rest, USAGE);
376
+ if (!subjectId) throw new CliUsageError("--subject is required", USAGE);
377
+ if (!isLabRouteSubjectId(subjectId)) {
378
+ throw new CliUsageError("--subject must be an exact Lab route subject id", USAGE);
379
+ }
380
+ const result = queryPassiveProductionSignals(subjectId, limit, configDir);
381
+ printData(result, wantsJson, passiveProductionLines(result));
382
+ return;
383
+ }
384
+ case "verdicts": {
385
+ const subjectId = takeOption(rest, "--subject");
386
+ const layer = takeEnumOption<EvidenceLayer>(
387
+ rest,
388
+ "--layer",
389
+ EVIDENCE_LAYERS,
390
+ "--layer must be a supported evidence layer",
391
+ );
392
+ const suiteId = takeOption(rest, "--suite");
393
+ const verdict = takeEnumOption<CompatibilityVerdict>(
394
+ rest,
395
+ "--verdict",
396
+ VERDICTS,
397
+ "--verdict must be a supported compatibility verdict",
398
+ );
399
+ const from = takeIntegerOption(rest, "--from", { min: 0 });
400
+ const to = takeIntegerOption(rest, "--to", { min: 0 });
401
+ assertRange(from, to);
402
+ const limit = takeIntegerOption(rest, "--limit", { min: 1 });
403
+ const cursor = takeOption(rest, "--cursor");
404
+ rejectArgs(rest, USAGE);
405
+ const page = queryLabVerdicts({ subjectId, layer, suiteId, verdict, from, to }, cursor, limit, configDir);
406
+ printData(page, wantsJson, verdictLines(page));
407
+ return;
408
+ }
409
+ case "subjects": {
410
+ const kind = takeOption(rest, "--kind");
411
+ const limit = takeIntegerOption(rest, "--limit", { min: 1 });
412
+ const cursor = takeOption(rest, "--cursor");
413
+ rejectArgs(rest, USAGE);
414
+ const page = queryLabSubjects(kind, cursor, limit, configDir);
415
+ printData(page, wantsJson, subjectListLines(page));
416
+ return;
417
+ }
418
+ case "subject": {
419
+ const subjectId = rest[0];
420
+ if (!subjectId) throw new CliUsageError("subject id required", USAGE);
421
+ rest.splice(0, 1);
422
+ rejectArgs(rest, USAGE);
423
+ const subject = queryLabSubjectById(subjectId, configDir);
424
+ if (!subject) throw new CliUsageError("unknown subject", USAGE);
425
+ printData({ subject }, wantsJson, [`Subject ${subjectId}`]);
426
+ return;
427
+ }
428
+ case "observations": {
429
+ const subjectId = takeOption(rest, "--subject");
430
+ const layer = takeEnumOption<EvidenceLayer>(rest, "--layer", EVIDENCE_LAYERS, "--layer must be a supported evidence layer");
431
+ const suiteId = takeOption(rest, "--suite");
432
+ const scenarioId = takeOption(rest, "--scenario");
433
+ const outcome = takeEnumOption<ObservationOutcome>(rest, "--outcome", OUTCOMES, "--outcome must be a supported observation outcome");
434
+ const executionMode = takeEnumOption<ExecutionMode>(rest, "--execution-mode", EXECUTION_MODES, "--execution-mode must be a supported execution mode");
435
+ const from = takeIntegerOption(rest, "--from", { min: 0 });
436
+ const to = takeIntegerOption(rest, "--to", { min: 0 });
437
+ assertRange(from, to);
438
+ const limit = takeIntegerOption(rest, "--limit", { min: 1 });
439
+ const cursor = takeOption(rest, "--cursor");
440
+ rejectArgs(rest, USAGE);
441
+ const page = queryLabObservations({ subjectId, layer, suiteId, scenarioId, outcome, executionMode, from, to }, cursor, limit, configDir);
442
+ printData(page, wantsJson, observationLines(page));
443
+ return;
444
+ }
445
+ case "events": {
446
+ const eventKind = takeEnumOption<LabEventKind>(rest, "--event-kind", EVENT_KINDS, "--event-kind must be a supported lab event kind");
447
+ const subjectId = takeOption(rest, "--subject");
448
+ const from = takeIntegerOption(rest, "--from", { min: 0 });
449
+ const to = takeIntegerOption(rest, "--to", { min: 0 });
450
+ assertRange(from, to);
451
+ const excludedRaw = takeOption(rest, "--excluded");
452
+ if (excludedRaw !== undefined && excludedRaw !== "true" && excludedRaw !== "false") {
453
+ throw new CliUsageError("--excluded must be true or false", USAGE);
454
+ }
455
+ const limit = takeIntegerOption(rest, "--limit", { min: 1 });
456
+ const cursor = takeOption(rest, "--cursor");
457
+ rejectArgs(rest, USAGE);
458
+ const page = queryLabEvents({
459
+ eventKind,
460
+ subjectId,
461
+ from,
462
+ to,
463
+ excluded: excludedRaw === "true" ? true : excludedRaw === "false" ? false : undefined,
464
+ }, cursor, limit, configDir);
465
+ printData(page, wantsJson, eventListLines(page));
466
+ return;
467
+ }
468
+ case "event": {
469
+ const eventId = rest[0];
470
+ if (!eventId) throw new CliUsageError("event id required", USAGE);
471
+ rest.splice(0, 1);
472
+ rejectArgs(rest, USAGE);
473
+ const event = queryLabEventById(eventId, configDir);
474
+ if (!event) throw new CliUsageError("unknown event", USAGE);
475
+ printData({ event }, wantsJson, [`Event ${eventId}`]);
476
+ return;
477
+ }
478
+ case "artifacts": {
479
+ const status = takeEnumOption<ArtifactStatus>(rest, "--status", ARTIFACT_STATUSES, "--status must be present, corrupt, or purged_unavailable");
480
+ const artifactClass = takeEnumOption<ArtifactClass>(rest, "--artifact-class", ARTIFACT_CLASSES, "--artifact-class must be a supported artifact class");
481
+ const limit = takeIntegerOption(rest, "--limit", { min: 1 });
482
+ const cursor = takeOption(rest, "--cursor");
483
+ rejectArgs(rest, USAGE);
484
+ const page = queryLabArtifacts({ status, artifactClass }, cursor, limit, configDir);
485
+ printData(page, wantsJson, artifactLines(page));
486
+ return;
487
+ }
488
+ case "artifact": {
489
+ const digest = rest[0];
490
+ if (!digest) throw new CliUsageError("artifact digest required", USAGE);
491
+ rest.splice(0, 1);
492
+ rejectArgs(rest, USAGE);
493
+ const artifact = queryLabArtifactByDigest(digest, configDir);
494
+ if (!artifact) throw new CliUsageError("unknown artifact", USAGE);
495
+ printData({ artifact }, wantsJson, [`Artifact ${digest}`]);
496
+ return;
497
+ }
498
+ case "catalog": {
499
+ const layer = takeEnumOption<EvidenceLayer>(rest, "--layer", EVIDENCE_LAYERS, "--layer must be a supported evidence layer");
500
+ const suiteId = takeOption(rest, "--suite");
501
+ rejectArgs(rest, USAGE);
502
+ const scenarios = queryLabCatalogEntries({ layer, suiteId });
503
+ printData({ scenarios }, wantsJson, catalogLines(scenarios));
504
+ return;
505
+ }
506
+ case "automation": {
507
+ const [automationSub = "status", ...automationRest] = rest;
508
+ switch (automationSub) {
509
+ case "status": {
510
+ rejectArgs(automationRest, USAGE);
511
+ const status = buildLabAutomationStatus(configDir);
512
+ printData(automationCliStatus(status), wantsJson, automationStatusLines(status));
513
+ return;
514
+ }
515
+ case "enable": {
516
+ const enableProtocol = takeFlag(automationRest, "--protocol");
517
+ const enableLive = takeFlag(automationRest, "--live");
518
+ rejectArgs(automationRest, USAGE);
519
+ const selectionExplicit = enableProtocol || enableLive;
520
+ const current = loadLabAutomationConfig(configDir).policy;
521
+ const policy = {
522
+ ...current,
523
+ enabled: true,
524
+ layers: {
525
+ protocolConformance: selectionExplicit ? enableProtocol : current.layers.protocolConformance,
526
+ liveRouteCompatibility: selectionExplicit ? enableLive : current.layers.liveRouteCompatibility,
527
+ taskEffectiveness: false,
528
+ },
529
+ taskEffectivenessBackgroundEnabled: false,
530
+ };
531
+ saveLabAutomationPolicyConfig(policy, configDir);
532
+ reconcileLabAutomationQueue(configDir);
533
+ startLabAutomationScheduler(configDir);
534
+ const status = buildLabAutomationStatus(configDir);
535
+ printData(automationCliStatus(status), wantsJson, automationStatusLines(status));
536
+ return;
537
+ }
538
+ case "disable": {
539
+ rejectArgs(automationRest, USAGE);
540
+ const policy = { ...loadLabAutomationConfig(configDir).policy, enabled: false };
541
+ saveLabAutomationPolicyConfig(policy, configDir);
542
+ reconcileLabAutomationQueue(configDir);
543
+ stopLabAutomationScheduler(configDir);
544
+ const status = buildLabAutomationStatus(configDir);
545
+ printData(automationCliStatus(status), wantsJson, automationStatusLines(status));
546
+ return;
547
+ }
548
+ case "runs": {
549
+ const limit = takeIntegerOption(automationRest, "--limit", { min: 1 });
550
+ const cursor = takeOption(automationRest, "--cursor");
551
+ rejectArgs(automationRest, USAGE);
552
+ const page = listLabAutomationRuns(loadLabAutomationState(configDir), limit ?? 50, cursor);
553
+ printData(page, wantsJson, runListLines(page));
554
+ return;
555
+ }
556
+ default:
557
+ throw new CliUsageError(`unknown automation subcommand: ${automationSub}`, USAGE);
558
+ }
559
+ }
560
+ case "run": {
561
+ const layer = takeEnumOption<LabAutomationLayer>(
562
+ rest,
563
+ "--layer",
564
+ ["protocol_conformance", "live_route_compatibility", "task_effectiveness"] as const,
565
+ "--layer must be protocol_conformance, live_route_compatibility, or task_effectiveness",
566
+ );
567
+ const scenarioId = takeOption(rest, "--scenario");
568
+ const providerName = takeOption(rest, "--provider");
569
+ const modelId = takeOption(rest, "--model");
570
+ rejectArgs(rest, USAGE);
571
+ if (!layer || !scenarioId) throw new CliUsageError("--layer and --scenario are required", USAGE);
572
+ const configSnapshot = readConfigDiagnostics().config;
573
+ if (layer === "live_route_compatibility") {
574
+ const loadConfig = () => configSnapshot;
575
+ setLabAutomationDispatchDeps({
576
+ configDir,
577
+ loadConfig,
578
+ routeExecutor: createProductionLabRouteExecutor({ configDir, loadConfig }),
579
+ });
580
+ }
581
+ const planned = planManualLabRun({
582
+ evidenceLayer: layer,
583
+ scenarioId,
584
+ providerName,
585
+ modelId,
586
+ config: configSnapshot,
587
+ configDir,
588
+ });
589
+ const record = await enqueueManualLabRun(planned, configDir);
590
+ if (!record) throw new CliUsageError("manual run enqueue failed", USAGE);
591
+ printData({ run: record }, wantsJson, [`Manual run ${record.runId} -> ${record.state}`]);
592
+ return;
593
+ }
594
+ default:
595
+ throw new CliUsageError(`unknown lab subcommand: ${sub}`, USAGE);
596
+ }
597
+ } catch (err) {
598
+ if (err instanceof CliUsageError || err instanceof RuntimeApiError) throw err;
599
+ if (err instanceof LabProjectionUnavailableError || err instanceof LabProjectionIncompatibleError) {
600
+ throw new LabStateError(labErrorMessage(err));
601
+ }
602
+ throw new CliUsageError(labErrorMessage(err), USAGE);
603
+ }
604
+ });
605
+ }
606
+
607
+ export const LAB_USAGE = USAGE;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Trusted facts captured by the plain-Node npm launcher before Bun auto-loads
3
+ * project dotenv files. The random proof travels in argv while the context
4
+ * travels in the environment, so a project `.env` cannot forge the pair during
5
+ * an ordinary `ocx ...` invocation.
6
+ */
7
+ export const NODE_LAUNCH_CONTEXT_ENV = "OCX_NODE_LAUNCH_CONTEXT";
8
+ export const NODE_LAUNCH_PROOF_PREFIX = "--ocx-internal-launch-proof=";
9
+
10
+ export const ANTHROPIC_PARENT_ENV_SLOTS = [
11
+ "ANTHROPIC_API_KEY",
12
+ "ANTHROPIC_AUTH_TOKEN",
13
+ "ANTHROPIC_BASE_URL",
14
+ ] as const;
15
+
16
+ export type AnthropicParentEnvSlot = typeof ANTHROPIC_PARENT_ENV_SLOTS[number];
17
+
18
+ export type TrustedNodeLaunchContext = {
19
+ anthropicEnvSlots: readonly AnthropicParentEnvSlot[];
20
+ };
21
+
22
+ let trustedContext: TrustedNodeLaunchContext | null = null;
23
+
24
+ function isLaunchProof(value: string): boolean {
25
+ return /^[A-Za-z0-9_-]{43}$/.test(value);
26
+ }
27
+
28
+ /** Consume the internal proof before normal CLI argument parsing. */
29
+ export function initializeNodeLauncherContext(
30
+ argv: string[] = process.argv,
31
+ env: NodeJS.ProcessEnv = process.env,
32
+ ): TrustedNodeLaunchContext | null {
33
+ const proofArgs: string[] = [];
34
+ for (let index = argv.length - 1; index >= 2; index -= 1) {
35
+ const value = argv[index];
36
+ if (!value?.startsWith(NODE_LAUNCH_PROOF_PREFIX)) continue;
37
+ proofArgs.push(value.slice(NODE_LAUNCH_PROOF_PREFIX.length));
38
+ argv.splice(index, 1);
39
+ }
40
+
41
+ const raw = env[NODE_LAUNCH_CONTEXT_ENV];
42
+ delete env[NODE_LAUNCH_CONTEXT_ENV];
43
+ // Older launchers used this unauthenticated marker. Never let a project
44
+ // dotenv resurrect it as a trusted provenance channel.
45
+ delete env.OCX_PRE_BUN_ANTHROPIC_ENV;
46
+ trustedContext = null;
47
+
48
+ if (proofArgs.length !== 1 || !raw || raw.length > 2048) return null;
49
+ const proof = proofArgs[0]!;
50
+ if (!isLaunchProof(proof)) return null;
51
+
52
+ try {
53
+ const parsed = JSON.parse(raw) as {
54
+ version?: unknown;
55
+ proof?: unknown;
56
+ anthropicEnvSlots?: unknown;
57
+ };
58
+ if (parsed.version !== 1 || parsed.proof !== proof || !Array.isArray(parsed.anthropicEnvSlots)) {
59
+ return null;
60
+ }
61
+ const allowed = new Set<string>(ANTHROPIC_PARENT_ENV_SLOTS);
62
+ const slots = parsed.anthropicEnvSlots.filter(
63
+ (slot): slot is AnthropicParentEnvSlot => typeof slot === "string" && allowed.has(slot),
64
+ );
65
+ if (slots.length !== parsed.anthropicEnvSlots.length || new Set(slots).size !== slots.length) {
66
+ return null;
67
+ }
68
+ trustedContext = { anthropicEnvSlots: slots };
69
+ return trustedContext;
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+
75
+ export function trustedNodeLauncherContext(): TrustedNodeLaunchContext | null {
76
+ return trustedContext;
77
+ }