@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,73 @@
1
+ import type { OcxConfig } from "../types";
2
+ import { injectClaudeAgentDefs } from "../claude/agents-inject";
3
+ import { fetchClaudeContextWindows } from "./claude";
4
+ import type { ReadinessGate } from "../server/readiness";
5
+
6
+ export interface ClaudeAgentStartupSyncDeps {
7
+ fetchContextWindows?: typeof fetchClaudeContextWindows;
8
+ injectAgentDefs?: typeof injectClaudeAgentDefs;
9
+ warn?: (message: string) => void;
10
+ }
11
+
12
+ /**
13
+ * Keep the public readiness gate pending until both startup reconciliations have settled.
14
+ *
15
+ * The Codex sync remains the authority for ready versus failed. Claude roster repair is
16
+ * deliberately best-effort (#2200), but readiness must not become observable between the
17
+ * Codex write and that repair: a service manager could otherwise launch Claude Code against
18
+ * stale `ocx-*.md` files. A small forwarding gate delays only the successful transition;
19
+ * terminal Codex failure is still published immediately.
20
+ */
21
+ export async function reconcileClientStartupBeforeReady<T>(
22
+ readinessGate: ReadinessGate,
23
+ syncCodex: (deferredGate: ReadinessGate) => Promise<T>,
24
+ syncClaudeRoster: () => Promise<unknown>,
25
+ ): Promise<T> {
26
+ let codexReady = false;
27
+ const deferredGate: ReadinessGate = {
28
+ getStatus: () => readinessGate.getStatus(),
29
+ markReady: () => { codexReady = true; },
30
+ markFailed: () => readinessGate.markFailed(),
31
+ };
32
+
33
+ const result = await syncCodex(deferredGate);
34
+ await syncClaudeRoster();
35
+ if (codexReady) readinessGate.markReady();
36
+ return result;
37
+ }
38
+
39
+ /**
40
+ * Reconcile the generated Claude Code roster after the proxy listener is live.
41
+ *
42
+ * This belongs to the owning CLI lifecycle rather than `startServer`: the latter is also a
43
+ * library/test primitive and must not mutate a developer's real `~/.claude` directory merely
44
+ * because an in-process test server was created. The live Management API supplies the same bounded
45
+ * context-window map used by `ocx claude`; failure keeps startup available and falls back to an
46
+ * unmarked roster. Disabled integrations skip discovery and prune verified-owned definitions.
47
+ */
48
+ export async function syncClaudeAgentDefsAtProxyStartup(
49
+ config: OcxConfig,
50
+ port: number,
51
+ deps: ClaudeAgentStartupSyncDeps = {},
52
+ ): Promise<string[] | null> {
53
+ const inject = deps.injectAgentDefs ?? injectClaudeAgentDefs;
54
+ const warn = deps.warn ?? (message => console.warn(message));
55
+
56
+ try {
57
+ if (config.claudeCode?.enabled === false || config.claudeCode?.injectAgents === false) {
58
+ return inject(config, {});
59
+ }
60
+
61
+ let windows: Record<string, number> = {};
62
+ try {
63
+ windows = await (deps.fetchContextWindows ?? fetchClaudeContextWindows)(config, port);
64
+ } catch {
65
+ // Startup remains best-effort. The next management mutation or `ocx claude` launch can
66
+ // restore context markers after a transient catalog/Management API failure.
67
+ }
68
+ return inject(config, windows);
69
+ } catch (error) {
70
+ warn(`⚠ Claude agent definitions could not be synced at proxy startup: ${error instanceof Error ? error.message : String(error)}`);
71
+ return null;
72
+ }
73
+ }
@@ -1,6 +1,7 @@
1
1
  import { readFileSync, writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { loadConfig, saveConfigPreservingClaudeCode } from "../config";
4
+ import { setIntegrationEnabled } from "../codex/desired-state";
4
5
  import {
5
6
  DESKTOP_FAMILIES,
6
7
  moveDesktopRoute,
@@ -10,10 +11,11 @@ import {
10
11
  type DesktopProfile,
11
12
  } from "../claude/desktop-profile";
12
13
  import { writeDesktop3pConfig, type Desktop3pConfigMode, parseDesktop3pModeArgs } from "../claude/desktop-3p";
13
- import { filterCatalogVisibleModels, desktopVisibleNativeSlugs } from "../codex/catalog";
14
+ import { filterCatalogVisibleModels, desktopVisibleNativeSlugs, nativeContextLimits } from "../codex/catalog";
14
15
  import { buildClaudeDesktopState, fetchAllModels } from "../server/management-api";
15
16
  import { findLiveProxy } from "../server/proxy-liveness";
16
- import { runtimeRequest } from "./runtime-api";
17
+ import { CliUsageError, runtimeRequest, takeJsonFlag } from "./runtime-api";
18
+ import { OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
17
19
 
18
20
  function isFamily(value: string | undefined): value is DesktopFamily {
19
21
  return !!value && (DESKTOP_FAMILIES as readonly string[]).includes(value);
@@ -23,6 +25,7 @@ function printDesktopHelp(): void {
23
25
  console.log(`Usage:
24
26
  ocx claude desktop [apply] [--static|--hybrid|--discovery-only]
25
27
  ocx claude desktop show [--json]
28
+ ocx claude desktop status [--json]
26
29
  ocx claude desktop move <provider/model> <opus|fable|sonnet|haiku> [--default]
27
30
  ocx claude desktop default <family> <provider/model|none>
28
31
  ocx claude desktop export <path|->
@@ -41,7 +44,11 @@ export async function applyProfile(
41
44
  profile: DesktopProfile,
42
45
  mode: Desktop3pConfigMode,
43
46
  deps: ApplyProfileDeps = {},
44
- ): Promise<{ ok: boolean; path: string; reason?: string }> {
47
+ ): Promise<{ ok: boolean; path: string; reason?: string; warning?: string }> {
48
+ // Explicit apply is an enable action. Persist intent before any Desktop write
49
+ // so a process crash cannot leave a gateway profile that startup immediately removes.
50
+ const desired = setIntegrationEnabled("claude-desktop", true);
51
+ if (!desired.ok) return { ok: false, path: "", reason: desired.message };
45
52
  const config = loadConfig();
46
53
  const state = await buildClaudeDesktopState(config, profile);
47
54
  config.claudeCode = { ...(config.claudeCode ?? {}), desktopProfile: state.profile };
@@ -52,7 +59,7 @@ export async function applyProfile(
52
59
  // serving process installs the map there; a local-only write leaves the
53
60
  // daemon unable to decode aliases, and the provider rejects them (400).
54
61
  const post = deps.postApplyImpl ?? (async (m: Desktop3pConfigMode, p: DesktopProfile) =>
55
- runtimeRequest<{ ok?: boolean; path?: string; error?: string }>(
62
+ runtimeRequest<{ ok?: boolean; path?: string; error?: string; saved?: boolean; warning?: string }>(
56
63
  "/api/claude-desktop/apply",
57
64
  // The daemon's config may be older than what we just saved, so the
58
65
  // profile travels with the request instead of being re-read there.
@@ -61,12 +68,26 @@ export async function applyProfile(
61
68
  try {
62
69
  const applied = await post(mode, state.profile);
63
70
  if (applied.ok === false) return { ok: false, path: applied.path ?? "", reason: applied.error ?? "daemon apply failed" };
64
- return { ok: true, path: applied.path ?? "" };
71
+ // Partial success: Desktop was written but the applied marker was not
72
+ // persisted. Pass the degradation up instead of reporting a clean apply.
73
+ const partial = (applied as { saved?: boolean; warning?: string }).saved === false;
74
+ return {
75
+ ok: true,
76
+ path: applied.path ?? "",
77
+ ...(partial ? { warning: (applied as { warning?: string }).warning ?? "applied marker was not saved" } : {}),
78
+ };
65
79
  } catch (error) {
66
80
  return { ok: false, path: "", reason: error instanceof Error ? error.message : String(error) };
67
81
  }
68
82
  }
69
83
  const allModels = await fetchAllModels(config);
84
+ // The toggle can persist OFF while fetchAllModels was awaiting (same race the
85
+ // management writers fence). Re-read persisted intent immediately before the
86
+ // writer; a lost race is a discriminated skip, not a write.
87
+ const { claudeDesktopIntegrationEnabledNow } = await import("../codex/desired-state");
88
+ if (!claudeDesktopIntegrationEnabledNow()) {
89
+ return { ok: false, path: "", reason: "desired_state_changed" };
90
+ }
70
91
  const routed = filterCatalogVisibleModels(allModels, config).map(model => ({
71
92
  provider: model.provider,
72
93
  id: model.id,
@@ -79,6 +100,7 @@ export async function applyProfile(
79
100
  config.apiKeys?.[0]?.key,
80
101
  mode,
81
102
  state.profile,
103
+ nativeContextLimits(config),
82
104
  );
83
105
  return { ok: result.written, path: result.path, reason: result.reason };
84
106
  }
@@ -97,10 +119,10 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
97
119
  const nonMode = argv.filter(arg => !["apply", "--static", "--hybrid", "--discovery-only"].includes(arg));
98
120
  if (nonMode.length > 0) {
99
121
  console.error(`알 수 없는 인자: ${nonMode.join(" ")}`);
100
- return 1;
122
+ return 2;
101
123
  }
102
124
  const parsedMode = parseDesktop3pModeArgs(legacyFlags);
103
- if ("error" in parsedMode) { console.error(parsedMode.error); return 1; }
125
+ if ("error" in parsedMode) { console.error(parsedMode.error); return 2; }
104
126
  try {
105
127
  const config = loadConfig();
106
128
  const state = await buildClaudeDesktopState(config);
@@ -111,6 +133,9 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
111
133
  return 1;
112
134
  }
113
135
  console.log(`Claude Desktop 설정을 적용했습니다: ${result.path}`);
136
+ // The write landed; only the bookkeeping marker did not. Saying nothing
137
+ // would leave the saved-vs-applied display wrong with no explanation.
138
+ if (result.warning) console.warn(`⚠️ ${result.warning}`);
114
139
  console.log("Claude Desktop을 완전히 종료한 뒤 다시 열어 주세요.");
115
140
  return 0;
116
141
  } catch (error) {
@@ -121,10 +146,29 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
121
146
 
122
147
  try {
123
148
  const config = loadConfig();
149
+ // `status` is API-backed and must NOT build local state first: the whole point of the
150
+ // route the GUI polls (/api/claude-desktop/status) is the applied-vs-desired comparison,
151
+ // including staleness, drift and health, which only the running proxy knows. `show`
152
+ // reports what this machine would write; `status` reports what is actually in effect.
153
+ if (command === "status") {
154
+ const rest = argv.slice(1);
155
+ const wantsJson = takeJsonFlag(rest);
156
+ if (rest.length > 0) throw new CliUsageError("Usage: ocx claude desktop status [--json]");
157
+ const live = await runtimeRequest<Record<string, unknown>>("/api/claude-desktop/status", {});
158
+ if (wantsJson) console.log(JSON.stringify(live, null, 2));
159
+ else {
160
+ for (const [key, value] of Object.entries(live)) {
161
+ console.log(`${key}: ${typeof value === "object" ? JSON.stringify(value) : String(value)}`);
162
+ }
163
+ }
164
+ return 0;
165
+ }
124
166
  const state = await buildClaudeDesktopState(config);
125
167
  if (command === "show") {
126
- if (argv.length > 2 || (argv[1] && argv[1] !== "--json")) throw new Error("Usage: ocx claude desktop show [--json]");
127
- if (argv[1] === "--json") console.log(JSON.stringify(state));
168
+ const rest = argv.slice(1);
169
+ const wantsJson = takeJsonFlag(rest);
170
+ if (rest.length > 0) throw new CliUsageError("Usage: ocx claude desktop show [--json]");
171
+ if (wantsJson) console.log(JSON.stringify(state));
128
172
  else {
129
173
  for (const family of DESKTOP_FAMILIES) {
130
174
  console.log(`${family.toUpperCase()}${state.profile.defaults[family] ? ` (default: ${state.profile.defaults[family]})` : ""}`);
@@ -137,7 +181,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
137
181
  }
138
182
  if (command === "move") {
139
183
  const [, route, familyRaw, ...flags] = argv;
140
- if (!route || !isFamily(familyRaw) || flags.some(flag => flag !== "--default")) throw new Error("Usage: ocx claude desktop move <route> <family> [--default]");
184
+ if (!route || !isFamily(familyRaw) || flags.some(flag => flag !== "--default")) throw new CliUsageError("Usage: ocx claude desktop move <route> <family> [--default]");
141
185
  if (!state.models.some(model => model.route === route && model.available)) throw new Error(`현재 사용할 수 없는 모델입니다: ${route}`);
142
186
  const profile = moveDesktopRoute(state.profile, route, familyRaw, flags.includes("--default"));
143
187
  config.claudeCode = { ...(config.claudeCode ?? {}), desktopProfile: profile };
@@ -147,7 +191,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
147
191
  }
148
192
  if (command === "default") {
149
193
  const [, familyRaw, routeRaw] = argv;
150
- if (!isFamily(familyRaw) || !routeRaw || argv.length !== 3) throw new Error("Usage: ocx claude desktop default <family> <route|none>");
194
+ if (!isFamily(familyRaw) || !routeRaw || argv.length !== 3) throw new CliUsageError("Usage: ocx claude desktop default <family> <route|none>");
151
195
  const route = routeRaw === "none" ? null : routeRaw;
152
196
  if (route && !state.models.some(model => model.route === route && model.available)) throw new Error(`현재 사용할 수 없는 모델입니다: ${route}`);
153
197
  const profile = setDesktopFamilyDefault(state.profile, familyRaw, route);
@@ -158,7 +202,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
158
202
  }
159
203
  if (command === "export") {
160
204
  const target = argv[1];
161
- if (!target || argv.length !== 2) throw new Error("Usage: ocx claude desktop export <path|->");
205
+ if (!target || argv.length !== 2) throw new CliUsageError("Usage: ocx claude desktop export <path|->");
162
206
  const json = JSON.stringify(state.profile, null, 2) + "\n";
163
207
  if (target === "-") process.stdout.write(json);
164
208
  else writeFileSync(resolve(target), json, { encoding: "utf8", mode: 0o600 });
@@ -167,7 +211,7 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
167
211
  if (command === "import") {
168
212
  const source = argv[1];
169
213
  const flags = argv.slice(2);
170
- if (!source || flags.some(flag => flag !== "--apply")) throw new Error("Usage: ocx claude desktop import <path> [--apply]");
214
+ if (!source || flags.some(flag => flag !== "--apply")) throw new CliUsageError("Usage: ocx claude desktop import <path> [--apply]");
171
215
  const profile = parseDesktopProfile(JSON.parse(readFileSync(resolve(source), "utf8")));
172
216
  const reconciled = (await buildClaudeDesktopState(config, profile)).profile;
173
217
  config.claudeCode = { ...(config.claudeCode ?? {}), desktopProfile: reconciled };
@@ -175,14 +219,15 @@ export async function handleClaudeDesktopCommand(argv: string[], deps: ApplyProf
175
219
  if (flags.includes("--apply")) {
176
220
  const result = await applyProfile(reconciled, "static", deps);
177
221
  if (!result.ok) { console.error(`프로필은 저장했지만 Desktop 적용에 실패했습니다: ${result.reason ?? "unknown error"}`); return 1; }
222
+ if (result.warning) console.warn(`⚠️ ${result.warning}`);
178
223
  }
179
224
  console.log("Claude Desktop 프로필을 가져왔습니다.");
180
225
  return 0;
181
226
  }
182
227
  printDesktopHelp();
183
- return 1;
228
+ return 2;
184
229
  } catch (error) {
185
230
  console.error(error instanceof Error ? error.message : String(error));
186
- return 1;
231
+ return error instanceof CliUsageError ? 2 : 1;
187
232
  }
188
233
  }
package/src/cli/claude.ts CHANGED
@@ -12,11 +12,15 @@ import { injectClaudeAgentDefs } from "../claude/agents-inject";
12
12
  import { effectiveModelEnv, resolveAutoContext } from "../claude/context-windows";
13
13
  import { refreshGatewayModelCacheFromProxy } from "../claude/gateway-cache";
14
14
  import { commandInvocation } from "../lib/win-exec";
15
+ import { isProxyAdmissionSecret } from "../server/auth-cors";
15
16
  import { findLiveProxy } from "../server/proxy-liveness";
16
17
  import type { OcxConfig } from "../types";
17
18
  import { configuredAdminToken } from "../lib/admin-secrets";
18
19
  import { PROXY_MARKER, ownAdmissionTokens, defaultAuthDetectDeps, detectClaudeAuth, type AuthDetectDeps } from "../claude/auth-detect";
19
20
  import { resolveClaudeAuthMode } from "../claude/auth-mode";
21
+ import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
22
+ import { selfLaunchArgv } from "../lib/self-launch-argv";
23
+ import { ANTHROPIC_PARENT_ENV_SLOTS, trustedNodeLauncherContext, type AnthropicParentEnvSlot } from "./launcher-context";
20
24
 
21
25
  export interface ClaudeLaunchEnv {
22
26
  [key: string]: string | undefined;
@@ -26,13 +30,43 @@ export interface ClaudeLaunchEnv {
26
30
  * Injectable IO for tests. `env` is deliberately NOT injectable: it is bound to the
27
31
  * launch base so detection and the spawned process can never disagree (audit R3-3).
28
32
  */
29
- export type ClaudeEnvDeps = { authDetect?: Omit<Partial<AuthDetectDeps>, "env" | "ownTokens"> };
33
+ export type ClaudeEnvDeps = {
34
+ authDetect?: Omit<Partial<AuthDetectDeps>, "env" | "ownTokens">;
35
+ /** Test seam; production uses the authenticated Node-launcher context. */
36
+ preBunAnthropicSlots?: readonly AnthropicParentEnvSlot[] | null;
37
+ /** Explicit unsafe opt-in from a root `--dangerously-skip-permissions` launch. */
38
+ allowRootSkipPermissions?: boolean;
39
+ };
40
+
41
+ function isClaudeLoopbackHostname(hostname: string): boolean {
42
+ const normalized = hostname.toLowerCase().replace(/\.$/, "");
43
+ return normalized === "localhost"
44
+ || normalized === "127.0.0.1"
45
+ || normalized === "::1"
46
+ || normalized === "[::1]";
47
+ }
48
+
49
+ function targetsLocalClaudeProxy(value: string | undefined, port: number): boolean {
50
+ if (!value) return false;
51
+ try {
52
+ const parsed = new URL(value);
53
+ const effectivePort = parsed.port === "" ? 80 : Number(parsed.port);
54
+ return parsed.protocol === "http:"
55
+ && isClaudeLoopbackHostname(parsed.hostname)
56
+ && effectivePort === port
57
+ && parsed.username === ""
58
+ && parsed.password === "";
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
30
63
 
31
64
  /**
32
65
  * Pure env assembly (unit-tested): never sets ANTHROPIC_API_KEY (setting both
33
66
  * token vars triggers Claude Code's auth-conflict warning, 003 E1), and never
34
- * overrides variables the user already exported, apart from stale loopback
35
- * ANTHROPIC_BASE_URL values owned by a previous opencodex launch.
67
+ * preserves Anthropic variables proven to exist in the parent Node launcher,
68
+ * apart from stale loopback ANTHROPIC_BASE_URL values owned by a previous
69
+ * opencodex launch. Unproven ambient values fail closed as project dotenv.
36
70
  */
37
71
  export function buildClaudeEnv(
38
72
  config: OcxConfig,
@@ -47,42 +81,56 @@ export function buildClaudeEnv(
47
81
  // stale marker left in place would suppress the admission key and then be removed,
48
82
  // leaving the child with no token at all (audit R2-1). It is opencodex state, never
49
83
  // user auth, so dropping it unconditionally is safe.
50
- if (env.ANTHROPIC_AUTH_TOKEN === PROXY_MARKER) delete env.ANTHROPIC_AUTH_TOKEN;
51
- // Step 1b — drop Anthropic credentials that the bundled Bun runtime synthesized from a
52
- // project `.env`/`.env.local` (issue #701). Claude Code disables claude.ai connectors the
53
- // moment either token slot is populated, so an ambient project file silently moved a
54
- // subscriber onto API billing while their OAuth login stayed healthy. The npm launcher
55
- // runs under Node, which does NOT auto-load dotenv, so it records the slots that existed
56
- // before Bun started; anything populated now but absent then came from the working
57
- // directory, not from the user. A genuine shell export is still honored, which keeps
58
- // auto-mode API-key auth working. An ABSENT marker means provenance is unknowable
59
- // (a direct `bun src/cli/index.ts` run, a test, or an older launcher), and then we
60
- // change nothing rather than guess an EMPTY marker is different: the launcher ran
61
- // and saw no pre-existing slots.
62
- const preBunSlots = base.OCX_PRE_BUN_ANTHROPIC_ENV;
63
- if (preBunSlots !== undefined) {
64
- const exported = new Set(preBunSlots.split(",").filter(name => name.length > 0));
65
- for (const name of ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"] as const) {
66
- const value = env[name];
67
- if (value !== undefined && value !== "" && !exported.has(name)) delete env[name];
68
- }
84
+ if (env.ANTHROPIC_AUTH_TOKEN?.trim() === PROXY_MARKER) delete env.ANTHROPIC_AUTH_TOKEN;
85
+ // Step 1b — drop Anthropic credentials AND destinations that Bun may have synthesized
86
+ // from a project `.env`/`.env.local`. The plain-Node launcher records genuine parent
87
+ // exports before Bun starts and pairs that context with an argv proof, so with a
88
+ // trusted context we know exactly which slots the user really exported.
89
+ //
90
+ // Without a trusted context all three slots are treated as project-controlled. An
91
+ // earlier revision of this branch preserved credentials here, reasoning that the
92
+ // destination is pinned below so a dotenv key would only ever reach the local proxy.
93
+ // That reasoning is wrong, and review caught it: `CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST`
94
+ // is only set when we own an auth token (see below, and #253 for why asserting it
95
+ // otherwise logs a subscriber out), so on a subscription launch Claude Code's
96
+ // settings.env merge can still replace `ANTHROPIC_BASE_URL` after we return. A
97
+ // preserved key then travels to that host. The repository documents that residual for
98
+ // subscription mode; it must not be widened into a credential leak.
99
+ //
100
+ // Direct `bun src/cli/index.ts` therefore loses ambient Anthropic values. That is a
101
+ // real cost to a documented entry point, and the escape hatch is the launcher: run
102
+ // through `ocx` (the published bin) and genuine shell exports are preserved by proof.
103
+ const explicitSlots = deps.preBunAnthropicSlots;
104
+ const trustedSlots = explicitSlots === undefined
105
+ ? trustedNodeLauncherContext()?.anthropicEnvSlots ?? []
106
+ : explicitSlots ?? [];
107
+ const exported = new Set<AnthropicParentEnvSlot>(trustedSlots);
108
+ for (const name of ANTHROPIC_PARENT_ENV_SLOTS) {
109
+ const value = env[name];
110
+ if (value !== undefined && value !== "" && !exported.has(name)) delete env[name];
69
111
  }
70
- // Never forward the seam itself to Claude Code.
112
+ // Never forward old or current provenance seams to Claude Code.
71
113
  delete env.OCX_PRE_BUN_ANTHROPIC_ENV;
114
+ delete env.OCX_NODE_LAUNCH_CONTEXT;
72
115
  const setDefault = (name: string, value: string | undefined) => {
73
116
  if (value === undefined || value.length === 0) return;
74
117
  if (env[name] !== undefined && env[name] !== "") return; // user wins
75
118
  env[name] = value;
76
119
  };
120
+ if (deps.allowRootSkipPermissions === true) {
121
+ setDefault("IS_SANDBOX", "1");
122
+ }
77
123
  setDefault("ANTHROPIC_BASE_URL", `http://127.0.0.1:${port}`);
78
124
  const existingBaseUrl = env.ANTHROPIC_BASE_URL;
79
125
  if (existingBaseUrl) {
80
126
  try {
81
127
  const parsed = new URL(existingBaseUrl);
82
- const isLoopback = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
83
- if (isLoopback && parsed.port !== "" && Number(parsed.port) !== port) {
128
+ const effectivePort = parsed.port === "" ? 80 : Number(parsed.port);
129
+ if (parsed.protocol === "http:"
130
+ && isClaudeLoopbackHostname(parsed.hostname)
131
+ && effectivePort !== port) {
84
132
  const replacement = `http://127.0.0.1:${port}`;
85
- console.error(`⚠ Replacing stale opencodex ANTHROPIC_BASE_URL ${existingBaseUrl} with ${replacement}.`);
133
+ console.error(`⚠ Replacing stale opencodex ANTHROPIC_BASE_URL ${parsed.origin} with ${replacement}.`);
86
134
  env.ANTHROPIC_BASE_URL = replacement;
87
135
  }
88
136
  } catch {
@@ -94,8 +142,26 @@ export function buildClaudeEnv(
94
142
  // the user's Claude login. Only inject a token when the proxy actually requires an
95
143
  // admission key; otherwise Claude Code keeps its own OAuth and sends it to us —
96
144
  // native claude models then pass through verbatim (see server/claude-messages.ts).
97
- if ((config.apiKeys?.length ?? 0) > 0) {
98
- setDefault("ANTHROPIC_AUTH_TOKEN", config.apiKeys![0].key);
145
+ const ownTokens = ownAdmissionTokens(config);
146
+ const targetsLocalProxy = targetsLocalClaudeProxy(env.ANTHROPIC_BASE_URL, port);
147
+ const inheritedApiKey = env.ANTHROPIC_API_KEY;
148
+ if (typeof inheritedApiKey === "string" && isProxyAdmissionSecret(inheritedApiKey, config)) {
149
+ delete env.ANTHROPIC_API_KEY;
150
+ }
151
+ const hasUserApiKey = Boolean(env.ANTHROPIC_API_KEY?.trim());
152
+ const inheritedAuthToken = env.ANTHROPIC_AUTH_TOKEN;
153
+ const inheritedTokenIsOurs = typeof inheritedAuthToken === "string"
154
+ && isProxyAdmissionSecret(inheritedAuthToken, config);
155
+ // system-env may have injected the proxy's admission key into the parent. A
156
+ // proof-bound external BASE_URL is still user-owned, so never let our inherited
157
+ // key follow it. A user API key also wins on a local launch; remove only the token
158
+ // values recognized by the shared proxy-admission contract and preserve every
159
+ // other token.
160
+ if (inheritedTokenIsOurs && (!targetsLocalProxy || hasUserApiKey)) {
161
+ delete env.ANTHROPIC_AUTH_TOKEN;
162
+ }
163
+ if (targetsLocalProxy && !hasUserApiKey && ownTokens.length > 0) {
164
+ setDefault("ANTHROPIC_AUTH_TOKEN", ownTokens[0]);
99
165
  }
100
166
  // Detection reads the SANITIZED launch env — the exact object spawned below — so the
101
167
  // resolver and the spawned process cannot disagree. It deliberately does NOT read the
@@ -110,11 +176,19 @@ export function buildClaudeEnv(
110
176
  ...defaultAuthDetectDeps(env as NodeJS.ProcessEnv),
111
177
  ...(deps.authDetect ?? {}),
112
178
  env: () => env as NodeJS.ProcessEnv,
113
- ownTokens: ownAdmissionTokens(config),
179
+ ownTokens,
114
180
  }));
115
- if (!env.ANTHROPIC_AUTH_TOKEN && resolved.markerMode === "proxy") {
181
+ if (!env.ANTHROPIC_AUTH_TOKEN && !hasUserApiKey && targetsLocalProxy && resolved.markerMode === "proxy") {
116
182
  env.ANTHROPIC_AUTH_TOKEN = PROXY_MARKER;
117
183
  }
184
+ const finalAuthToken = env.ANTHROPIC_AUTH_TOKEN;
185
+ const hostOwnsAuthentication = targetsLocalProxy
186
+ && !hasUserApiKey
187
+ && typeof finalAuthToken === "string"
188
+ && (
189
+ finalAuthToken.trim() === PROXY_MARKER
190
+ || isProxyAdmissionSecret(finalAuthToken, config)
191
+ );
118
192
  if (resolved.origin === "auto-unknown") {
119
193
  console.error("⚠ Claude 인증을 확인하지 못했습니다 — 구독 방식으로 진행합니다. GUI에서 인증 모드를 직접 지정하면 이 판단을 덮어쓸 수 있습니다.");
120
194
  }
@@ -136,7 +210,7 @@ export function buildClaudeEnv(
136
210
  // Claude Code 2.1.206+ also treats this as a host-auth assertion. Injecting it
137
211
  // without a host token makes a valid claude.ai subscription look logged out,
138
212
  // so the guard is only safe when opencodex actually owns authentication.
139
- if (env.ANTHROPIC_AUTH_TOKEN) {
213
+ if (hostOwnsAuthentication) {
140
214
  setDefault("CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST", "1");
141
215
  }
142
216
  // Opt-in effort forcing (devlog 136 B6): opus-shaped aliases already carry
@@ -201,11 +275,11 @@ async function ensureProxyForClaude(): Promise<number | null> {
201
275
  if (live) return live.port;
202
276
  const cfgPort = loadConfig().port;
203
277
  const pinPort = typeof cfgPort === "number" && cfgPort > 0 ? cfgPort : 10100;
204
- const child = spawn(process.execPath, [process.argv[1], "start", "--port", String(pinPort)], {
278
+ const child = spawn(process.execPath, selfLaunchArgv(["start", "--port", String(pinPort)]), {
205
279
  detached: true,
206
280
  stdio: "ignore",
207
281
  windowsHide: true,
208
- env: { ...process.env, OCX_SERVICE: "1" },
282
+ env: withProcessRuntimeProvenance({ ...process.env, OCX_SERVICE: "1" }),
209
283
  });
210
284
  child.unref();
211
285
  const deadline = Date.now() + 8_000;
@@ -232,6 +306,22 @@ export function claudeNotFoundHint(
232
306
  return platform === "win32" && code === 9009 && !signal ? CLAUDE_INSTALL_HINT : null;
233
307
  }
234
308
 
309
+ export function shouldAllowRootSkipPermissions(
310
+ args: readonly string[],
311
+ getuid: (() => number) | null | undefined = process.getuid,
312
+ ): boolean {
313
+ return args.includes("--dangerously-skip-permissions")
314
+ && typeof getuid === "function"
315
+ && getuid() === 0;
316
+ }
317
+
318
+ export function rootSkipPermissionsNotice(env: ClaudeLaunchEnv): string {
319
+ if (env.IS_SANDBOX === "1") {
320
+ return "⚠ Root --dangerously-skip-permissions requested: OpenCodex set IS_SANDBOX=1 to bypass Claude Code's root guard. OpenCodex did not create an OS sandbox; prefer running as a non-root user.";
321
+ }
322
+ return `⚠ Root --dangerously-skip-permissions requested: preserving user IS_SANDBOX=${env.IS_SANDBOX}; Claude Code's root guard remains in control.`;
323
+ }
324
+
235
325
  export async function cmdClaude(args: string[]): Promise<number> {
236
326
  const config = loadConfig();
237
327
  if (config.claudeCode?.enabled === false) {
@@ -244,11 +334,15 @@ export async function cmdClaude(args: string[]): Promise<number> {
244
334
  return 1;
245
335
  }
246
336
  const contextWindows = await fetchClaudeContextWindows(config, port);
247
- const env = buildClaudeEnv(config, port, process.env, contextWindows);
337
+ const allowRootSkipPermissions = shouldAllowRootSkipPermissions(args);
338
+ const env = buildClaudeEnv(config, port, process.env, contextWindows, { allowRootSkipPermissions });
339
+ if (allowRootSkipPermissions) {
340
+ console.error(rootSkipPermissionsNotice(env));
341
+ }
248
342
  // Pre-write the CLI's gateway-model cache (devlog 030): without a token the CLI
249
343
  // never refreshes it, so the picker would keep showing yesterday's aliases.
250
344
  try {
251
- const cachePath = await refreshGatewayModelCacheFromProxy(port);
345
+ const cachePath = await refreshGatewayModelCacheFromProxy(port, { admissionConfig: config });
252
346
  if (cachePath === null) {
253
347
  console.error("⚠ Gateway model cache could not be refreshed; the model picker may be stale.");
254
348
  }
@@ -0,0 +1,103 @@
1
+ import { inspectCodexLogs, type CodexLogGuardInspection } from "../codex/log-guard/inspect";
2
+ import {
3
+ getCodexLogGuardProtectionStatus,
4
+ type CodexLogGuardStatus,
5
+ } from "../codex/log-guard/protection";
6
+
7
+ function kib(bytes: number): string {
8
+ return `${(bytes / 1024).toFixed(1)} KiB`;
9
+ }
10
+
11
+ function fileMetadataLines(report: CodexLogGuardInspection): string[] {
12
+ if (report.externalSqliteHome === null) return [];
13
+ const location = report.externalSqliteHome ? "external sqlite_home" : "CODEX_HOME sqlite_home";
14
+ return [
15
+ ` ${location}; DB ${kib(report.files.databaseBytes)}, WAL ${kib(report.files.walBytes)}, SHM ${kib(report.files.shmBytes)}`,
16
+ ];
17
+ }
18
+
19
+ type DoctorReport = CodexLogGuardInspection | CodexLogGuardStatus;
20
+
21
+ function protectionLines(report: DoctorReport): string[] {
22
+ if (!("protection" in report)) return [];
23
+ const protection = report.protection;
24
+ switch (protection.state) {
25
+ case "active":
26
+ return [` ok protection active (${protection.desiredMode})`];
27
+ case "off":
28
+ return [" -- protection off"];
29
+ case "drifted":
30
+ return [
31
+ ` WARN protection drifted (desired ${protection.desiredMode}; observed ${protection.observedMode})`,
32
+ " Action: ocx storage codex-logs repair",
33
+ ];
34
+ case "unsupported":
35
+ return [" -- protection unavailable for this schema"];
36
+ case "unknown":
37
+ return [" WARN protection state unknown; inspect reserved Log Guard triggers before changing mode"];
38
+ }
39
+ }
40
+
41
+ export function formatCodexLogGuardDoctor(report: DoctorReport): string[] {
42
+ const lines = ["Codex diagnostic logs"];
43
+ lines.push(...protectionLines(report));
44
+
45
+ if (report.schema.state === "unavailable") {
46
+ lines.push(" -- inspection unavailable");
47
+ return lines;
48
+ }
49
+ if (report.schema.state === "missing") {
50
+ lines.push(" -- logs_2.sqlite is not present");
51
+ return lines;
52
+ }
53
+ if (report.schema.state === "unreadable") {
54
+ lines.push(" -- logs_2.sqlite is unreadable; inspection metadata only");
55
+ lines.push(...fileMetadataLines(report));
56
+ lines.push(" checkpointed read-only snapshot; activity rate not measured");
57
+ return lines;
58
+ }
59
+ if (report.schema.state === "unsupported") {
60
+ lines.push(" -- unknown schema; inspection only");
61
+ } else {
62
+ lines.push(" ok schema compatible");
63
+ }
64
+
65
+ lines.push(...fileMetadataLines(report));
66
+
67
+ if (report.metrics) {
68
+ lines.push(
69
+ ` ${report.metrics.totalRows} rows; TRACE ${(report.metrics.traceShare * 100).toFixed(1)}%; reclaimable ${kib(report.metrics.reclaimableBytes)}`,
70
+ );
71
+ const top = report.metrics.topTargets[0];
72
+ if (top) lines.push(` top target ${top.target} (${top.rows} rows)`);
73
+ }
74
+
75
+ lines.push(" checkpointed read-only snapshot; activity rate not measured");
76
+ return lines;
77
+ }
78
+
79
+ export interface CodexLogGuardDoctorDeps {
80
+ inspect?: () => DoctorReport;
81
+ log?: (line: string) => void;
82
+ }
83
+
84
+ /** Observe-only doctor section. Inspection failures are reported without mutating or failing doctor. */
85
+ export function printCodexLogGuardDoctor(deps: CodexLogGuardDoctorDeps = {}): void {
86
+ const inspect = deps.inspect ?? getCodexLogGuardProtectionStatus;
87
+ const log = deps.log ?? console.log;
88
+ try {
89
+ for (const line of formatCodexLogGuardDoctor(inspect())) log(line);
90
+ } catch (error) {
91
+ // Production can still fall back to PR 1's simpler inspector if the enriched
92
+ // protection lookup fails. An injected inspector is a test/caller boundary:
93
+ // never escape that boundary and touch the real Codex home behind its back.
94
+ if (!deps.inspect) {
95
+ try {
96
+ for (const line of formatCodexLogGuardDoctor(inspectCodexLogs())) log(line);
97
+ return;
98
+ } catch { /* report the original failure below */ }
99
+ }
100
+ log("Codex diagnostic logs");
101
+ log(" -- inspection unavailable");
102
+ }
103
+ }
@@ -17,6 +17,8 @@ const DEFAULT_DEPS: CodexShimAutoRestoreCliDeps = {
17
17
 
18
18
  export function skipsCodexShimAutoRestore(command: string | undefined, args: string[]): boolean {
19
19
  if (command === "uninstall" || command === "remove") return true;
20
+ // `lab` is read-only inspection; it must not trigger shim side effects.
21
+ if (command === "lab") return true;
20
22
  return command === "codex-shim" && ["install", "uninstall", "remove"].includes(args[1] ?? "");
21
23
  }
22
24