@remodex/rmx 1.0.2

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 (588) hide show
  1. package/AGENTS_INSTALL.md +91 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/assets/architecture.png +0 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/claude-code-models.gif +0 -0
  7. package/assets/codex-app-picker.png +0 -0
  8. package/bin/ocx.mjs +584 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-CZqebSPQ.css +1 -0
  11. package/gui/dist/assets/index-CkETtt7P.js +71 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/fonts/google-sans-cyrillic.woff2 +0 -0
  14. package/gui/dist/fonts/google-sans-latin.woff2 +0 -0
  15. package/gui/dist/icons.svg +24 -0
  16. package/gui/dist/index.html +25 -0
  17. package/gui/dist/logo.png +0 -0
  18. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  19. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  20. package/gui/dist/provider-icons/claude-color.svg +1 -0
  21. package/gui/dist/provider-icons/cline-color.svg +16 -0
  22. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  23. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  24. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  25. package/gui/dist/provider-icons/cursor-color.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/github-copilot-color.svg +1 -0
  32. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  33. package/gui/dist/provider-icons/grok.svg +1 -0
  34. package/gui/dist/provider-icons/groq-color.svg +1 -0
  35. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  36. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  37. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  38. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  39. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  40. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  41. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  42. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  43. package/gui/dist/provider-icons/openai.svg +1 -0
  44. package/gui/dist/provider-icons/opencode.svg +2 -0
  45. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  46. package/gui/dist/provider-icons/pi.svg +21 -0
  47. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  48. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  49. package/gui/dist/provider-icons/telegram.svg +1 -0
  50. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  51. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  52. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  53. package/package.json +118 -0
  54. package/src/AGENTS.md +28 -0
  55. package/src/adapters/anthropic-image-guard.ts +251 -0
  56. package/src/adapters/anthropic-image-normalize.ts +518 -0
  57. package/src/adapters/anthropic.ts +1205 -0
  58. package/src/adapters/azure.ts +36 -0
  59. package/src/adapters/base.ts +83 -0
  60. package/src/adapters/client-fingerprint.ts +59 -0
  61. package/src/adapters/command-code.ts +453 -0
  62. package/src/adapters/cursor/arg-codec.ts +38 -0
  63. package/src/adapters/cursor/arg-normalize.ts +104 -0
  64. package/src/adapters/cursor/cursor-errors.ts +165 -0
  65. package/src/adapters/cursor/discovery.ts +276 -0
  66. package/src/adapters/cursor/effort-map.ts +139 -0
  67. package/src/adapters/cursor/exec-policy.ts +88 -0
  68. package/src/adapters/cursor/framing.ts +250 -0
  69. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  70. package/src/adapters/cursor/kv-store.ts +52 -0
  71. package/src/adapters/cursor/live-models.ts +153 -0
  72. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  73. package/src/adapters/cursor/live-transport.ts +1235 -0
  74. package/src/adapters/cursor/mcp-config.ts +42 -0
  75. package/src/adapters/cursor/mcp-manager.ts +333 -0
  76. package/src/adapters/cursor/message-mapper.ts +49 -0
  77. package/src/adapters/cursor/native-exec-common.ts +59 -0
  78. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  79. package/src/adapters/cursor/native-exec-fs.ts +332 -0
  80. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  81. package/src/adapters/cursor/native-exec-network.ts +43 -0
  82. package/src/adapters/cursor/native-exec-shell.ts +548 -0
  83. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  84. package/src/adapters/cursor/native-exec.ts +604 -0
  85. package/src/adapters/cursor/protobuf-events.ts +735 -0
  86. package/src/adapters/cursor/protobuf-request.ts +719 -0
  87. package/src/adapters/cursor/request-builder.ts +280 -0
  88. package/src/adapters/cursor/thread-continuity.ts +67 -0
  89. package/src/adapters/cursor/tool-definitions.ts +621 -0
  90. package/src/adapters/cursor/transport-retry.ts +132 -0
  91. package/src/adapters/cursor/transport.ts +57 -0
  92. package/src/adapters/cursor/types.ts +59 -0
  93. package/src/adapters/cursor.ts +196 -0
  94. package/src/adapters/google-antigravity-replay.ts +520 -0
  95. package/src/adapters/google-antigravity-wire.ts +140 -0
  96. package/src/adapters/google-errors.ts +85 -0
  97. package/src/adapters/google-http.ts +100 -0
  98. package/src/adapters/google-tool-schema.ts +173 -0
  99. package/src/adapters/google-truncation.ts +24 -0
  100. package/src/adapters/google-wire-compiler.ts +232 -0
  101. package/src/adapters/google.ts +859 -0
  102. package/src/adapters/identity.ts +77 -0
  103. package/src/adapters/image.ts +23 -0
  104. package/src/adapters/kiro-constants.ts +16 -0
  105. package/src/adapters/kiro-errors.ts +208 -0
  106. package/src/adapters/kiro-events.ts +197 -0
  107. package/src/adapters/kiro-images.ts +129 -0
  108. package/src/adapters/kiro-retry.ts +312 -0
  109. package/src/adapters/kiro-thinking.ts +104 -0
  110. package/src/adapters/kiro-tool-fallback.ts +36 -0
  111. package/src/adapters/kiro-tools.ts +224 -0
  112. package/src/adapters/kiro-truncation.ts +33 -0
  113. package/src/adapters/kiro-wire.ts +129 -0
  114. package/src/adapters/kiro.ts +1924 -0
  115. package/src/adapters/mimo-free.ts +263 -0
  116. package/src/adapters/openai-chat.ts +1265 -0
  117. package/src/adapters/openai-responses.ts +1309 -0
  118. package/src/adapters/run-turn-queue.ts +114 -0
  119. package/src/adapters/tool-catalog-nudge.ts +71 -0
  120. package/src/adapters/upstream-http-error.ts +48 -0
  121. package/src/android-remote/assets.ts +218 -0
  122. package/src/android-remote/attachments.ts +168 -0
  123. package/src/android-remote/auth.ts +237 -0
  124. package/src/android-remote/cloudflare-provisioning.ts +409 -0
  125. package/src/android-remote/cloudflare-secret.ts +106 -0
  126. package/src/android-remote/cloudflare-tunnel.ts +488 -0
  127. package/src/android-remote/cloudflared.ts +286 -0
  128. package/src/android-remote/codex-app-server.ts +565 -0
  129. package/src/android-remote/desktop-history-page.ts +936 -0
  130. package/src/android-remote/desktop-ipc.ts +3643 -0
  131. package/src/android-remote/desktop-ownership-store.ts +98 -0
  132. package/src/android-remote/desktop-project-registration.ts +129 -0
  133. package/src/android-remote/desktop-session-stream.ts +1110 -0
  134. package/src/android-remote/desktop-workspace-state.ts +443 -0
  135. package/src/android-remote/file-change-parser.ts +112 -0
  136. package/src/android-remote/gateway.ts +9108 -0
  137. package/src/android-remote/mutation-store.ts +299 -0
  138. package/src/android-remote/projection.ts +1780 -0
  139. package/src/android-remote/queued-turn-store.ts +248 -0
  140. package/src/android-remote/session-command-recovery.ts +1384 -0
  141. package/src/android-remote/store.ts +466 -0
  142. package/src/android-remote/thread-reconciliation.ts +133 -0
  143. package/src/android-remote/thread-source-paths.ts +307 -0
  144. package/src/android-remote/thread-stream.ts +546 -0
  145. package/src/android-remote/turn-activity.ts +235 -0
  146. package/src/android-remote/user-message-identity.ts +94 -0
  147. package/src/bridge.ts +1793 -0
  148. package/src/chat/inbound.ts +295 -0
  149. package/src/chat/outbound.ts +821 -0
  150. package/src/claude/agents-inject.ts +267 -0
  151. package/src/claude/alias.ts +149 -0
  152. package/src/claude/auth-detect.ts +229 -0
  153. package/src/claude/auth-mode-migration.ts +32 -0
  154. package/src/claude/auth-mode.ts +62 -0
  155. package/src/claude/context-windows.ts +189 -0
  156. package/src/claude/desktop-3p-guard.ts +35 -0
  157. package/src/claude/desktop-3p-paths.ts +84 -0
  158. package/src/claude/desktop-3p.ts +601 -0
  159. package/src/claude/desktop-health.ts +26 -0
  160. package/src/claude/desktop-profile.ts +263 -0
  161. package/src/claude/gateway-cache.ts +70 -0
  162. package/src/claude/inbound-debug.ts +163 -0
  163. package/src/claude/inbound.ts +519 -0
  164. package/src/claude/model-info.ts +154 -0
  165. package/src/claude/outbound.ts +898 -0
  166. package/src/cli/access.ts +108 -0
  167. package/src/cli/account-api.ts +296 -0
  168. package/src/cli/account-auth.ts +250 -0
  169. package/src/cli/account-catalog-refresh.ts +14 -0
  170. package/src/cli/account-extended.ts +476 -0
  171. package/src/cli/account-main.ts +317 -0
  172. package/src/cli/account.ts +297 -0
  173. package/src/cli/agent-driven.ts +70 -0
  174. package/src/cli/agent.ts +184 -0
  175. package/src/cli/catalog-prewarm.ts +27 -0
  176. package/src/cli/claude-desktop.ts +211 -0
  177. package/src/cli/claude.ts +302 -0
  178. package/src/cli/codex-shim-autorestore.ts +45 -0
  179. package/src/cli/codex-shim-readiness.ts +69 -0
  180. package/src/cli/combo.ts +124 -0
  181. package/src/cli/config-command.ts +183 -0
  182. package/src/cli/debug.ts +228 -0
  183. package/src/cli/desktop-first-run.ts +25 -0
  184. package/src/cli/doctor.ts +1022 -0
  185. package/src/cli/export-command.ts +201 -0
  186. package/src/cli/help.ts +370 -0
  187. package/src/cli/index.ts +1565 -0
  188. package/src/cli/init.ts +224 -0
  189. package/src/cli/integrations.ts +225 -0
  190. package/src/cli/interactive-confirm.ts +133 -0
  191. package/src/cli/internal-dispatch.ts +35 -0
  192. package/src/cli/launcher-context.ts +77 -0
  193. package/src/cli/models-runtime.ts +224 -0
  194. package/src/cli/models.ts +340 -0
  195. package/src/cli/observe.ts +170 -0
  196. package/src/cli/opencode.ts +587 -0
  197. package/src/cli/provider-runtime.ts +179 -0
  198. package/src/cli/provider.ts +476 -0
  199. package/src/cli/ready.ts +301 -0
  200. package/src/cli/route-policy.ts +92 -0
  201. package/src/cli/runtime-api.ts +328 -0
  202. package/src/cli/star-prompt.ts +211 -0
  203. package/src/cli/status-oauth.ts +78 -0
  204. package/src/cli/status.ts +321 -0
  205. package/src/cli/system-command.ts +196 -0
  206. package/src/cli/system-restart-client.ts +146 -0
  207. package/src/cli/tray-proxy.ts +205 -0
  208. package/src/cli/v2.ts +200 -0
  209. package/src/cli.ts +10 -0
  210. package/src/clients/config-export.ts +1109 -0
  211. package/src/codex/account-id.ts +34 -0
  212. package/src/codex/account-label.ts +34 -0
  213. package/src/codex/account-lifecycle.ts +172 -0
  214. package/src/codex/account-namespace-match.ts +63 -0
  215. package/src/codex/account-namespaces.ts +195 -0
  216. package/src/codex/account-pause.ts +20 -0
  217. package/src/codex/account-priority.ts +83 -0
  218. package/src/codex/account-runtime-state.ts +31 -0
  219. package/src/codex/account-store.ts +517 -0
  220. package/src/codex/account-usability.ts +40 -0
  221. package/src/codex/admission.ts +263 -0
  222. package/src/codex/app-server-processes.ts +799 -0
  223. package/src/codex/auth-api.ts +2098 -0
  224. package/src/codex/auth-collision.ts +107 -0
  225. package/src/codex/auth-context.ts +480 -0
  226. package/src/codex/autostart-health.ts +156 -0
  227. package/src/codex/catalog/account-models.ts +67 -0
  228. package/src/codex/catalog/aggregation.ts +471 -0
  229. package/src/codex/catalog/bundled.ts +533 -0
  230. package/src/codex/catalog/effort.ts +432 -0
  231. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  232. package/src/codex/catalog/kinds.ts +2 -0
  233. package/src/codex/catalog/metadata.ts +287 -0
  234. package/src/codex/catalog/native-models.ts +7 -0
  235. package/src/codex/catalog/parsing.ts +503 -0
  236. package/src/codex/catalog/provider-fetch.ts +2267 -0
  237. package/src/codex/catalog/sync.ts +1606 -0
  238. package/src/codex/catalog-admission.ts +197 -0
  239. package/src/codex/catalog-refresh-status.ts +87 -0
  240. package/src/codex/catalog-write-serialization.ts +242 -0
  241. package/src/codex/catalog.ts +15 -0
  242. package/src/codex/codex-write-lock.ts +384 -0
  243. package/src/codex/convergence-types.ts +593 -0
  244. package/src/codex/convergence.ts +580 -0
  245. package/src/codex/custom-model-catalog-migration.ts +176 -0
  246. package/src/codex/data/upstream-models.json +830 -0
  247. package/src/codex/desired-state.ts +230 -0
  248. package/src/codex/desktop-client-processes.ts +521 -0
  249. package/src/codex/exec-invocation.ts +22 -0
  250. package/src/codex/features.ts +1091 -0
  251. package/src/codex/generation.ts +202 -0
  252. package/src/codex/history-job.ts +347 -0
  253. package/src/codex/history-lock.ts +242 -0
  254. package/src/codex/history-migration-guardian.ts +115 -0
  255. package/src/codex/history-provider.ts +1075 -0
  256. package/src/codex/history-transition.ts +105 -0
  257. package/src/codex/history-worker.ts +204 -0
  258. package/src/codex/home.ts +206 -0
  259. package/src/codex/inject-coordination.ts +257 -0
  260. package/src/codex/inject.ts +1857 -0
  261. package/src/codex/injected-marker.ts +79 -0
  262. package/src/codex/integration-record.ts +266 -0
  263. package/src/codex/internal/catalog-writer.ts +203 -0
  264. package/src/codex/internal/history-writer.ts +105 -0
  265. package/src/codex/journal.ts +172 -0
  266. package/src/codex/main-account-cache.ts +56 -0
  267. package/src/codex/main-account.ts +40 -0
  268. package/src/codex/management-convergence.ts +114 -0
  269. package/src/codex/model-cache.ts +267 -0
  270. package/src/codex/native-main-admission.ts +47 -0
  271. package/src/codex/native-main-auth-temp.ts +187 -0
  272. package/src/codex/native-main-claim.ts +167 -0
  273. package/src/codex/native-main-lock-file.ts +162 -0
  274. package/src/codex/native-main-owner.ts +329 -0
  275. package/src/codex/native-profile-api.ts +247 -0
  276. package/src/codex/native-profile-manager.ts +1531 -0
  277. package/src/codex/native-profile-processes.ts +121 -0
  278. package/src/codex/native-profile-recovery.ts +99 -0
  279. package/src/codex/native-profile-stage-store.ts +387 -0
  280. package/src/codex/native-profile-startup.ts +348 -0
  281. package/src/codex/native-profile-store.ts +855 -0
  282. package/src/codex/native-profile-types.ts +120 -0
  283. package/src/codex/native-residue.ts +691 -0
  284. package/src/codex/paths.ts +78 -0
  285. package/src/codex/plugins-doctor.ts +242 -0
  286. package/src/codex/pool-rotation.ts +295 -0
  287. package/src/codex/project-config-warnings.ts +426 -0
  288. package/src/codex/prompt-journal.ts +311 -0
  289. package/src/codex/prompt-layers.ts +967 -0
  290. package/src/codex/prompt-lock.ts +143 -0
  291. package/src/codex/provider-adoption.ts +242 -0
  292. package/src/codex/quota-rejection.ts +224 -0
  293. package/src/codex/quota.ts +494 -0
  294. package/src/codex/refresh.ts +60 -0
  295. package/src/codex/routing.ts +1855 -0
  296. package/src/codex/runtime.ts +659 -0
  297. package/src/codex/shim.ts +1215 -0
  298. package/src/codex/subagent-defaults.ts +557 -0
  299. package/src/codex/subagent-model-fallback.ts +560 -0
  300. package/src/codex/sync.ts +238 -0
  301. package/src/codex/transition-state.ts +612 -0
  302. package/src/codex/upstream-host-health.ts +368 -0
  303. package/src/codex/user-identity.ts +374 -0
  304. package/src/codex/warmup.ts +192 -0
  305. package/src/codex/websocket-registry.ts +100 -0
  306. package/src/codex/write-coordination.ts +114 -0
  307. package/src/combos/failover.ts +140 -0
  308. package/src/combos/index.ts +44 -0
  309. package/src/combos/request.ts +64 -0
  310. package/src/combos/resolve.ts +232 -0
  311. package/src/combos/types.ts +392 -0
  312. package/src/config.ts +3270 -0
  313. package/src/generated/model-metadata.ts +144 -0
  314. package/src/github/star-state.ts +203 -0
  315. package/src/grok/inject.ts +540 -0
  316. package/src/grok/inspect.ts +45 -0
  317. package/src/grok/status.ts +127 -0
  318. package/src/grok/sync.ts +66 -0
  319. package/src/images/artifacts.ts +516 -0
  320. package/src/images/fulfill-video.ts +163 -0
  321. package/src/images/fulfill.ts +149 -0
  322. package/src/images/index.ts +4 -0
  323. package/src/images/loop.ts +922 -0
  324. package/src/images/plan.ts +133 -0
  325. package/src/images/synthetic-tool.ts +133 -0
  326. package/src/images/types.ts +41 -0
  327. package/src/images/xai-client.ts +141 -0
  328. package/src/images/xai-video-client.ts +163 -0
  329. package/src/index.ts +22 -0
  330. package/src/integrations/config-io.ts +151 -0
  331. package/src/integrations/journal.ts +315 -0
  332. package/src/integrations/merge.ts +135 -0
  333. package/src/integrations/native/ownership-preflight.ts +202 -0
  334. package/src/integrations/ownership.ts +111 -0
  335. package/src/integrations/registry.ts +108 -0
  336. package/src/integrations/serialize.ts +235 -0
  337. package/src/integrations/state.ts +290 -0
  338. package/src/integrations/store.ts +103 -0
  339. package/src/integrations/writer.ts +492 -0
  340. package/src/lib/abort.ts +146 -0
  341. package/src/lib/admin-secrets.ts +25 -0
  342. package/src/lib/admission.ts +83 -0
  343. package/src/lib/app-owned-memory-stores.ts +173 -0
  344. package/src/lib/app-owned-memory.ts +265 -0
  345. package/src/lib/bounded-body.ts +242 -0
  346. package/src/lib/bun-binary-validator.d.mts +3 -0
  347. package/src/lib/bun-binary-validator.mjs +18 -0
  348. package/src/lib/bun-runtime.ts +184 -0
  349. package/src/lib/bun-stream-caps.ts +127 -0
  350. package/src/lib/config-ownership.ts +438 -0
  351. package/src/lib/crash-guard.ts +344 -0
  352. package/src/lib/debug-log-buffer.ts +83 -0
  353. package/src/lib/debug-settings.ts +108 -0
  354. package/src/lib/debug.ts +31 -0
  355. package/src/lib/destination-policy.ts +316 -0
  356. package/src/lib/errors.ts +364 -0
  357. package/src/lib/eventstream-decoder.ts +253 -0
  358. package/src/lib/gcp-adc.ts +341 -0
  359. package/src/lib/injection-debug-log.ts +58 -0
  360. package/src/lib/local-management-attestation.ts +51 -0
  361. package/src/lib/open-url.ts +25 -0
  362. package/src/lib/pinned-http.ts +182 -0
  363. package/src/lib/privacy.ts +20 -0
  364. package/src/lib/process-control.ts +168 -0
  365. package/src/lib/provider-environment.ts +470 -0
  366. package/src/lib/provider-outbound.ts +203 -0
  367. package/src/lib/provider-url.ts +14 -0
  368. package/src/lib/proxy-env.ts +18 -0
  369. package/src/lib/redact.ts +510 -0
  370. package/src/lib/remodex-home.ts +616 -0
  371. package/src/lib/retry-after.ts +55 -0
  372. package/src/lib/service-secrets.ts +178 -0
  373. package/src/lib/shadow-call.ts +54 -0
  374. package/src/lib/sidecar-tracker.ts +52 -0
  375. package/src/lib/sse-decoder.ts +364 -0
  376. package/src/lib/state-store-registrations.ts +109 -0
  377. package/src/lib/state-store-sweeper.ts +184 -0
  378. package/src/lib/system-restart-contract.ts +73 -0
  379. package/src/lib/test-home-guard.ts +98 -0
  380. package/src/lib/token-estimate.ts +69 -0
  381. package/src/lib/translator-budget.ts +366 -0
  382. package/src/lib/upstream-reachability.ts +91 -0
  383. package/src/lib/upstream-retry.ts +508 -0
  384. package/src/lib/win-exec.ts +115 -0
  385. package/src/lib/win-paths.ts +68 -0
  386. package/src/lib/windows-elevation.ts +705 -0
  387. package/src/lib/windows-secret-acl.ts +817 -0
  388. package/src/lib/windows-user-principal.ts +283 -0
  389. package/src/lib/winsw.ts +402 -0
  390. package/src/model-sources.ts +73 -0
  391. package/src/oauth/anthropic-routing.ts +594 -0
  392. package/src/oauth/anthropic.ts +177 -0
  393. package/src/oauth/callback-server.ts +294 -0
  394. package/src/oauth/chatgpt.ts +150 -0
  395. package/src/oauth/command-code.ts +239 -0
  396. package/src/oauth/cursor.ts +231 -0
  397. package/src/oauth/github-copilot.ts +428 -0
  398. package/src/oauth/google-antigravity.ts +230 -0
  399. package/src/oauth/health.ts +443 -0
  400. package/src/oauth/index.ts +1280 -0
  401. package/src/oauth/key-providers.ts +128 -0
  402. package/src/oauth/kimi.ts +213 -0
  403. package/src/oauth/kiro-credentials.ts +726 -0
  404. package/src/oauth/kiro.ts +621 -0
  405. package/src/oauth/local-token-detect.ts +121 -0
  406. package/src/oauth/log.ts +48 -0
  407. package/src/oauth/login-cli.ts +163 -0
  408. package/src/oauth/pkce.ts +15 -0
  409. package/src/oauth/store.ts +655 -0
  410. package/src/oauth/token-guardian.ts +309 -0
  411. package/src/oauth/types.ts +62 -0
  412. package/src/oauth/xai.ts +241 -0
  413. package/src/providers/alibaba-region-backup.ts +75 -0
  414. package/src/providers/alibaba-region-migration.ts +156 -0
  415. package/src/providers/alibaba-region-startup.ts +36 -0
  416. package/src/providers/antigravity-models.ts +317 -0
  417. package/src/providers/api-keys.ts +140 -0
  418. package/src/providers/base-url-choices.ts +64 -0
  419. package/src/providers/codex-capacity.ts +288 -0
  420. package/src/providers/command-code-efforts.ts +85 -0
  421. package/src/providers/context-cap.ts +73 -0
  422. package/src/providers/derive.ts +451 -0
  423. package/src/providers/free-directory.ts +187 -0
  424. package/src/providers/github-copilot-transport.ts +56 -0
  425. package/src/providers/google-vertex-location.ts +14 -0
  426. package/src/providers/key-failover.ts +271 -0
  427. package/src/providers/kiro-models.ts +67 -0
  428. package/src/providers/label.ts +19 -0
  429. package/src/providers/model-discovery-limits.ts +16 -0
  430. package/src/providers/model-discovery.ts +361 -0
  431. package/src/providers/openai-sidecar.ts +235 -0
  432. package/src/providers/openai-tier-startup.ts +27 -0
  433. package/src/providers/openai-tiers.ts +301 -0
  434. package/src/providers/openai-virtual-models.ts +83 -0
  435. package/src/providers/openrouter-routing.ts +102 -0
  436. package/src/providers/provider-id-rewrite.ts +179 -0
  437. package/src/providers/quota.ts +1942 -0
  438. package/src/providers/reasoning-capabilities.ts +336 -0
  439. package/src/providers/registry.ts +2375 -0
  440. package/src/providers/slug-codec.ts +74 -0
  441. package/src/providers/xai-transport.ts +149 -0
  442. package/src/reasoning-effort.ts +243 -0
  443. package/src/responses/compaction.ts +124 -0
  444. package/src/responses/hosted-tool-policy.ts +9 -0
  445. package/src/responses/parser.ts +714 -0
  446. package/src/responses/reasoning-envelope.ts +60 -0
  447. package/src/responses/reasoning-replay-cache.ts +106 -0
  448. package/src/responses/schema.ts +159 -0
  449. package/src/responses/spill-store.ts +431 -0
  450. package/src/responses/state.ts +1039 -0
  451. package/src/responses/tool-groups.ts +19 -0
  452. package/src/router.ts +802 -0
  453. package/src/routing/analytics.ts +377 -0
  454. package/src/routing/capability.ts +205 -0
  455. package/src/routing/cost.ts +77 -0
  456. package/src/routing/evaluator.ts +444 -0
  457. package/src/routing/health.ts +401 -0
  458. package/src/routing/history/cursor.ts +43 -0
  459. package/src/routing/history/indexer.ts +605 -0
  460. package/src/routing/history/schema.ts +72 -0
  461. package/src/routing/profile-namespace.ts +15 -0
  462. package/src/routing/profile.ts +424 -0
  463. package/src/routing/quota.ts +145 -0
  464. package/src/routing/request-evidence.ts +45 -0
  465. package/src/routing/trace.ts +686 -0
  466. package/src/server/adapter-resolve.ts +83 -0
  467. package/src/server/auth-cors.ts +606 -0
  468. package/src/server/chat-completions.ts +379 -0
  469. package/src/server/claude-messages.ts +980 -0
  470. package/src/server/effort-policy.ts +251 -0
  471. package/src/server/github-copilot-responses-repair.ts +338 -0
  472. package/src/server/gui-static.ts +152 -0
  473. package/src/server/image-retry.ts +42 -0
  474. package/src/server/images.ts +485 -0
  475. package/src/server/index.ts +1633 -0
  476. package/src/server/lifecycle.ts +482 -0
  477. package/src/server/live.ts +609 -0
  478. package/src/server/management/agent-settings-routes.ts +1180 -0
  479. package/src/server/management/android-remote-routes.ts +390 -0
  480. package/src/server/management/api-access.ts +141 -0
  481. package/src/server/management/api-key-usage.ts +167 -0
  482. package/src/server/management/body.ts +35 -0
  483. package/src/server/management/combo-routes.ts +244 -0
  484. package/src/server/management/config-routes.ts +602 -0
  485. package/src/server/management/context.ts +88 -0
  486. package/src/server/management/integration-routes.ts +538 -0
  487. package/src/server/management/logs-usage-routes.ts +516 -0
  488. package/src/server/management/model-routes.ts +519 -0
  489. package/src/server/management/model-rows.ts +143 -0
  490. package/src/server/management/native-integration-routes.ts +781 -0
  491. package/src/server/management/oauth-account-routes.ts +573 -0
  492. package/src/server/management/provider-routes.ts +781 -0
  493. package/src/server/management/request-history-routes.ts +191 -0
  494. package/src/server/management/routing-analytics-routes.ts +74 -0
  495. package/src/server/management/routing-profile-routes.ts +384 -0
  496. package/src/server/management/shared.ts +277 -0
  497. package/src/server/management/sidebar-routes.ts +106 -0
  498. package/src/server/management/sync-response.ts +69 -0
  499. package/src/server/management/system-restart.ts +433 -0
  500. package/src/server/management/system-routes.ts +141 -0
  501. package/src/server/management/usage-summary-cache.ts +86 -0
  502. package/src/server/management-api.ts +269 -0
  503. package/src/server/management-auth.ts +353 -0
  504. package/src/server/memory-watchdog.ts +156 -0
  505. package/src/server/port-reclaim.ts +307 -0
  506. package/src/server/ports.ts +156 -0
  507. package/src/server/proxy-liveness.ts +326 -0
  508. package/src/server/proxy-stop.ts +92 -0
  509. package/src/server/readiness.ts +99 -0
  510. package/src/server/relay-eager.ts +353 -0
  511. package/src/server/relay.ts +1179 -0
  512. package/src/server/request-decompress.ts +132 -0
  513. package/src/server/request-log-conversation.ts +168 -0
  514. package/src/server/request-log.ts +1072 -0
  515. package/src/server/responses/collaboration.ts +409 -0
  516. package/src/server/responses/compact.ts +710 -0
  517. package/src/server/responses/core.ts +3561 -0
  518. package/src/server/responses/encrypted-payload.ts +308 -0
  519. package/src/server/responses/fetch-helpers.ts +171 -0
  520. package/src/server/responses/passthrough-error.ts +78 -0
  521. package/src/server/responses/policy-fallback.ts +152 -0
  522. package/src/server/responses/terminal-guard.ts +230 -0
  523. package/src/server/responses/upstream-error.ts +48 -0
  524. package/src/server/responses-image-gen-repair.ts +132 -0
  525. package/src/server/responses-item-id-repair.ts +272 -0
  526. package/src/server/responses-json-events.ts +52 -0
  527. package/src/server/responses-model-rewrite.ts +29 -0
  528. package/src/server/responses-snapshot-repair.ts +621 -0
  529. package/src/server/responses.ts +10 -0
  530. package/src/server/search.ts +181 -0
  531. package/src/server/sse-frame-buffer.ts +292 -0
  532. package/src/server/sse-payload-rewrite.ts +263 -0
  533. package/src/server/startup-action-control.ts +308 -0
  534. package/src/server/startup-health-cache.ts +119 -0
  535. package/src/server/system-env.ts +418 -0
  536. package/src/server/windows-tcp-drop.ts +184 -0
  537. package/src/server/windows-tray-control.ts +41 -0
  538. package/src/server/ws-bridge.ts +470 -0
  539. package/src/service-manager-probe.ts +824 -0
  540. package/src/service.ts +3011 -0
  541. package/src/stall-timeout.ts +20 -0
  542. package/src/storage/cleanup-job.ts +57 -0
  543. package/src/storage/cleanup.ts +3085 -0
  544. package/src/storage/policy-job.ts +457 -0
  545. package/src/storage/policy-scheduler.ts +40 -0
  546. package/src/storage/policy-worker.ts +59 -0
  547. package/src/storage/policy.ts +527 -0
  548. package/src/storage/restore-job.ts +299 -0
  549. package/src/storage/restore-worker.ts +58 -0
  550. package/src/storage/scanner.ts +238 -0
  551. package/src/storage/storage-mutation-coordinator.ts +139 -0
  552. package/src/storage/worker-lifecycle.ts +215 -0
  553. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  554. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  555. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  556. package/src/tray/assets/opencodex-tray.png +0 -0
  557. package/src/tray/windows-tray.ps1 +364 -0
  558. package/src/tray/windows.ts +738 -0
  559. package/src/types.ts +1531 -0
  560. package/src/update/badge.ts +72 -0
  561. package/src/update/desktop-release.ts +1620 -0
  562. package/src/update/index.ts +402 -0
  563. package/src/update/job.ts +1906 -0
  564. package/src/update/notify.ts +261 -0
  565. package/src/update/npm-cache-preflight.d.mts +47 -0
  566. package/src/update/npm-cache-preflight.mjs +201 -0
  567. package/src/update/npm-invocation.d.mts +23 -0
  568. package/src/update/npm-invocation.mjs +94 -0
  569. package/src/update/tray-update-plan.d.mts +18 -0
  570. package/src/update/tray-update-plan.mjs +38 -0
  571. package/src/usage/cost.ts +0 -0
  572. package/src/usage/debug.ts +97 -0
  573. package/src/usage/expected-prices.ts +283 -0
  574. package/src/usage/log.ts +695 -0
  575. package/src/usage/summary.ts +585 -0
  576. package/src/usage/totals.ts +14 -0
  577. package/src/vision/anthropic-describe.ts +185 -0
  578. package/src/vision/describe.ts +127 -0
  579. package/src/vision/index.ts +558 -0
  580. package/src/vision/reasoning.ts +55 -0
  581. package/src/web-search/anthropic-executor.ts +189 -0
  582. package/src/web-search/executor.ts +105 -0
  583. package/src/web-search/format-result.ts +89 -0
  584. package/src/web-search/index.ts +196 -0
  585. package/src/web-search/loop.ts +791 -0
  586. package/src/web-search/parse.ts +235 -0
  587. package/src/web-search/progress-stream.ts +342 -0
  588. package/src/web-search/synthetic-tool.ts +47 -0
