@coseung2/opencodex 2.8.0-cs.1

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 (451) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +219 -0
  3. package/assets/architecture.png +0 -0
  4. package/assets/banner.png +0 -0
  5. package/assets/claude-code-models.gif +0 -0
  6. package/assets/codex-app-picker.png +0 -0
  7. package/bin/ocx-notch.mjs +78 -0
  8. package/bin/ocx.mjs +454 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-CAnnes06.js +67 -0
  11. package/gui/dist/assets/index-OY43ubAq.css +1 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/icons.svg +24 -0
  14. package/gui/dist/index.html +25 -0
  15. package/gui/dist/logo.png +0 -0
  16. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  17. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  18. package/gui/dist/provider-icons/antigravity.svg +1 -0
  19. package/gui/dist/provider-icons/claude-color.svg +1 -0
  20. package/gui/dist/provider-icons/claude.svg +1 -0
  21. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  22. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  23. package/gui/dist/provider-icons/copilot.svg +1 -0
  24. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  25. package/gui/dist/provider-icons/cursor.svg +2 -0
  26. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  27. package/gui/dist/provider-icons/discord.svg +1 -0
  28. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  29. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  30. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  31. package/gui/dist/provider-icons/gemini.svg +1 -0
  32. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  33. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  34. package/gui/dist/provider-icons/grok-color.svg +1 -0
  35. package/gui/dist/provider-icons/grok.svg +1 -0
  36. package/gui/dist/provider-icons/groq-color.svg +1 -0
  37. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  38. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  39. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  40. package/gui/dist/provider-icons/kiro.svg +14 -0
  41. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  42. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  43. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  44. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  45. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  46. package/gui/dist/provider-icons/openai.svg +1 -0
  47. package/gui/dist/provider-icons/opencode.svg +1 -0
  48. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  49. package/gui/dist/provider-icons/pi.svg +21 -0
  50. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  51. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  52. package/gui/dist/provider-icons/telegram.svg +1 -0
  53. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  54. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  55. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  56. package/package.json +107 -0
  57. package/packages/ocx-notch/README.md +55 -0
  58. package/packages/ocx-notch/THIRD_PARTY_NOTICES.md +45 -0
  59. package/src/AGENTS.md +28 -0
  60. package/src/adapters/anthropic-image-guard.ts +251 -0
  61. package/src/adapters/anthropic-image-normalize.ts +518 -0
  62. package/src/adapters/anthropic.ts +1003 -0
  63. package/src/adapters/azure.ts +36 -0
  64. package/src/adapters/base.ts +72 -0
  65. package/src/adapters/client-fingerprint.ts +59 -0
  66. package/src/adapters/cursor/arg-codec.ts +38 -0
  67. package/src/adapters/cursor/arg-normalize.ts +104 -0
  68. package/src/adapters/cursor/cursor-errors.ts +165 -0
  69. package/src/adapters/cursor/discovery.ts +276 -0
  70. package/src/adapters/cursor/effort-map.ts +127 -0
  71. package/src/adapters/cursor/exec-policy.ts +88 -0
  72. package/src/adapters/cursor/framing.ts +211 -0
  73. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  74. package/src/adapters/cursor/kv-store.ts +52 -0
  75. package/src/adapters/cursor/live-models.ts +153 -0
  76. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  77. package/src/adapters/cursor/live-transport.ts +1214 -0
  78. package/src/adapters/cursor/mcp-config.ts +42 -0
  79. package/src/adapters/cursor/mcp-manager.ts +333 -0
  80. package/src/adapters/cursor/message-mapper.ts +49 -0
  81. package/src/adapters/cursor/native-exec-common.ts +55 -0
  82. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  83. package/src/adapters/cursor/native-exec-fs.ts +329 -0
  84. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  85. package/src/adapters/cursor/native-exec-network.ts +43 -0
  86. package/src/adapters/cursor/native-exec-shell.ts +548 -0
  87. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  88. package/src/adapters/cursor/native-exec.ts +576 -0
  89. package/src/adapters/cursor/protobuf-events.ts +563 -0
  90. package/src/adapters/cursor/protobuf-request.ts +714 -0
  91. package/src/adapters/cursor/request-builder.ts +255 -0
  92. package/src/adapters/cursor/thread-continuity.ts +67 -0
  93. package/src/adapters/cursor/tool-definitions.ts +505 -0
  94. package/src/adapters/cursor/transport-retry.ts +132 -0
  95. package/src/adapters/cursor/transport.ts +57 -0
  96. package/src/adapters/cursor/types.ts +52 -0
  97. package/src/adapters/cursor.ts +196 -0
  98. package/src/adapters/google-antigravity-replay.ts +303 -0
  99. package/src/adapters/google-antigravity-wire.ts +108 -0
  100. package/src/adapters/google-errors.ts +85 -0
  101. package/src/adapters/google-http.ts +100 -0
  102. package/src/adapters/google-tool-schema.ts +173 -0
  103. package/src/adapters/google-truncation.ts +13 -0
  104. package/src/adapters/google-wire-compiler.ts +232 -0
  105. package/src/adapters/google.ts +758 -0
  106. package/src/adapters/identity.ts +44 -0
  107. package/src/adapters/image.ts +23 -0
  108. package/src/adapters/kiro-constants.ts +16 -0
  109. package/src/adapters/kiro-errors.ts +197 -0
  110. package/src/adapters/kiro-events.ts +179 -0
  111. package/src/adapters/kiro-images.ts +129 -0
  112. package/src/adapters/kiro-retry.ts +312 -0
  113. package/src/adapters/kiro-thinking.ts +96 -0
  114. package/src/adapters/kiro-tool-fallback.ts +36 -0
  115. package/src/adapters/kiro-tools.ts +215 -0
  116. package/src/adapters/kiro-truncation.ts +33 -0
  117. package/src/adapters/kiro-wire.ts +129 -0
  118. package/src/adapters/kiro.ts +1898 -0
  119. package/src/adapters/mimo-free.ts +263 -0
  120. package/src/adapters/openai-chat.ts +1005 -0
  121. package/src/adapters/openai-responses.ts +1137 -0
  122. package/src/adapters/run-turn-queue.ts +114 -0
  123. package/src/adapters/tool-catalog-nudge.ts +71 -0
  124. package/src/adapters/upstream-http-error.ts +48 -0
  125. package/src/bridge.ts +1619 -0
  126. package/src/chat/inbound.ts +295 -0
  127. package/src/chat/outbound.ts +765 -0
  128. package/src/claude/agents-inject.ts +243 -0
  129. package/src/claude/alias.ts +149 -0
  130. package/src/claude/auth-detect.ts +229 -0
  131. package/src/claude/auth-mode-migration.ts +32 -0
  132. package/src/claude/auth-mode.ts +62 -0
  133. package/src/claude/context-windows.ts +189 -0
  134. package/src/claude/desktop-3p-guard.ts +35 -0
  135. package/src/claude/desktop-3p-paths.ts +84 -0
  136. package/src/claude/desktop-3p.ts +381 -0
  137. package/src/claude/desktop-health.ts +26 -0
  138. package/src/claude/desktop-profile.ts +263 -0
  139. package/src/claude/gateway-cache.ts +70 -0
  140. package/src/claude/inbound-debug.ts +163 -0
  141. package/src/claude/inbound.ts +509 -0
  142. package/src/claude/model-info.ts +151 -0
  143. package/src/claude/outbound.ts +872 -0
  144. package/src/cli/access.ts +108 -0
  145. package/src/cli/account-api.ts +268 -0
  146. package/src/cli/account-auth.ts +223 -0
  147. package/src/cli/account-extended.ts +350 -0
  148. package/src/cli/account.ts +275 -0
  149. package/src/cli/agent-driven.ts +70 -0
  150. package/src/cli/agent.ts +184 -0
  151. package/src/cli/catalog-prewarm.ts +27 -0
  152. package/src/cli/claude-desktop.ts +188 -0
  153. package/src/cli/claude.ts +286 -0
  154. package/src/cli/codex-shim-autorestore.ts +45 -0
  155. package/src/cli/combo.ts +119 -0
  156. package/src/cli/config-command.ts +145 -0
  157. package/src/cli/debug.ts +228 -0
  158. package/src/cli/doctor.ts +930 -0
  159. package/src/cli/export-command.ts +187 -0
  160. package/src/cli/help.ts +330 -0
  161. package/src/cli/index.ts +1103 -0
  162. package/src/cli/init.ts +224 -0
  163. package/src/cli/integrations.ts +142 -0
  164. package/src/cli/interactive-confirm.ts +133 -0
  165. package/src/cli/internal-dispatch.ts +20 -0
  166. package/src/cli/models-runtime.ts +212 -0
  167. package/src/cli/models.ts +336 -0
  168. package/src/cli/observe.ts +117 -0
  169. package/src/cli/opencode.ts +586 -0
  170. package/src/cli/provider-runtime.ts +162 -0
  171. package/src/cli/provider.ts +463 -0
  172. package/src/cli/runtime-api.ts +325 -0
  173. package/src/cli/star-prompt.ts +134 -0
  174. package/src/cli/status-oauth.ts +78 -0
  175. package/src/cli/status.ts +321 -0
  176. package/src/cli/system-command.ts +114 -0
  177. package/src/cli/tray-proxy.ts +52 -0
  178. package/src/cli/v2.ts +173 -0
  179. package/src/cli.ts +10 -0
  180. package/src/clients/config-export.ts +377 -0
  181. package/src/codex/account-id.ts +34 -0
  182. package/src/codex/account-label.ts +34 -0
  183. package/src/codex/account-lifecycle.ts +55 -0
  184. package/src/codex/account-namespace-match.ts +63 -0
  185. package/src/codex/account-namespaces.ts +149 -0
  186. package/src/codex/account-pause.ts +20 -0
  187. package/src/codex/account-runtime-state.ts +31 -0
  188. package/src/codex/account-store.ts +517 -0
  189. package/src/codex/account-usability.ts +20 -0
  190. package/src/codex/app-server-processes.ts +756 -0
  191. package/src/codex/auth-api.ts +1540 -0
  192. package/src/codex/auth-collision.ts +107 -0
  193. package/src/codex/auth-context.ts +352 -0
  194. package/src/codex/autostart-health.ts +149 -0
  195. package/src/codex/catalog/aggregation.ts +378 -0
  196. package/src/codex/catalog/bundled.ts +251 -0
  197. package/src/codex/catalog/effort.ts +355 -0
  198. package/src/codex/catalog/metadata.ts +180 -0
  199. package/src/codex/catalog/parsing.ts +456 -0
  200. package/src/codex/catalog/provider-fetch.ts +902 -0
  201. package/src/codex/catalog/sync.ts +620 -0
  202. package/src/codex/catalog.ts +12 -0
  203. package/src/codex/data/upstream-models.json +830 -0
  204. package/src/codex/exec-invocation.ts +22 -0
  205. package/src/codex/features.ts +969 -0
  206. package/src/codex/history-migration-guardian.ts +102 -0
  207. package/src/codex/history-provider.ts +776 -0
  208. package/src/codex/home.ts +206 -0
  209. package/src/codex/inject.ts +799 -0
  210. package/src/codex/injected-marker.ts +72 -0
  211. package/src/codex/journal.ts +163 -0
  212. package/src/codex/main-account-cache.ts +32 -0
  213. package/src/codex/main-account.ts +40 -0
  214. package/src/codex/model-cache.ts +227 -0
  215. package/src/codex/paths.ts +65 -0
  216. package/src/codex/plugins-doctor.ts +242 -0
  217. package/src/codex/pool-rotation.ts +225 -0
  218. package/src/codex/project-config-warnings.ts +411 -0
  219. package/src/codex/quota.ts +411 -0
  220. package/src/codex/refresh.ts +53 -0
  221. package/src/codex/routing.ts +1477 -0
  222. package/src/codex/runtime.ts +538 -0
  223. package/src/codex/shim.ts +1189 -0
  224. package/src/codex/subagent-defaults.ts +550 -0
  225. package/src/codex/subagent-model-fallback.ts +469 -0
  226. package/src/codex/sync.ts +130 -0
  227. package/src/codex/warmup.ts +192 -0
  228. package/src/codex/websocket-registry.ts +100 -0
  229. package/src/combos/failover.ts +140 -0
  230. package/src/combos/index.ts +41 -0
  231. package/src/combos/request.ts +62 -0
  232. package/src/combos/resolve.ts +232 -0
  233. package/src/combos/types.ts +326 -0
  234. package/src/config.ts +2355 -0
  235. package/src/generated/jawcode-model-metadata.ts +104 -0
  236. package/src/github/star-state.ts +203 -0
  237. package/src/grok/inject.ts +519 -0
  238. package/src/grok/status.ts +121 -0
  239. package/src/grok/sync.ts +66 -0
  240. package/src/images/artifacts.ts +516 -0
  241. package/src/images/fulfill-video.ts +163 -0
  242. package/src/images/fulfill.ts +149 -0
  243. package/src/images/index.ts +4 -0
  244. package/src/images/loop.ts +829 -0
  245. package/src/images/plan.ts +133 -0
  246. package/src/images/synthetic-tool.ts +133 -0
  247. package/src/images/types.ts +41 -0
  248. package/src/images/xai-client.ts +141 -0
  249. package/src/images/xai-video-client.ts +163 -0
  250. package/src/index.ts +22 -0
  251. package/src/lib/abort.ts +146 -0
  252. package/src/lib/admin-secrets.ts +25 -0
  253. package/src/lib/admission.ts +83 -0
  254. package/src/lib/app-owned-memory-stores.ts +173 -0
  255. package/src/lib/app-owned-memory.ts +265 -0
  256. package/src/lib/bounded-body.ts +202 -0
  257. package/src/lib/bun-binary-validator.d.mts +3 -0
  258. package/src/lib/bun-binary-validator.mjs +18 -0
  259. package/src/lib/bun-runtime.ts +71 -0
  260. package/src/lib/bun-stream-caps.ts +126 -0
  261. package/src/lib/config-ownership.ts +360 -0
  262. package/src/lib/crash-guard.ts +344 -0
  263. package/src/lib/debug-log-buffer.ts +83 -0
  264. package/src/lib/debug-settings.ts +108 -0
  265. package/src/lib/debug.ts +31 -0
  266. package/src/lib/destination-policy.ts +316 -0
  267. package/src/lib/errors.ts +364 -0
  268. package/src/lib/eventstream-decoder.ts +253 -0
  269. package/src/lib/gcp-adc.ts +341 -0
  270. package/src/lib/injection-debug-log.ts +58 -0
  271. package/src/lib/open-url.ts +25 -0
  272. package/src/lib/pinned-http.ts +151 -0
  273. package/src/lib/privacy.ts +20 -0
  274. package/src/lib/process-control.ts +165 -0
  275. package/src/lib/provider-outbound.ts +170 -0
  276. package/src/lib/provider-url.ts +14 -0
  277. package/src/lib/proxy-env.ts +18 -0
  278. package/src/lib/redact.ts +105 -0
  279. package/src/lib/retry-after.ts +55 -0
  280. package/src/lib/service-secrets.ts +25 -0
  281. package/src/lib/shadow-call.ts +30 -0
  282. package/src/lib/sidecar-tracker.ts +52 -0
  283. package/src/lib/sse-decoder.ts +323 -0
  284. package/src/lib/state-store-registrations.ts +109 -0
  285. package/src/lib/state-store-sweeper.ts +184 -0
  286. package/src/lib/test-home-guard.ts +90 -0
  287. package/src/lib/token-estimate.ts +69 -0
  288. package/src/lib/translator-budget.ts +356 -0
  289. package/src/lib/upstream-retry.ts +239 -0
  290. package/src/lib/win-exec.ts +115 -0
  291. package/src/lib/win-paths.ts +68 -0
  292. package/src/lib/windows-elevation.ts +705 -0
  293. package/src/lib/windows-secret-acl.ts +514 -0
  294. package/src/lib/winsw.ts +375 -0
  295. package/src/oauth/anthropic-routing.ts +594 -0
  296. package/src/oauth/anthropic.ts +177 -0
  297. package/src/oauth/callback-server.ts +294 -0
  298. package/src/oauth/chatgpt.ts +150 -0
  299. package/src/oauth/cursor.ts +211 -0
  300. package/src/oauth/github-copilot.ts +428 -0
  301. package/src/oauth/google-antigravity.ts +230 -0
  302. package/src/oauth/health.ts +399 -0
  303. package/src/oauth/index.ts +1173 -0
  304. package/src/oauth/key-providers.ts +108 -0
  305. package/src/oauth/kimi.ts +213 -0
  306. package/src/oauth/kiro-credentials.ts +726 -0
  307. package/src/oauth/kiro.ts +577 -0
  308. package/src/oauth/local-token-detect.ts +121 -0
  309. package/src/oauth/log.ts +48 -0
  310. package/src/oauth/login-cli.ts +163 -0
  311. package/src/oauth/pkce.ts +15 -0
  312. package/src/oauth/store.ts +630 -0
  313. package/src/oauth/token-guardian.ts +303 -0
  314. package/src/oauth/types.ts +62 -0
  315. package/src/oauth/xai.ts +241 -0
  316. package/src/providers/alibaba-region-backup.ts +75 -0
  317. package/src/providers/alibaba-region-migration.ts +156 -0
  318. package/src/providers/alibaba-region-startup.ts +36 -0
  319. package/src/providers/antigravity-models.ts +205 -0
  320. package/src/providers/api-keys.ts +140 -0
  321. package/src/providers/base-url-choices.ts +64 -0
  322. package/src/providers/context-cap.ts +65 -0
  323. package/src/providers/derive.ts +339 -0
  324. package/src/providers/free-directory.ts +184 -0
  325. package/src/providers/github-copilot-transport.ts +56 -0
  326. package/src/providers/key-failover.ts +203 -0
  327. package/src/providers/kiro-models.ts +67 -0
  328. package/src/providers/label.ts +19 -0
  329. package/src/providers/model-discovery.ts +356 -0
  330. package/src/providers/openai-sidecar.ts +175 -0
  331. package/src/providers/openai-tier-startup.ts +27 -0
  332. package/src/providers/openai-tiers.ts +301 -0
  333. package/src/providers/openai-virtual-models.ts +82 -0
  334. package/src/providers/openrouter-routing.ts +102 -0
  335. package/src/providers/provider-id-rewrite.ts +150 -0
  336. package/src/providers/quota.ts +1045 -0
  337. package/src/providers/registry.ts +1600 -0
  338. package/src/providers/slug-codec.ts +67 -0
  339. package/src/providers/xai-transport.ts +141 -0
  340. package/src/reasoning-effort.ts +135 -0
  341. package/src/responses/compaction.ts +117 -0
  342. package/src/responses/parser.ts +656 -0
  343. package/src/responses/reasoning-envelope.ts +52 -0
  344. package/src/responses/schema.ts +159 -0
  345. package/src/responses/spill-store.ts +394 -0
  346. package/src/responses/state.ts +895 -0
  347. package/src/responses/tool-groups.ts +19 -0
  348. package/src/router.ts +425 -0
  349. package/src/server/adapter-resolve.ts +80 -0
  350. package/src/server/auth-cors.ts +530 -0
  351. package/src/server/chat-completions.ts +368 -0
  352. package/src/server/claude-messages.ts +914 -0
  353. package/src/server/effort-policy.ts +172 -0
  354. package/src/server/gui-static.ts +123 -0
  355. package/src/server/image-retry.ts +42 -0
  356. package/src/server/images.ts +476 -0
  357. package/src/server/index.ts +1126 -0
  358. package/src/server/lifecycle.ts +305 -0
  359. package/src/server/live.ts +598 -0
  360. package/src/server/management/agent-settings-routes.ts +1039 -0
  361. package/src/server/management/api-access.ts +141 -0
  362. package/src/server/management/api-key-usage.ts +167 -0
  363. package/src/server/management/body.ts +35 -0
  364. package/src/server/management/combo-routes.ts +220 -0
  365. package/src/server/management/config-routes.ts +422 -0
  366. package/src/server/management/context.ts +31 -0
  367. package/src/server/management/logs-usage-routes.ts +516 -0
  368. package/src/server/management/model-routes.ts +525 -0
  369. package/src/server/management/oauth-account-routes.ts +561 -0
  370. package/src/server/management/provider-routes.ts +556 -0
  371. package/src/server/management/shared.ts +277 -0
  372. package/src/server/management/sidebar-routes.ts +88 -0
  373. package/src/server/management/system-restart.ts +179 -0
  374. package/src/server/management/system-routes.ts +117 -0
  375. package/src/server/management/usage-summary-cache.ts +86 -0
  376. package/src/server/management-api.ts +211 -0
  377. package/src/server/management-auth.ts +267 -0
  378. package/src/server/memory-watchdog.ts +156 -0
  379. package/src/server/port-reclaim.ts +307 -0
  380. package/src/server/ports.ts +116 -0
  381. package/src/server/proxy-liveness.ts +201 -0
  382. package/src/server/relay-eager.ts +313 -0
  383. package/src/server/relay.ts +1060 -0
  384. package/src/server/request-decompress.ts +132 -0
  385. package/src/server/request-log-conversation.ts +168 -0
  386. package/src/server/request-log.ts +1046 -0
  387. package/src/server/responses/collaboration.ts +354 -0
  388. package/src/server/responses/compact.ts +384 -0
  389. package/src/server/responses/core.ts +2756 -0
  390. package/src/server/responses/encrypted-payload.ts +308 -0
  391. package/src/server/responses/fetch-helpers.ts +157 -0
  392. package/src/server/responses/passthrough-error.ts +78 -0
  393. package/src/server/responses/terminal-guard.ts +230 -0
  394. package/src/server/responses/upstream-error.ts +48 -0
  395. package/src/server/responses-image-gen-repair.ts +132 -0
  396. package/src/server/responses-item-id-repair.ts +224 -0
  397. package/src/server/responses.ts +9 -0
  398. package/src/server/search.ts +136 -0
  399. package/src/server/sse-payload-rewrite.ts +175 -0
  400. package/src/server/startup-action-control.ts +308 -0
  401. package/src/server/startup-health-cache.ts +113 -0
  402. package/src/server/system-env.ts +413 -0
  403. package/src/server/windows-tcp-drop.ts +184 -0
  404. package/src/server/windows-tray-control.ts +41 -0
  405. package/src/server/ws-bridge.ts +471 -0
  406. package/src/service.ts +2554 -0
  407. package/src/stall-timeout.ts +20 -0
  408. package/src/storage/cleanup-job.ts +57 -0
  409. package/src/storage/cleanup.ts +3085 -0
  410. package/src/storage/policy-job.ts +457 -0
  411. package/src/storage/policy-scheduler.ts +40 -0
  412. package/src/storage/policy-worker.ts +59 -0
  413. package/src/storage/policy.ts +527 -0
  414. package/src/storage/restore-job.ts +299 -0
  415. package/src/storage/restore-worker.ts +58 -0
  416. package/src/storage/scanner.ts +238 -0
  417. package/src/storage/storage-mutation-coordinator.ts +139 -0
  418. package/src/storage/worker-lifecycle.ts +215 -0
  419. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  420. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  421. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  422. package/src/tray/assets/opencodex-tray.png +0 -0
  423. package/src/tray/windows-tray.ps1 +290 -0
  424. package/src/tray/windows.ts +730 -0
  425. package/src/types.ts +1237 -0
  426. package/src/update/badge.ts +72 -0
  427. package/src/update/index.ts +392 -0
  428. package/src/update/job.ts +1513 -0
  429. package/src/update/notify.ts +257 -0
  430. package/src/update/npm-invocation.d.mts +23 -0
  431. package/src/update/npm-invocation.mjs +94 -0
  432. package/src/update/tray-update-plan.d.mts +18 -0
  433. package/src/update/tray-update-plan.mjs +38 -0
  434. package/src/usage/cost.ts +0 -0
  435. package/src/usage/debug.ts +97 -0
  436. package/src/usage/expected-prices.ts +164 -0
  437. package/src/usage/log.ts +658 -0
  438. package/src/usage/summary.ts +585 -0
  439. package/src/usage/totals.ts +14 -0
  440. package/src/vision/anthropic-describe.ts +185 -0
  441. package/src/vision/describe.ts +125 -0
  442. package/src/vision/index.ts +467 -0
  443. package/src/web-search/anthropic-executor.ts +189 -0
  444. package/src/web-search/executor.ts +105 -0
  445. package/src/web-search/format-result.ts +89 -0
  446. package/src/web-search/index.ts +196 -0
  447. package/src/web-search/loop.ts +664 -0
  448. package/src/web-search/parse.ts +220 -0
  449. package/src/web-search/progress-stream.ts +342 -0
  450. package/src/web-search/synthetic-tool.ts +47 -0
  451. package/vendor/ocx-notch/win32-x64/ocx-notch.exe +0 -0