@@ -0,0 +1,329 @@
1
+ import { join } from "node:path";
2
+
3
+ import { Database } from "bun:sqlite";
4
+
5
+ import {
6
+ assertStableLockFile,
7
+ hardenStableLockFile,
8
+ openStableLockFile,
9
+ type StableLockFile,
10
+ } from "./native-main-lock-file";
11
+ import type { NativeProfileContext } from "./native-profile-store";
12
+ import { NativeProfileError } from "./native-profile-types";
13
+
14
+ export const NATIVE_MAIN_OWNER_RETRY_MS = 250;
15
+ export const NATIVE_MAIN_OWNER_DB = ".opencodex-native-main.owner.sqlite";
16
+
17
+ export type NativeMainOwnerUnavailableReason = "lock-unavailable" | "unsupported-filesystem";
18
+
19
+ export type NativeMainOwnerSnapshot =
20
+ | { status: "acquiring"; homeId: string }
21
+ | { status: "contended"; homeId: string }
22
+ | { status: "held"; homeId: string }
23
+ | { status: "unavailable"; homeId: string; reason: NativeMainOwnerUnavailableReason }
24
+ | { status: "closing"; homeId: string }
25
+ | { status: "released"; homeId: string };
26
+
27
+ export interface NativeMainOwnerOptions {
28
+ retryMs?: number;
29
+ hardenPath?: (path: string) => Promise<void>;
30
+ platform?: NodeJS.Platform;
31
+ env?: NodeJS.ProcessEnv;
32
+ }
33
+
34
+ export interface NativeMainOwnerReference {
35
+ readonly homeId: string;
36
+ snapshot(): NativeMainOwnerSnapshot;
37
+ subscribe(listener: (snapshot: NativeMainOwnerSnapshot) => void): () => void;
38
+ release(): Promise<void>;
39
+ }
40
+
41
+ interface OwnerEntry {
42
+ readonly context: NativeProfileContext;
43
+ readonly lockPath: string;
44
+ readonly options: Required<Pick<NativeMainOwnerOptions, "retryMs" | "platform">>
45
+ & Pick<NativeMainOwnerOptions, "env" | "hardenPath">;
46
+ refs: number;
47
+ generation: number;
48
+ snapshot: NativeMainOwnerSnapshot;
49
+ listeners: Set<(snapshot: NativeMainOwnerSnapshot) => void>;
50
+ database?: Database;
51
+ file?: StableLockFile;
52
+ prepared: boolean;
53
+ timer?: ReturnType<typeof setTimeout>;
54
+ drive?: Promise<void>;
55
+ activeOperations: Set<Promise<unknown>>;
56
+ aclTimeoutRetryUsed: boolean;
57
+ closing: boolean;
58
+ }
59
+
60
+ const entries = new Map<string, OwnerEntry>();
61
+ const detachedEntries = new Set<OwnerEntry>();
62
+ let exitHookInstalled = false;
63
+
64
+ function errorCode(error: unknown): string | undefined {
65
+ return error && typeof error === "object" && "code" in error
66
+ ? String((error as { code?: unknown }).code)
67
+ : undefined;
68
+ }
69
+
70
+ function isBusy(error: unknown): boolean {
71
+ const code = errorCode(error);
72
+ const message = error instanceof Error ? error.message : String(error);
73
+ return code === "SQLITE_BUSY" || code === "SQLITE_LOCKED" || /database (?:is|table is) locked/i.test(message);
74
+ }
75
+
76
+ export function nativeMainOwnerFilesystemSupported(
77
+ codexHome: string,
78
+ platform: NodeJS.Platform = process.platform,
79
+ env: NodeJS.ProcessEnv = process.env,
80
+ ): boolean {
81
+ if (platform === "win32") {
82
+ const normalized = codexHome.replaceAll("/", "\\");
83
+ if (normalized.startsWith("\\\\?\\UNC\\")) return false;
84
+ if (normalized.startsWith("\\\\") && !normalized.startsWith("\\\\?\\")) return false;
85
+ }
86
+ if (
87
+ platform === "linux"
88
+ && (env.WSL_INTEROP !== undefined || env.WSL_DISTRO_NAME !== undefined)
89
+ && /^\/mnt\/[a-z](?:\/|$)/i.test(codexHome)
90
+ ) return false;
91
+ return true;
92
+ }
93
+
94
+ function publish(entry: OwnerEntry, snapshot: NativeMainOwnerSnapshot): void {
95
+ entry.snapshot = snapshot;
96
+ for (const listener of entry.listeners) {
97
+ try { listener({ ...snapshot }); } catch { /* an observer cannot forfeit process ownership */ }
98
+ }
99
+ }
100
+
101
+ function closeDatabase(entry: OwnerEntry): void {
102
+ const database = entry.database;
103
+ entry.database = undefined;
104
+ if (!database) return;
105
+ try { database.exec("ROLLBACK"); } catch { /* close still releases the OS-backed lock */ }
106
+ try { database.close(); } catch { /* the process exit path is best-effort */ }
107
+ }
108
+
109
+ function closeEntry(entry: OwnerEntry): void {
110
+ closeDatabase(entry);
111
+ try { entry.file?.close(); } catch { /* the process exit path is best-effort */ }
112
+ entry.file = undefined;
113
+ }
114
+
115
+ function installExitHook(): void {
116
+ if (exitHookInstalled) return;
117
+ exitHookInstalled = true;
118
+ process.once("exit", () => {
119
+ for (const entry of entries.values()) {
120
+ if (entry.timer) clearTimeout(entry.timer);
121
+ closeEntry(entry);
122
+ }
123
+ for (const entry of detachedEntries) closeEntry(entry);
124
+ entries.clear();
125
+ detachedEntries.clear();
126
+ });
127
+ }
128
+
129
+ function scheduleRetry(entry: OwnerEntry, generation: number): void {
130
+ if (entry.closing || entry.refs === 0 || entry.timer) return;
131
+ entry.timer = setTimeout(() => {
132
+ entry.timer = undefined;
133
+ if (entry.generation !== generation || entry.closing || entry.refs === 0) return;
134
+ entry.drive = drive(entry, generation).finally(() => {
135
+ if (entry.generation === generation) entry.drive = undefined;
136
+ });
137
+ }, entry.options.retryMs);
138
+ entry.timer.unref?.();
139
+ }
140
+
141
+ async function prepareOwnerDatabase(entry: OwnerEntry): Promise<void> {
142
+ if (entry.prepared) return;
143
+ let file: StableLockFile | undefined;
144
+ let database: Database | undefined;
145
+ try {
146
+ file = openStableLockFile(entry.lockPath, entry.options.platform);
147
+ database = new Database(entry.lockPath, { create: true });
148
+ // The resolved platform is threaded into the DEFAULT hardener for the same
149
+ // reason the claim path does it: otherwise a test that forces `platform`
150
+ // here still exercises the host's branch, and the production default — the
151
+ // thing that actually hardens the owner's lock file — stays unproved. An
152
+ // audit replaced this fallback with a no-op and 91 tests stayed green.
153
+ if (entry.options.hardenPath) {
154
+ await entry.options.hardenPath(entry.lockPath);
155
+ } else {
156
+ await hardenStableLockFile(entry.lockPath, entry.options.platform, {
157
+ retryTimedOutOnce: entry.aclTimeoutRetryUsed,
158
+ });
159
+ }
160
+ assertStableLockFile(entry.lockPath, file);
161
+ entry.file = file;
162
+ file = undefined;
163
+ } catch (error) {
164
+ try { file?.close(); } catch { /* mapped by caller */ }
165
+ throw error;
166
+ } finally {
167
+ try { database?.close(); } catch { /* the caller publishes the hard failure */ }
168
+ }
169
+ entry.prepared = true;
170
+ }
171
+
172
+ async function drive(entry: OwnerEntry, generation: number): Promise<void> {
173
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation || entry.database) return;
174
+ if (!nativeMainOwnerFilesystemSupported(
175
+ entry.context.codexHome,
176
+ entry.options.platform,
177
+ entry.options.env ?? process.env,
178
+ )) {
179
+ publish(entry, { status: "unavailable", homeId: entry.context.homeId, reason: "unsupported-filesystem" });
180
+ return;
181
+ }
182
+ let candidate: Database | undefined;
183
+ try {
184
+ await prepareOwnerDatabase(entry);
185
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation) return;
186
+ candidate = new Database(entry.lockPath, { create: true });
187
+ candidate.exec("PRAGMA locking_mode = NORMAL; PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
188
+ assertStableLockFile(entry.lockPath, entry.file!);
189
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation) {
190
+ try { candidate.exec("ROLLBACK"); } catch { /* close below */ }
191
+ candidate.close();
192
+ return;
193
+ }
194
+ entry.database = candidate;
195
+ candidate = undefined;
196
+ publish(entry, { status: "held", homeId: entry.context.homeId });
197
+ } catch (error) {
198
+ try { candidate?.close(); } catch { /* acquisition already failed */ }
199
+ if (entry.closing || entry.refs === 0 || entry.generation !== generation) return;
200
+ if (isBusy(error)) {
201
+ publish(entry, { status: "contended", homeId: entry.context.homeId });
202
+ scheduleRetry(entry, generation);
203
+ return;
204
+ }
205
+ if (errorCode(error) === "ETIMEDOUT" && !entry.aclTimeoutRetryUsed) {
206
+ entry.aclTimeoutRetryUsed = true;
207
+ // A timeout is neither ownership contention nor a permanent ACL denial.
208
+ // Stay fail-closed in `acquiring` and spend exactly one fresh-budget retry.
209
+ scheduleRetry(entry, generation);
210
+ return;
211
+ }
212
+ publish(entry, { status: "unavailable", homeId: entry.context.homeId, reason: "lock-unavailable" });
213
+ }
214
+ }
215
+
216
+ function entryFor(context: NativeProfileContext, options: NativeMainOwnerOptions): OwnerEntry {
217
+ const existing = entries.get(context.homeId);
218
+ if (existing) {
219
+ if (existing.context.codexHome !== context.codexHome) {
220
+ throw new NativeProfileError("CODEX_HOME_UNAVAILABLE", "The canonical native-main home changed unexpectedly.", 409);
221
+ }
222
+ return existing;
223
+ }
224
+ const entry: OwnerEntry = {
225
+ context,
226
+ lockPath: join(context.codexHome, NATIVE_MAIN_OWNER_DB),
227
+ options: {
228
+ retryMs: Math.max(1, options.retryMs ?? NATIVE_MAIN_OWNER_RETRY_MS),
229
+ platform: options.platform ?? process.platform,
230
+ env: options.env,
231
+ hardenPath: options.hardenPath,
232
+ },
233
+ refs: 0,
234
+ generation: 1,
235
+ snapshot: { status: "acquiring", homeId: context.homeId },
236
+ listeners: new Set(),
237
+ activeOperations: new Set(),
238
+ aclTimeoutRetryUsed: false,
239
+ prepared: false,
240
+ closing: false,
241
+ };
242
+ entries.set(context.homeId, entry);
243
+ installExitHook();
244
+ return entry;
245
+ }
246
+
247
+ export function retainNativeMainOwner(
248
+ context: NativeProfileContext,
249
+ options: NativeMainOwnerOptions = {},
250
+ ): NativeMainOwnerReference {
251
+ const entry = entryFor(context, options);
252
+ if (entry.closing) {
253
+ throw new NativeProfileError("NATIVE_MAIN_OWNER_BUSY", "Native-main ownership is closing.", 503, true);
254
+ }
255
+ entry.refs += 1;
256
+ if (!entry.drive && !entry.database && !entry.timer && entry.snapshot.status !== "unavailable") {
257
+ const generation = entry.generation;
258
+ entry.drive = drive(entry, generation).finally(() => {
259
+ if (entry.generation === generation) entry.drive = undefined;
260
+ });
261
+ }
262
+ let released = false;
263
+ return {
264
+ homeId: context.homeId,
265
+ snapshot: () => ({ ...entry.snapshot }),
266
+ subscribe(listener) {
267
+ entry.listeners.add(listener);
268
+ listener({ ...entry.snapshot });
269
+ return () => { entry.listeners.delete(listener); };
270
+ },
271
+ async release() {
272
+ if (released) return;
273
+ released = true;
274
+ entry.refs = Math.max(0, entry.refs - 1);
275
+ if (entry.refs !== 0 || entry.closing) return;
276
+ entry.closing = true;
277
+ entry.generation += 1;
278
+ if (entry.timer) clearTimeout(entry.timer);
279
+ entry.timer = undefined;
280
+ publish(entry, { status: "closing", homeId: entry.context.homeId });
281
+ // Detach synchronously before the first await. startServer is synchronous,
282
+ // so a failed listen must be able to create a fresh reference immediately;
283
+ // SQLite will keep that successor contended until this entry finishes close.
284
+ if (entries.get(entry.context.homeId) === entry) entries.delete(entry.context.homeId);
285
+ detachedEntries.add(entry);
286
+ if (entry.drive) await Promise.allSettled([entry.drive]);
287
+ while (entry.activeOperations.size > 0) {
288
+ await Promise.allSettled([...entry.activeOperations]);
289
+ }
290
+ closeEntry(entry);
291
+ publish(entry, { status: "released", homeId: entry.context.homeId });
292
+ detachedEntries.delete(entry);
293
+ },
294
+ };
295
+ }
296
+
297
+ export function nativeMainOwnerSnapshot(context: NativeProfileContext): NativeMainOwnerSnapshot | null {
298
+ const entry = entries.get(context.homeId);
299
+ return entry ? { ...entry.snapshot } : null;
300
+ }
301
+
302
+ export function assertNativeMainOwner(context: NativeProfileContext): void {
303
+ const entry = entries.get(context.homeId);
304
+ if (entry?.snapshot.status === "held" && entry.database && !entry.closing) return;
305
+ const unavailable = entry?.snapshot.status === "unavailable";
306
+ throw new NativeProfileError(
307
+ unavailable ? "NATIVE_MAIN_OWNER_UNAVAILABLE" : "NATIVE_MAIN_OWNER_BUSY",
308
+ unavailable
309
+ ? "This process cannot establish native-main ownership for the effective CODEX_HOME."
310
+ : "Another Remodex process owns the physical native-main login for the effective CODEX_HOME.",
311
+ 503,
312
+ true,
313
+ );
314
+ }
315
+
316
+ export async function withNativeMainOwnerOperation<T>(
317
+ context: NativeProfileContext,
318
+ operation: () => Promise<T>,
319
+ ): Promise<T> {
320
+ const entry = entries.get(context.homeId);
321
+ // Direct manager unit tests and non-server library consumers retain the historical
322
+ // transaction-only behavior. Every live server registers an entry before listen.
323
+ if (!entry) return operation();
324
+ assertNativeMainOwner(context);
325
+ let tracked!: Promise<T>;
326
+ tracked = Promise.resolve().then(operation).finally(() => { entry.activeOperations.delete(tracked); });
327
+ entry.activeOperations.add(tracked);
328
+ return tracked;
329
+ }
@@ -0,0 +1,247 @@
1
+ import type { OcxConfig } from "../types";
2
+ import { jsonResponse } from "../server/auth-cors";
3
+ import { acquireNativeMainProfileDrain, getNativeMainProfileRequestCount } from "../server/lifecycle";
4
+ import {
5
+ managementBodyTooLargeResponse,
6
+ readManagementJsonBody,
7
+ rethrowManagementBodyTooLarge,
8
+ } from "../server/management/body";
9
+ import { NativeProfileManager } from "./native-profile-manager";
10
+ import { NativeProfileError } from "./native-profile-types";
11
+ import {
12
+ blockNativeMainRecovery,
13
+ completeNativeMainRecovery,
14
+ nativeMainStartupGateSnapshot,
15
+ } from "./native-profile-startup";
16
+ import { probeNativeProfileRecoveryState } from "./native-profile-store";
17
+ import { nativeMainOwnerSnapshot, withNativeMainOwnerOperation } from "./native-main-owner";
18
+ import { withNativeMainExclusiveClaim, withNativeMainSharedClaim } from "./native-main-claim";
19
+
20
+ export interface NativeProfileApiDeps {
21
+ manager?: NativeProfileManager;
22
+ drainTimeoutMs?: number;
23
+ sleep?: (ms: number) => Promise<void>;
24
+ probeRecoveryState?: typeof probeNativeProfileRecoveryState;
25
+ blockRecovery?: typeof blockNativeMainRecovery;
26
+ completeRecovery?: typeof completeNativeMainRecovery;
27
+ }
28
+
29
+ type NativeMainApiClaim =
30
+ | { mode: "none" }
31
+ | { mode: "shared" }
32
+ | { mode: "exclusive"; waitMs: number };
33
+
34
+ async function withNativeMainApiOperation<T>(
35
+ manager: NativeProfileManager,
36
+ operation: () => Promise<T>,
37
+ claim: NativeMainApiClaim = { mode: "none" },
38
+ ): Promise<T> {
39
+ // Direct handler unit tests and library callers may inject a partial manager.
40
+ // A live server always constructs a real manager and registers an owner entry
41
+ // before listen, so only that path needs the additional cross-process claim.
42
+ const context = (manager as Partial<NativeProfileManager>).context;
43
+ if (!context) return operation();
44
+ return withNativeMainOwnerOperation(context, () => {
45
+ if (!nativeMainOwnerSnapshot(context) || claim.mode === "none") return operation();
46
+ if (claim.mode === "shared") return withNativeMainSharedClaim(context, operation);
47
+ return withNativeMainExclusiveClaim(context, operation, { waitMs: claim.waitMs });
48
+ });
49
+ }
50
+
51
+ async function body(req: Request): Promise<Record<string, unknown>> {
52
+ try {
53
+ const parsed = await readManagementJsonBody(req);
54
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("body");
55
+ return parsed as Record<string, unknown>;
56
+ } catch (error) {
57
+ rethrowManagementBodyTooLarge(error);
58
+ throw new NativeProfileError("INVALID_REQUEST", "A JSON object body is required.", 400);
59
+ }
60
+ }
61
+
62
+ async function withMainRequestDrain<T>(deps: NativeProfileApiDeps, operation: () => Promise<T>): Promise<T> {
63
+ const drainLease = acquireNativeMainProfileDrain("native-main-profile");
64
+ if (!drainLease) throw new NativeProfileError("MAIN_REQUESTS_ACTIVE", "The proxy is already draining requests.", 503, true);
65
+ try {
66
+ const deadline = Date.now() + (deps.drainTimeoutMs ?? 10_000);
67
+ while (getNativeMainProfileRequestCount() > 0 && Date.now() < deadline) await (deps.sleep ?? Bun.sleep)(50);
68
+ if (getNativeMainProfileRequestCount() > 0) {
69
+ throw new NativeProfileError("MAIN_REQUESTS_ACTIVE", "In-flight requests did not finish before the native-login switch deadline.", 409, true);
70
+ }
71
+ return await operation();
72
+ } finally {
73
+ drainLease.release();
74
+ }
75
+ }
76
+
77
+ async function withRecoveryGateTransition<T>(
78
+ manager: NativeProfileManager,
79
+ deps: NativeProfileApiDeps,
80
+ operation: () => Promise<T>,
81
+ ): Promise<T> {
82
+ const context = manager.context;
83
+ if (!context) return operation();
84
+ const probe = deps.probeRecoveryState ?? probeNativeProfileRecoveryState;
85
+ const block = deps.blockRecovery ?? blockNativeMainRecovery;
86
+ let before: ReturnType<typeof probeNativeProfileRecoveryState>;
87
+ try {
88
+ before = probe(context);
89
+ } catch (error) {
90
+ try { block(context.homeId); } catch { /* preserve the probe failure */ }
91
+ throw error;
92
+ }
93
+ let operationFailed = false;
94
+ try {
95
+ return await operation();
96
+ } catch (error) {
97
+ operationFailed = true;
98
+ throw error;
99
+ } finally {
100
+ let after: ReturnType<typeof probeNativeProfileRecoveryState> = "none";
101
+ let afterProbed = false;
102
+ let transitionError: unknown;
103
+ try {
104
+ after = probe(context);
105
+ afterProbed = true;
106
+ } catch (error) {
107
+ transitionError = error;
108
+ try { block(context.homeId); } catch { /* preserve the operation/probe failure */ }
109
+ }
110
+ if (!afterProbed) {
111
+ if (!operationFailed) throw transitionError;
112
+ } else if (after !== "none") {
113
+ try {
114
+ block(context.homeId, after);
115
+ } catch (error) {
116
+ if (!operationFailed) throw error;
117
+ }
118
+ } else {
119
+ const gate = nativeMainStartupGateSnapshot();
120
+ const matchingGateCanComplete = gate.status === "blocked"
121
+ && gate.homeId === context.homeId
122
+ && (gate.reason === "recovery-pending" || gate.reason === "manual-recovery")
123
+ && (before !== "none" || !operationFailed);
124
+ if (matchingGateCanComplete) {
125
+ try {
126
+ (deps.completeRecovery ?? completeNativeMainRecovery)(context.homeId);
127
+ } catch (transitionError) {
128
+ // Preserve the operation's typed/public error. A post-operation probe or
129
+ // completion failure must not replace it; the gate remains fail closed.
130
+ if (!operationFailed) throw transitionError;
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ export async function handleNativeProfileAPI(
138
+ req: Request,
139
+ url: URL,
140
+ config: OcxConfig,
141
+ deps: NativeProfileApiDeps = {},
142
+ ): Promise<Response | null> {
143
+ if (!url.pathname.startsWith("/api/native-main-profiles")) return null;
144
+ try {
145
+ const manager = deps.manager ?? new NativeProfileManager();
146
+ if (url.pathname === "/api/native-main-profiles" && req.method === "GET") {
147
+ return jsonResponse(await manager.list(), 200, req, config);
148
+ }
149
+ if (url.pathname === "/api/native-main-profiles/doctor" && req.method === "GET") {
150
+ return jsonResponse(await withNativeMainApiOperation(manager, () => manager.doctor(), { mode: "shared" }), 200, req, config);
151
+ }
152
+ if (url.pathname === "/api/native-main-profiles/register" && req.method === "POST") {
153
+ const input = await body(req);
154
+ if (typeof input.label !== "string") throw new NativeProfileError("INVALID_REQUEST", "A profile label is required.", 400);
155
+ return jsonResponse(await withNativeMainApiOperation(
156
+ manager,
157
+ () => manager.register(input.label as string),
158
+ { mode: "shared" },
159
+ ), 200, req, config);
160
+ }
161
+ if (url.pathname === "/api/native-main-profiles/stage" && req.method === "POST") {
162
+ return jsonResponse(await withNativeMainApiOperation(
163
+ manager,
164
+ () => manager.prepareStage(),
165
+ { mode: "shared" },
166
+ ), 200, req, config);
167
+ }
168
+ if (url.pathname === "/api/native-main-profiles/stage/heartbeat" && req.method === "POST") {
169
+ const input = await body(req);
170
+ if (typeof input.stageId !== "string" || typeof input.writerToken !== "string") {
171
+ throw new NativeProfileError("INVALID_REQUEST", "A staging identifier and writer token are required.", 400);
172
+ }
173
+ return jsonResponse(await withNativeMainApiOperation(
174
+ manager,
175
+ () => manager.heartbeatStage(input.stageId as string, input.writerToken as string),
176
+ ), 200, req, config);
177
+ }
178
+ if (url.pathname === "/api/native-main-profiles/stage/finish" && req.method === "POST") {
179
+ const input = await body(req);
180
+ if (typeof input.stageId !== "string" || typeof input.writerToken !== "string" || typeof input.label !== "string") {
181
+ throw new NativeProfileError("INVALID_REQUEST", "A staging identifier, writer token, and profile label are required.", 400);
182
+ }
183
+ return jsonResponse(await withNativeMainApiOperation(
184
+ manager,
185
+ () => manager.finishStage(input.stageId as string, input.writerToken as string, input.label as string),
186
+ { mode: "shared" },
187
+ ), 200, req, config);
188
+ }
189
+ if (url.pathname === "/api/native-main-profiles/stage/cancel" && req.method === "POST") {
190
+ const input = await body(req);
191
+ if (typeof input.stageId !== "string" || typeof input.writerToken !== "string") {
192
+ throw new NativeProfileError("INVALID_REQUEST", "A staging identifier and writer token are required.", 400);
193
+ }
194
+ const cleanup = await withNativeMainApiOperation(
195
+ manager,
196
+ () => manager.cancelStage(input.stageId as string, input.writerToken as string),
197
+ );
198
+ return jsonResponse({ ok: true, ...cleanup }, 200, req, config);
199
+ }
200
+ if (url.pathname === "/api/native-main-profiles/switch" && req.method === "POST") {
201
+ const input = await body(req);
202
+ if (typeof input.target !== "string") throw new NativeProfileError("INVALID_REQUEST", "A target profile is required.", 400);
203
+ const switched = await withMainRequestDrain(deps, () => withRecoveryGateTransition(
204
+ manager,
205
+ deps,
206
+ () => withNativeMainApiOperation(
207
+ manager,
208
+ () => manager.switch(input.target as string, input.confirmedStopped === true),
209
+ { mode: "exclusive", waitMs: deps.drainTimeoutMs ?? 10_000 },
210
+ ),
211
+ ));
212
+ return jsonResponse(
213
+ switched,
214
+ 200,
215
+ req,
216
+ config,
217
+ );
218
+ }
219
+ if (url.pathname === "/api/native-main-profiles/recover" && req.method === "POST") {
220
+ const input = await body(req);
221
+ const recovered = await withMainRequestDrain(deps, () => withRecoveryGateTransition(
222
+ manager,
223
+ deps,
224
+ () => withNativeMainApiOperation(
225
+ manager,
226
+ () => manager.recover(input.rollback === true, input.confirmedStopped === true),
227
+ { mode: "exclusive", waitMs: deps.drainTimeoutMs ?? 10_000 },
228
+ ),
229
+ ));
230
+ return jsonResponse(recovered, 200, req, config);
231
+ }
232
+ return jsonResponse({ error: "Unknown native-profile operation", code: "INVALID_REQUEST" }, 404, req, config);
233
+ } catch (error) {
234
+ const tooLarge = managementBodyTooLargeResponse(error, req, config);
235
+ if (tooLarge) return tooLarge;
236
+ if (error instanceof NativeProfileError) {
237
+ return jsonResponse({
238
+ error: error.message,
239
+ code: error.code,
240
+ retryable: error.retryable,
241
+ ...(error.cleanupRequired === true ? { cleanupRequired: true } : {}),
242
+ ...(typeof error.plaintextMayRemain === "boolean" ? { plaintextMayRemain: error.plaintextMayRemain } : {}),
243
+ }, error.status, req, config);
244
+ }
245
+ return jsonResponse({ error: "Native-profile operation failed.", code: "INTERNAL_ERROR" }, 500, req, config);
246
+ }
247
+ }