@@ -0,0 +1,162 @@
1
+ import {
2
+ CliUsageError,
3
+ csv,
4
+ printData,
5
+ rejectArgs,
6
+ runCliAction,
7
+ runtimeRequest,
8
+ summaryLines,
9
+ takeBooleanOption,
10
+ takeFlag,
11
+ takeOption,
12
+ type RuntimeApiDeps,
13
+ } from "./runtime-api";
14
+
15
+ const USAGE = `Usage:
16
+ ocx provider edit <name> [--adapter <id>] [--base-url <url>] [--default-model <id|->]
17
+ [--auth-mode <key|forward|oauth|local|->] [--note <text|->]
18
+ [--api-key-transport <x-api-key|bearer|->]
19
+ [--enabled <on|off>] [--live-models <on|off>] [--allow-private-network <on|off>] [--json]
20
+ ocx provider test <name> [--json]
21
+ ocx provider quota [--refresh] [--json]
22
+ ocx provider presets [--json]
23
+ ocx provider account-mode <pool|direct> [--json]
24
+ ocx provider selected <name> [--set <model,model...>] [--clear] [--json]`;
25
+
26
+ function cleared(value: string | undefined): string | undefined {
27
+ return value === "-" ? "" : value;
28
+ }
29
+
30
+ async function edit(argv: string[], deps: RuntimeApiDeps): Promise<void> {
31
+ const args = [...argv];
32
+ const name = args.shift()?.trim();
33
+ if (!name) throw new CliUsageError("provider name is required", USAGE);
34
+ const wantsJson = takeFlag(args, "--json");
35
+ const patch: Record<string, unknown> = {};
36
+ const adapter = takeOption(args, "--adapter");
37
+ const baseUrl = takeOption(args, "--base-url");
38
+ const defaultModel = cleared(takeOption(args, "--default-model"));
39
+ const authMode = cleared(takeOption(args, "--auth-mode"));
40
+ const note = cleared(takeOption(args, "--note"));
41
+ const apiKeyTransport = cleared(takeOption(args, "--api-key-transport"));
42
+ const enabled = takeBooleanOption(args, "--enabled");
43
+ const liveModels = takeBooleanOption(args, "--live-models");
44
+ const allowPrivateNetwork = takeBooleanOption(args, "--allow-private-network");
45
+ rejectArgs(args, USAGE);
46
+ if (adapter !== undefined) patch.adapter = adapter;
47
+ if (baseUrl !== undefined) patch.baseUrl = baseUrl;
48
+ if (defaultModel !== undefined) patch.defaultModel = defaultModel;
49
+ if (authMode !== undefined) patch.authMode = authMode;
50
+ if (note !== undefined) patch.note = note;
51
+ if (apiKeyTransport !== undefined) patch.apiKeyTransport = apiKeyTransport;
52
+ if (enabled !== undefined) patch.disabled = !enabled;
53
+ if (liveModels !== undefined) patch.liveModels = liveModels;
54
+ if (allowPrivateNetwork !== undefined) patch.allowPrivateNetwork = allowPrivateNetwork;
55
+ if (Object.keys(patch).length === 0) throw new CliUsageError("at least one edit option is required", USAGE);
56
+ const result = await runtimeRequest(`/api/providers?name=${encodeURIComponent(name)}`, {
57
+ method: "PATCH",
58
+ body: JSON.stringify(patch),
59
+ }, deps);
60
+ printData(result, wantsJson, [`Updated provider ${name}.`]);
61
+ }
62
+
63
+ async function testProvider(argv: string[], deps: RuntimeApiDeps): Promise<void> {
64
+ const args = [...argv];
65
+ const name = args.shift()?.trim();
66
+ const wantsJson = takeFlag(args, "--json");
67
+ if (!name) throw new CliUsageError("provider name is required", USAGE);
68
+ rejectArgs(args, USAGE);
69
+ const result = await runtimeRequest<Record<string, unknown>>(`/api/providers/test?name=${encodeURIComponent(name)}`, {
70
+ method: "POST",
71
+ }, deps);
72
+ if (result.applicable === false) {
73
+ printData(result, wantsJson, [
74
+ `${name}: not applicable`,
75
+ "Static catalog; no live model-discovery endpoint to test.",
76
+ ]);
77
+ return;
78
+ }
79
+ const ok = result.ok === true;
80
+ printData(result, wantsJson, [
81
+ `${name}: ${ok ? "connected" : "failed"}`,
82
+ String(result.message ?? result.error ?? "No detail"),
83
+ `Latency: ${String(result.latencyMs ?? "?")} ms`,
84
+ ]);
85
+ if (!ok) process.exitCode = 1;
86
+ }
87
+
88
+ async function quota(argv: string[], deps: RuntimeApiDeps): Promise<void> {
89
+ const args = [...argv];
90
+ const wantsJson = takeFlag(args, "--json");
91
+ const refresh = takeFlag(args, "--refresh");
92
+ rejectArgs(args, USAGE);
93
+ const result = await runtimeRequest(`/api/provider-quotas${refresh ? "?refresh=1" : ""}`, {}, deps);
94
+ printData(result, wantsJson, summaryLines(result));
95
+ }
96
+
97
+ async function presets(argv: string[], deps: RuntimeApiDeps): Promise<void> {
98
+ const args = [...argv];
99
+ const wantsJson = takeFlag(args, "--json");
100
+ rejectArgs(args, USAGE);
101
+ const result = await runtimeRequest<{ providers?: unknown[] } | unknown[]>("/api/provider-presets", {}, deps);
102
+ const rows = Array.isArray(result) ? result : result.providers ?? [];
103
+ printData(result, wantsJson, rows.map(row => {
104
+ const record = row as Record<string, unknown>;
105
+ return `${String(record.id ?? record.name ?? "?")} ${String(record.label ?? record.adapter ?? "")}`.trimEnd();
106
+ }));
107
+ }
108
+
109
+ async function accountMode(argv: string[], deps: RuntimeApiDeps): Promise<void> {
110
+ const args = [...argv];
111
+ const mode = args.shift();
112
+ const wantsJson = takeFlag(args, "--json");
113
+ if (mode !== "pool" && mode !== "direct") throw new CliUsageError("mode must be pool or direct", USAGE);
114
+ rejectArgs(args, USAGE);
115
+ const result = await runtimeRequest("/api/providers?name=openai", {
116
+ method: "PATCH",
117
+ body: JSON.stringify({ codexAccountMode: mode }),
118
+ }, deps);
119
+ printData(result, wantsJson, [`OpenAI Codex account mode: ${mode}`]);
120
+ }
121
+
122
+ async function selected(argv: string[], deps: RuntimeApiDeps): Promise<void> {
123
+ const args = [...argv];
124
+ const name = args.shift()?.trim();
125
+ const wantsJson = takeFlag(args, "--json");
126
+ const set = csv(takeOption(args, "--set"));
127
+ const clear = takeFlag(args, "--clear");
128
+ if (!name) throw new CliUsageError("provider name is required", USAGE);
129
+ if (set !== undefined && clear) throw new CliUsageError("--set and --clear cannot be combined", USAGE);
130
+ rejectArgs(args, USAGE);
131
+ if (set === undefined && !clear) {
132
+ const result = await runtimeRequest<Record<string, unknown>>("/api/selected-models", {}, deps);
133
+ const selectedMap = (result.selected ?? {}) as Record<string, string[]>;
134
+ const availableMap = (result.available ?? {}) as Record<string, string[]>;
135
+ printData({ provider: name, selected: selectedMap[name] ?? [], available: availableMap[name] ?? [] }, wantsJson,
136
+ [`Selected models for ${name}: ${(selectedMap[name] ?? []).join(", ") || "all"}`]);
137
+ return;
138
+ }
139
+ const models = clear ? [] : (set ?? []);
140
+ const result = await runtimeRequest("/api/selected-models", {
141
+ method: "PUT",
142
+ body: JSON.stringify({ provider: name, models }),
143
+ }, deps);
144
+ printData(result, wantsJson, [`${name}: ${models.length ? models.join(", ") : "all models"}`]);
145
+ }
146
+
147
+ export async function handleProviderRuntimeCommand(sub: string, argv: string[], deps: RuntimeApiDeps = {}): Promise<number | null> {
148
+ const handlers: Record<string, (args: string[], deps: RuntimeApiDeps) => Promise<void>> = {
149
+ edit,
150
+ update: edit,
151
+ test: testProvider,
152
+ quota,
153
+ presets,
154
+ "account-mode": accountMode,
155
+ selected,
156
+ };
157
+ const handler = handlers[sub];
158
+ if (!handler) return null;
159
+ return runCliAction(() => handler(argv, deps));
160
+ }
161
+
162
+ export const PROVIDER_RUNTIME_USAGE = USAGE;
@@ -0,0 +1,463 @@
1
+ /**
2
+ * `ocx provider` subcommand — non-interactive provider management.
3
+ *
4
+ * Subcommands:
5
+ * list List configured and available registry providers
6
+ * add <name> Add a provider from the registry or with custom flags
7
+ * remove <name> Remove a configured provider
8
+ * show <name> Show provider config details (secrets masked)
9
+ * set-default <name> Change the default provider
10
+ */
11
+ import { apiKeyTransportConfigError, hasOwnProvider, isValidProviderName, loadConfig, saveConfig } from "../config";
12
+ import { hasHelpFlag } from "./help";
13
+ import { getProviderRegistryEntry, PROVIDER_REGISTRY } from "../providers/registry";
14
+ import { providerConfigSeed } from "../providers/derive";
15
+ import type { OcxProviderConfig } from "../types";
16
+ import { findLiveProxy } from "../server/proxy-liveness";
17
+ import { syncModelsToCodex } from "../codex/sync";
18
+ import { codexAccountNamespaceProviderCollisionError } from "../codex/account-namespace-match";
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Arg helpers
22
+ // ---------------------------------------------------------------------------
23
+
24
+ function consumeFlag(args: string[], flag: string): boolean {
25
+ const idx = args.indexOf(flag);
26
+ if (idx === -1) return false;
27
+ args.splice(idx, 1);
28
+ return true;
29
+ }
30
+
31
+ function consumeFlagValue(args: string[], flag: string): string | undefined {
32
+ const idx = args.indexOf(flag);
33
+ if (idx === -1 || idx + 1 >= args.length) return undefined;
34
+ const value = args[idx + 1];
35
+ args.splice(idx, 2);
36
+ return value;
37
+ }
38
+
39
+ /** Reject any leftover args (unknown flags or trailing values). */
40
+ function rejectUnknownArgs(args: string[], usage: string): void {
41
+ if (args.length === 0) return;
42
+ const unknown = args.filter(a => a.startsWith("-"));
43
+ if (unknown.length > 0) {
44
+ console.error(`Unknown flag(s): ${unknown.join(", ")}`);
45
+ } else {
46
+ console.error(`Unexpected argument(s): ${args.join(", ")}`);
47
+ }
48
+ console.error(usage);
49
+ process.exit(1);
50
+ }
51
+
52
+ function maskSecret(value: string): string {
53
+ if (value.length <= 8) return "****";
54
+ return `${value.slice(0, 4)}****${value.slice(-4)}`;
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Validation helper (F1 fix: validate before saveConfig)
59
+ // ---------------------------------------------------------------------------
60
+
61
+ function validateAndSave(config: ReturnType<typeof loadConfig>): void {
62
+ if (!config.providers || Object.keys(config.providers).length === 0) {
63
+ console.error("Error: config would have no providers. Aborting.");
64
+ process.exit(1);
65
+ }
66
+ if (!hasOwnProvider(config.providers, config.defaultProvider)) {
67
+ console.error(`Error: defaultProvider "${config.defaultProvider}" does not exist in providers. Aborting.`);
68
+ process.exit(1);
69
+ }
70
+ saveConfig(config);
71
+ }
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // provider list
75
+ // ---------------------------------------------------------------------------
76
+
77
+ function handleList(args: string[]): void {
78
+ const wantsJson = consumeFlag(args, "--json");
79
+ rejectUnknownArgs(args, "Usage: ocx provider list [--json]");
80
+
81
+ const config = loadConfig();
82
+ const configured = Object.keys(config.providers);
83
+
84
+ if (wantsJson) {
85
+ const entries = configured.map(name => {
86
+ const prov = config.providers[name];
87
+ const registryEntry = getProviderRegistryEntry(name);
88
+ return {
89
+ name,
90
+ adapter: prov.adapter,
91
+ baseUrl: prov.baseUrl,
92
+ authMode: prov.authMode ?? "key",
93
+ defaultModel: prov.defaultModel ?? null,
94
+ isDefault: name === config.defaultProvider,
95
+ source: registryEntry ? "registry" : "custom",
96
+ models: prov.models ?? [],
97
+ };
98
+ });
99
+ console.log(JSON.stringify({ configured: entries, registryCount: PROVIDER_REGISTRY.length }, null, 2));
100
+ return;
101
+ }
102
+
103
+ console.log("Configured providers:\n");
104
+ for (const name of configured) {
105
+ const prov = config.providers[name];
106
+ const isDefault = name === config.defaultProvider ? " (default)" : "";
107
+ const registryEntry = getProviderRegistryEntry(name);
108
+ const source = registryEntry ? "" : " [custom]";
109
+ const model = prov.defaultModel ? ` model=${prov.defaultModel}` : "";
110
+ console.log(` ${name}${isDefault}${source} adapter=${prov.adapter}${model}`);
111
+ }
112
+
113
+ const available = PROVIDER_REGISTRY.filter(e => !configured.includes(e.id));
114
+ if (available.length > 0) {
115
+ console.log(`\nAvailable from registry (${available.length}):\n`);
116
+ for (const entry of available) {
117
+ const auth = entry.authKind === "forward" ? "chatgpt-login" : entry.authKind;
118
+ console.log(` ${entry.id.padEnd(24)} ${entry.label} (${auth})`);
119
+ }
120
+ console.log(`\nAdd with: ocx provider add <name> [--api-key <key>]`);
121
+ }
122
+ }
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // provider add
126
+ // ---------------------------------------------------------------------------
127
+
128
+ const ADD_USAGE = "Usage: ocx provider add <name> [--adapter <adapter>] [--base-url <url>] [--api-key <key>] [--api-key-transport <x-api-key|bearer>] [--default-model <model>] [--allow-private-network] [--set-default] [--force] [--json] [--sync]";
129
+
130
+ async function handleAdd(args: string[]): Promise<void> {
131
+ const name = args[0];
132
+ if (!name || name.startsWith("-")) {
133
+ console.error(ADD_USAGE);
134
+ process.exit(1);
135
+ }
136
+
137
+ if (!isValidProviderName(name)) {
138
+ console.error(`Invalid provider name: "${name}". Use letters, numbers, dots, underscores, or hyphens.`);
139
+ process.exit(1);
140
+ }
141
+
142
+ const restArgs = args.slice(1);
143
+ const force = consumeFlag(restArgs, "--force");
144
+ const setDefault = consumeFlag(restArgs, "--set-default");
145
+ const wantsJson = consumeFlag(restArgs, "--json");
146
+ const wantsSync = consumeFlag(restArgs, "--sync");
147
+ const allowPrivateNetwork = consumeFlag(restArgs, "--allow-private-network");
148
+ const apiKey = consumeFlagValue(restArgs, "--api-key");
149
+ const apiKeyTransport = consumeFlagValue(restArgs, "--api-key-transport");
150
+ const adapter = consumeFlagValue(restArgs, "--adapter");
151
+ const baseUrl = consumeFlagValue(restArgs, "--base-url");
152
+ const defaultModel = consumeFlagValue(restArgs, "--default-model");
153
+ rejectUnknownArgs(restArgs, ADD_USAGE);
154
+
155
+ const config = loadConfig();
156
+
157
+ const namespaceCollision = codexAccountNamespaceProviderCollisionError(config.codexAccountNamespaces, name);
158
+ if (namespaceCollision) {
159
+ console.error(`Error: ${namespaceCollision}.`);
160
+ process.exit(1);
161
+ }
162
+
163
+ if (hasOwnProvider(config.providers, name) && !force) {
164
+ console.error(`Provider "${name}" already exists. Use --force to overwrite.`);
165
+ process.exit(1);
166
+ }
167
+
168
+ let provConfig: OcxProviderConfig;
169
+ const registryEntry = getProviderRegistryEntry(name);
170
+
171
+ if (registryEntry) {
172
+ provConfig = providerConfigSeed(registryEntry);
173
+ if (apiKey) {
174
+ if (registryEntry.authKind === "forward") {
175
+ console.warn(`Warning: provider "${name}" uses ChatGPT login (forward auth); --api-key is ignored.`);
176
+ } else if (registryEntry.authKind === "oauth") {
177
+ console.warn(`Warning: provider "${name}" uses OAuth auth; --api-key is ignored. Run: ocx login ${name}`);
178
+ } else {
179
+ provConfig.apiKey = apiKey;
180
+ }
181
+ }
182
+ if (defaultModel) provConfig.defaultModel = defaultModel;
183
+ if (adapter) provConfig.adapter = adapter;
184
+ if (baseUrl) provConfig.baseUrl = baseUrl;
185
+ } else {
186
+ if (!adapter || !baseUrl) {
187
+ console.error(`Provider "${name}" is not in the registry. --adapter and --base-url are required.`);
188
+ console.error("Usage: ocx provider add <name> --adapter <adapter> --base-url <url> [--api-key <key>]");
189
+ process.exit(1);
190
+ }
191
+ provConfig = {
192
+ adapter,
193
+ baseUrl,
194
+ ...(apiKey ? { apiKey } : {}),
195
+ ...(defaultModel ? { defaultModel } : {}),
196
+ };
197
+ }
198
+
199
+ if (apiKeyTransport !== undefined) {
200
+ if (apiKeyTransport !== "x-api-key" && apiKeyTransport !== "bearer") {
201
+ console.error('Error: --api-key-transport must be "x-api-key" or "bearer".');
202
+ process.exit(1);
203
+ }
204
+ const transportError = apiKeyTransportConfigError({ ...provConfig, apiKeyTransport });
205
+ if (transportError) {
206
+ console.error(`Error: ${transportError}.`);
207
+ process.exit(1);
208
+ }
209
+ provConfig.apiKeyTransport = apiKeyTransport;
210
+ }
211
+
212
+ config.providers[name] = provConfig;
213
+ if (allowPrivateNetwork) provConfig.allowPrivateNetwork = true;
214
+ if (setDefault) config.defaultProvider = name;
215
+
216
+ validateAndSave(config);
217
+
218
+ if (wantsJson) {
219
+ console.log(JSON.stringify({
220
+ action: "added",
221
+ provider: name,
222
+ adapter: provConfig.adapter,
223
+ baseUrl: provConfig.baseUrl,
224
+ defaultModel: provConfig.defaultModel ?? null,
225
+ isDefault: config.defaultProvider === name,
226
+ source: registryEntry ? "registry" : "custom",
227
+ needsSync: true,
228
+ }, null, 2));
229
+ return;
230
+ }
231
+
232
+ if (wantsSync) {
233
+ const live = await findLiveProxy();
234
+ if (live) {
235
+ await syncModelsToCodex(live.port).catch(e => {
236
+ console.error(`Warning: sync failed: ${e instanceof Error ? e.message : String(e)}`);
237
+ });
238
+ }
239
+ }
240
+
241
+ const registryLabel = registryEntry ? ` (${registryEntry.label})` : "";
242
+ console.log(`✅ Provider "${name}"${registryLabel} added.`);
243
+ if (setDefault) console.log(` Set as default provider.`);
244
+ if (registryEntry?.authKind === "oauth") {
245
+ console.log(` Authenticate with: ocx login ${name}`);
246
+ }
247
+ if (registryEntry?.authKind === "key" && !apiKey) {
248
+ const envKey = `${name.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_API_KEY`;
249
+ console.log(` Set API key with: ocx provider add ${name} --api-key <key> --force`);
250
+ console.log(` Or set env var: ${envKey}`);
251
+ }
252
+ if (wantsSync) {
253
+ console.log(` Models synced to Codex.`);
254
+ } else {
255
+ console.log(` Apply to Codex: ocx sync`);
256
+ }
257
+ }
258
+
259
+ // ---------------------------------------------------------------------------
260
+ // provider remove
261
+ // ---------------------------------------------------------------------------
262
+
263
+ function handleRemove(args: string[]): void {
264
+ const restArgs = [...args];
265
+ const wantsJson = consumeFlag(restArgs, "--json");
266
+ const name = restArgs[0];
267
+ if (!name || name.startsWith("-")) {
268
+ console.error("Usage: ocx provider remove <name> [--json]");
269
+ process.exit(1);
270
+ }
271
+ rejectUnknownArgs(restArgs.slice(1), "Usage: ocx provider remove <name> [--json]");
272
+
273
+ const config = loadConfig();
274
+ if (!hasOwnProvider(config.providers, name)) {
275
+ console.error(`Provider "${name}" is not configured.`);
276
+ process.exit(1);
277
+ }
278
+
279
+ if (name === config.defaultProvider) {
280
+ console.error(`Cannot remove "${name}" — it is the default provider. Change the default first: ocx provider set-default <other>`);
281
+ process.exit(1);
282
+ }
283
+
284
+ if (Object.keys(config.providers).length <= 1) {
285
+ console.error("Cannot remove the last provider.");
286
+ process.exit(1);
287
+ }
288
+
289
+ const dependentCombos = Object.entries(config.combos ?? {})
290
+ .filter(([, combo]) => combo.targets.some(target => target.provider === name))
291
+ .map(([id]) => id)
292
+ .sort();
293
+ if (dependentCombos.length > 0) {
294
+ console.error(`Cannot remove "${name}" — combo(s) depend on it: ${dependentCombos.join(", ")}`);
295
+ process.exit(1);
296
+ }
297
+
298
+ delete config.providers[name];
299
+ validateAndSave(config);
300
+
301
+
302
+ if (wantsJson) {
303
+ console.log(JSON.stringify({
304
+ action: "removed",
305
+ provider: name,
306
+ remainingProviders: Object.keys(config.providers),
307
+ defaultProvider: config.defaultProvider,
308
+ needsSync: true,
309
+ }, null, 2));
310
+ return;
311
+ }
312
+
313
+ console.log(`✅ Provider "${name}" removed.`);
314
+ }
315
+
316
+ // ---------------------------------------------------------------------------
317
+ // provider show
318
+ // ---------------------------------------------------------------------------
319
+
320
+ function handleShow(args: string[]): void {
321
+ const restArgs = [...args];
322
+ const wantsJson = consumeFlag(restArgs, "--json");
323
+ const name = restArgs[0];
324
+ if (!name || name.startsWith("-")) {
325
+ console.error("Usage: ocx provider show <name> [--json]");
326
+ process.exit(1);
327
+ }
328
+ rejectUnknownArgs(restArgs.slice(1), "Usage: ocx provider show <name> [--json]");
329
+
330
+ const config = loadConfig();
331
+ if (!hasOwnProvider(config.providers, name)) {
332
+ console.error(`Provider "${name}" is not configured.`);
333
+ process.exit(1);
334
+ }
335
+
336
+ const prov = config.providers[name];
337
+ const display = {
338
+ ...prov,
339
+ ...(prov.apiKey ? { apiKey: maskSecret(prov.apiKey) } : {}),
340
+ ...(prov.apiKeyPool ? { apiKeyPool: prov.apiKeyPool.map(e => ({ ...e, key: maskSecret(e.key) })) } : {}),
341
+ };
342
+
343
+ if (wantsJson) {
344
+ console.log(JSON.stringify({ name, isDefault: name === config.defaultProvider, ...display }, null, 2));
345
+ return;
346
+ }
347
+
348
+ console.log(`Provider: ${name}${name === config.defaultProvider ? " (default)" : ""}`);
349
+ console.log(` adapter: ${display.adapter}`);
350
+ console.log(` baseUrl: ${display.baseUrl}`);
351
+ if (display.authMode) console.log(` authMode: ${display.authMode}`);
352
+ if (display.apiKey) console.log(` apiKey: ${display.apiKey}`);
353
+ if (display.defaultModel) console.log(` defaultModel: ${display.defaultModel}`);
354
+ if (display.models?.length) console.log(` models: ${display.models.join(", ")}`);
355
+ }
356
+
357
+ // ---------------------------------------------------------------------------
358
+ // provider set-default
359
+ // ---------------------------------------------------------------------------
360
+
361
+ function handleSetDefault(args: string[]): void {
362
+ const restArgs = [...args];
363
+ const wantsJson = consumeFlag(restArgs, "--json");
364
+ const name = restArgs[0];
365
+ if (!name || name.startsWith("-")) {
366
+ console.error("Usage: ocx provider set-default <name> [--json]");
367
+ process.exit(1);
368
+ }
369
+ rejectUnknownArgs(restArgs.slice(1), "Usage: ocx provider set-default <name> [--json]");
370
+
371
+ const config = loadConfig();
372
+ if (!hasOwnProvider(config.providers, name)) {
373
+ console.error(`Provider "${name}" is not configured. Add it first: ocx provider add ${name}`);
374
+ process.exit(1);
375
+ }
376
+
377
+ if (config.defaultProvider === name) {
378
+ if (wantsJson) {
379
+ console.log(JSON.stringify({ action: "noop", provider: name, defaultProvider: name, needsSync: false }, null, 2));
380
+ } else {
381
+ console.log(`"${name}" is already the default provider.`);
382
+ }
383
+ return;
384
+ }
385
+
386
+ config.defaultProvider = name;
387
+ validateAndSave(config);
388
+
389
+
390
+ if (wantsJson) {
391
+ console.log(JSON.stringify({ action: "set-default", provider: name, defaultProvider: name, needsSync: true }, null, 2));
392
+ return;
393
+ }
394
+
395
+ console.log(`✅ Default provider set to "${name}".`);
396
+ }
397
+
398
+ // ---------------------------------------------------------------------------
399
+ // Router (F2 fix: handle help flags internally, like service/codex-shim)
400
+ // ---------------------------------------------------------------------------
401
+
402
+ const PROVIDER_USAGE = `Usage: ocx provider <subcommand>
403
+
404
+ Subcommands:
405
+ list List configured and available providers
406
+ add <name> Add a provider (registry or custom)
407
+ edit <name> Edit live provider fields
408
+ test <name> Test the provider's upstream model endpoint
409
+ remove <name> Remove a configured provider
410
+ show <name> Show provider config details
411
+ set-default <name> Change the default provider
412
+ selected <name> Show or set the provider model allowlist
413
+ quota Show provider quota reports
414
+ presets List GUI provider presets
415
+ account-mode <mode> Set OpenAI Codex pool/direct mode
416
+
417
+ Examples:
418
+ ocx provider list
419
+ ocx provider add anthropic --api-key sk-ant-...
420
+ ocx provider add my-ollama --adapter openai-chat --base-url http://localhost:11434/v1
421
+ ocx provider show anthropic --json
422
+ ocx provider set-default anthropic
423
+ ocx provider remove my-ollama`;
424
+
425
+ export async function handleProviderCommand(args: string[]): Promise<void> {
426
+ const sub = args[0];
427
+
428
+ if (!sub || sub === "help" || hasHelpFlag(args)) {
429
+ console.log(PROVIDER_USAGE);
430
+ process.exit(0);
431
+ }
432
+
433
+ const subArgs = args.slice(1);
434
+
435
+ switch (sub) {
436
+ case "list":
437
+ handleList(subArgs);
438
+ break;
439
+ case "add":
440
+ await handleAdd(subArgs);
441
+ break;
442
+ case "remove":
443
+ handleRemove(subArgs);
444
+ break;
445
+ case "show":
446
+ handleShow(subArgs);
447
+ break;
448
+ case "set-default":
449
+ handleSetDefault(subArgs);
450
+ break;
451
+ default: {
452
+ const { handleProviderRuntimeCommand } = await import("./provider-runtime");
453
+ const code = await handleProviderRuntimeCommand(sub, subArgs);
454
+ if (code !== null) {
455
+ process.exitCode = code;
456
+ break;
457
+ }
458
+ console.error(`Unknown provider subcommand: ${sub}`);
459
+ console.error(PROVIDER_USAGE);
460
+ process.exit(1);
461
+ }
462
+ }
463
+ }