@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,121 @@
1
+ import { execFile } from "node:child_process";
2
+ import { basename } from "node:path";
3
+ import { resolveTrustedWindowsPowerShellExe } from "../lib/windows-elevation";
4
+
5
+ const PROCESS_LIST_MAX_BUFFER = 16 * 1024 * 1024;
6
+ const DIRECT_CODEX_BASENAMES = new Set(["codex", "codex.exe"]);
7
+ const CODEX_INTERPRETER_BASENAMES = new Set(["node", "nodejs", "bun"]);
8
+ const CODEX_ENTRYPOINT_BASENAMES = new Set([
9
+ "codex",
10
+ "codex.js",
11
+ "codex.mjs",
12
+ "codex.cjs",
13
+ "codex.ts",
14
+ ]);
15
+
16
+ export interface NativeProcessExecOptions {
17
+ encoding: "utf8";
18
+ timeout: number;
19
+ maxBuffer: number;
20
+ windowsHide?: boolean;
21
+ shell: false;
22
+ killSignal: "SIGKILL";
23
+ }
24
+
25
+ export type NativeProcessExecutor = (
26
+ file: string,
27
+ args: string[],
28
+ options: NativeProcessExecOptions,
29
+ ) => Promise<string>;
30
+
31
+ export interface NativeCodexProcessProbeOptions {
32
+ platform?: NodeJS.Platform;
33
+ execFile?: NativeProcessExecutor;
34
+ pid?: number;
35
+ }
36
+
37
+ export type NativeCodexProcessProbe =
38
+ | { status: "clear"; count: 0 }
39
+ | { status: "busy"; count: number }
40
+ | { status: "unknown"; count: 0 };
41
+
42
+ /** Async, shell-free child execution with runtime-enforced timeout and output bounds. */
43
+ export const executeNativeProcess: NativeProcessExecutor = (file, args, options) => new Promise((resolve, reject) => {
44
+ execFile(file, args, {
45
+ encoding: options.encoding,
46
+ timeout: options.timeout,
47
+ maxBuffer: options.maxBuffer,
48
+ windowsHide: options.windowsHide,
49
+ shell: false,
50
+ killSignal: options.killSignal,
51
+ }, (error, stdout) => {
52
+ if (error) {
53
+ reject(error);
54
+ return;
55
+ }
56
+ resolve(stdout);
57
+ });
58
+ });
59
+
60
+ async function windowsProcessCount(run: NativeProcessExecutor): Promise<number> {
61
+ const powershell = resolveTrustedWindowsPowerShellExe();
62
+ const script = [
63
+ "$ErrorActionPreference='Stop';",
64
+ "$self=$PID;",
65
+ "$items=Get-CimInstance Win32_Process | Where-Object { $_.ProcessId -ne $self -and ($_.Name -match '^(?i:codex)(?:\\.exe)?$' -or $_.CommandLine -match '(?i)(?:^|[\\\\/\"\\s])codex(?:\\.exe|\\.cmd)?(?:[\"\\s]|$)') };",
66
+ "@($items).Count",
67
+ ].join(" ");
68
+ const output = (await run(powershell, ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script], {
69
+ encoding: "utf8",
70
+ timeout: 12_000,
71
+ maxBuffer: PROCESS_LIST_MAX_BUFFER,
72
+ windowsHide: true,
73
+ shell: false,
74
+ killSignal: "SIGKILL",
75
+ })).trim();
76
+ if (!/^\d+$/.test(output)) throw new Error("invalid process count");
77
+ const count = Number(output);
78
+ if (!Number.isSafeInteger(count)) throw new Error("invalid process count");
79
+ return count;
80
+ }
81
+
82
+ async function unixProcessCount(run: NativeProcessExecutor, pid: number): Promise<number> {
83
+ const output = await run("ps", ["-eo", "pid=,comm=,args="], {
84
+ encoding: "utf8",
85
+ timeout: 5_000,
86
+ maxBuffer: PROCESS_LIST_MAX_BUFFER,
87
+ shell: false,
88
+ killSignal: "SIGKILL",
89
+ });
90
+ let count = 0;
91
+ for (const line of output.split("\n")) {
92
+ const match = line.trim().match(/^(\d+)\s+(\S+)\s*(.*)$/);
93
+ if (!match || Number(match[1]) === pid) continue;
94
+ const command = basename(match[2]!).toLowerCase();
95
+ const [rawArgv0 = "", rawEntrypoint = ""] = match[3]!.trim().split(/\s+/, 2);
96
+ const argv0 = basename(rawArgv0).toLowerCase();
97
+ const entrypoint = basename(rawEntrypoint).toLowerCase();
98
+ const isDirectCodex = DIRECT_CODEX_BASENAMES.has(command)
99
+ || DIRECT_CODEX_BASENAMES.has(argv0);
100
+ const isInterpreterWrappedCodex = CODEX_INTERPRETER_BASENAMES.has(argv0)
101
+ && CODEX_ENTRYPOINT_BASENAMES.has(entrypoint);
102
+ if (isDirectCodex || isInterpreterWrappedCodex) count += 1;
103
+ }
104
+ return count;
105
+ }
106
+
107
+ /** Best-effort, read-only process probe. It never terminates a user process. */
108
+ export async function probeNativeCodexProcesses({
109
+ platform = process.platform,
110
+ execFile: run = executeNativeProcess,
111
+ pid = process.pid,
112
+ }: NativeCodexProcessProbeOptions = {}): Promise<NativeCodexProcessProbe> {
113
+ try {
114
+ const count = await (platform === "win32"
115
+ ? windowsProcessCount(run)
116
+ : unixProcessCount(run, pid));
117
+ return count > 0 ? { status: "busy", count } : { status: "clear", count: 0 };
118
+ } catch {
119
+ return { status: "unknown", count: 0 };
120
+ }
121
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Pure recovery decision model for the native-main profile design spike.
3
+ *
4
+ * This module deliberately performs no credential, vault, or filesystem I/O.
5
+ * The eventual transaction runner must make its observations first, then apply
6
+ * exactly one decision returned here while holding the home-scoped lock.
7
+ */
8
+
9
+ import type { NativeProfileJournalPhase } from "./native-profile-types";
10
+
11
+ export type { NativeProfileJournalPhase } from "./native-profile-types";
12
+
13
+ export type NativeProfileAuthObservation =
14
+ | {
15
+ identity: "source";
16
+ digest: "exact" | "changed";
17
+ }
18
+ | {
19
+ identity: "target";
20
+ digest: "exact" | "changed";
21
+ }
22
+ | {
23
+ identity: "unknown" | "other";
24
+ digest: "unknown";
25
+ };
26
+
27
+ export type NativeProfileRecoveryDecision =
28
+ | {
29
+ action: "rollback-source";
30
+ publishRuntimeTransition: false;
31
+ externallyRefreshed: boolean;
32
+ reason: "source-active";
33
+ }
34
+ | {
35
+ action: "commit-target";
36
+ publishRuntimeTransition: true;
37
+ externallyRefreshed: boolean;
38
+ reason: "target-active-vault-pending";
39
+ }
40
+ | {
41
+ action: "finalize-target";
42
+ publishRuntimeTransition: true;
43
+ externallyRefreshed: boolean;
44
+ reason: "target-active-vault-committed";
45
+ }
46
+ | {
47
+ action: "manual-recovery";
48
+ publishRuntimeTransition: false;
49
+ externallyRefreshed: false;
50
+ reason: "auth-unconfirmed" | "third-identity";
51
+ };
52
+
53
+ export function decideNativeProfileRecovery(
54
+ phase: NativeProfileJournalPhase,
55
+ observation: NativeProfileAuthObservation,
56
+ ): NativeProfileRecoveryDecision {
57
+ if (observation.identity === "unknown") {
58
+ return {
59
+ action: "manual-recovery",
60
+ publishRuntimeTransition: false,
61
+ externallyRefreshed: false,
62
+ reason: "auth-unconfirmed",
63
+ };
64
+ }
65
+
66
+ if (observation.identity === "other") {
67
+ return {
68
+ action: "manual-recovery",
69
+ publishRuntimeTransition: false,
70
+ externallyRefreshed: false,
71
+ reason: "third-identity",
72
+ };
73
+ }
74
+
75
+ if (observation.identity === "source") {
76
+ return {
77
+ action: "rollback-source",
78
+ publishRuntimeTransition: false,
79
+ externallyRefreshed: observation.digest === "changed",
80
+ reason: "source-active",
81
+ };
82
+ }
83
+
84
+ if (phase === "vault-committed") {
85
+ return {
86
+ action: "finalize-target",
87
+ publishRuntimeTransition: true,
88
+ externallyRefreshed: observation.digest === "changed",
89
+ reason: "target-active-vault-committed",
90
+ };
91
+ }
92
+
93
+ return {
94
+ action: "commit-target",
95
+ publishRuntimeTransition: true,
96
+ externallyRefreshed: observation.digest === "changed",
97
+ reason: "target-active-vault-pending",
98
+ };
99
+ }
@@ -0,0 +1,387 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
2
+ import {
3
+ closeSync,
4
+ constants as fsConstants,
5
+ existsSync,
6
+ fstatSync,
7
+ lstatSync,
8
+ openSync,
9
+ readFileSync,
10
+ realpathSync,
11
+ } from "node:fs";
12
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
13
+
14
+ import type { NativeProfileContext } from "./native-profile-store";
15
+ import { NativeProfileError } from "./native-profile-types";
16
+
17
+ export const NATIVE_STAGE_LEASE_MS = 30 * 60_000;
18
+ export const NATIVE_STAGE_HEARTBEAT_INTERVAL_MS = 60_000;
19
+ export const NATIVE_STAGE_SWEEP_INTERVAL_MS = 60_000;
20
+ const MAX_STAGE_REGISTRY_BYTES = 1024 * 1024;
21
+ const MAX_STAGES = 32;
22
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
23
+ const HASH_RE = /^[0-9a-f]{64}$/;
24
+ const TOKEN_DOMAIN = "opencodex-native-stage-writer-v1\0";
25
+
26
+ export type NativeStageState = "creating" | "open" | "cleanup-required";
27
+ export type NativeStageTerminalOutcome = null | "imported" | "rejected" | "cancelled" | "expired";
28
+
29
+ export interface NativeStageRecordV1 {
30
+ stageId: string;
31
+ leaseId: string;
32
+ writerTokenHash: string;
33
+ creatorInstanceId: string;
34
+ stagingRoot: string;
35
+ stagingRootDev: string;
36
+ stagingRootIno: string;
37
+ stageDev: string | null;
38
+ stageIno: string | null;
39
+ state: NativeStageState;
40
+ terminalOutcome: NativeStageTerminalOutcome;
41
+ createdAt: number;
42
+ lastHeartbeatAt: number;
43
+ leaseExpiresAt: number;
44
+ }
45
+
46
+ interface NativeStageRegistryV1 {
47
+ version: 1;
48
+ revision: number;
49
+ homeId: string;
50
+ stages: NativeStageRecordV1[];
51
+ }
52
+
53
+ export interface NativeStageProof {
54
+ record: NativeStageRecordV1;
55
+ path: string;
56
+ identity: { dev: bigint; ino: bigint };
57
+ expired: boolean;
58
+ }
59
+
60
+ export interface NativeStageReservation {
61
+ stageId: string;
62
+ leaseId: string;
63
+ writerToken: string;
64
+ stagingCodexHome: string;
65
+ leaseExpiresAt: number;
66
+ }
67
+
68
+ type AtomicWriter = (path: string, content: string) => Promise<void>;
69
+
70
+ export interface NativeStageStoreOptions {
71
+ context: NativeProfileContext;
72
+ now: () => number;
73
+ randomUUID: () => string;
74
+ atomicWrite: AtomicWriter;
75
+ hardenPath: (path: string) => Promise<void>;
76
+ leaseMs?: number;
77
+ }
78
+
79
+ function storageUnsafe(message: string): NativeProfileError {
80
+ return new NativeProfileError("PROFILE_STORAGE_UNSAFE", message, 409, false, true, true);
81
+ }
82
+
83
+ function pathIdentity(path: string): { dev: bigint; ino: bigint } {
84
+ const entry = lstatSync(path, { bigint: true });
85
+ return { dev: entry.dev, ino: entry.ino };
86
+ }
87
+
88
+ function sameIdentity(entry: { dev: bigint; ino: bigint }, dev: string, ino: string): boolean {
89
+ return entry.dev.toString() === dev && entry.ino.toString() === ino;
90
+ }
91
+
92
+ function boundedRead(path: string): string {
93
+ let fd: number | undefined;
94
+ try {
95
+ const flags = process.platform === "win32"
96
+ ? fsConstants.O_RDONLY
97
+ : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW;
98
+ fd = openSync(path, flags);
99
+ const opened = fstatSync(fd, { bigint: true });
100
+ const linked = lstatSync(path, { bigint: true });
101
+ if (
102
+ !opened.isFile()
103
+ || !linked.isFile()
104
+ || linked.isSymbolicLink()
105
+ || opened.dev !== linked.dev
106
+ || opened.ino !== linked.ino
107
+ || opened.size > BigInt(MAX_STAGE_REGISTRY_BYTES)
108
+ ) throw storageUnsafe("The native-login stage registry is not a bounded private file.");
109
+ return readFileSync(fd, "utf8");
110
+ } finally {
111
+ if (fd !== undefined) closeSync(fd);
112
+ }
113
+ }
114
+
115
+ function isInteger(value: unknown): value is number {
116
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
117
+ }
118
+
119
+ function parseRecord(value: unknown): NativeStageRecordV1 {
120
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("record");
121
+ const record = value as Record<string, unknown>;
122
+ const state = record.state;
123
+ const terminal = record.terminalOutcome;
124
+ if (
125
+ typeof record.stageId !== "string" || !UUID_RE.test(record.stageId)
126
+ || typeof record.leaseId !== "string" || !UUID_RE.test(record.leaseId)
127
+ || typeof record.writerTokenHash !== "string" || !HASH_RE.test(record.writerTokenHash)
128
+ || typeof record.creatorInstanceId !== "string" || !HASH_RE.test(record.creatorInstanceId)
129
+ || typeof record.stagingRoot !== "string" || !isAbsolute(record.stagingRoot)
130
+ || typeof record.stagingRootDev !== "string" || !/^\d+$/.test(record.stagingRootDev)
131
+ || typeof record.stagingRootIno !== "string" || !/^\d+$/.test(record.stagingRootIno)
132
+ || (record.stageDev !== null && (typeof record.stageDev !== "string" || !/^\d+$/.test(record.stageDev)))
133
+ || (record.stageIno !== null && (typeof record.stageIno !== "string" || !/^\d+$/.test(record.stageIno)))
134
+ || !["creating", "open", "cleanup-required"].includes(String(state))
135
+ || ![null, "imported", "rejected", "cancelled", "expired"].includes(terminal as never)
136
+ || !isInteger(record.createdAt)
137
+ || !isInteger(record.lastHeartbeatAt)
138
+ || !isInteger(record.leaseExpiresAt)
139
+ || record.lastHeartbeatAt < record.createdAt
140
+ || record.leaseExpiresAt < record.lastHeartbeatAt
141
+ ) throw new Error("record");
142
+ if (state === "open" && (record.stageDev === null || record.stageIno === null || terminal !== null)) throw new Error("record state");
143
+ if (state === "creating" && terminal !== null) throw new Error("record state");
144
+ if (state === "cleanup-required" && terminal === null) throw new Error("record state");
145
+ return record as unknown as NativeStageRecordV1;
146
+ }
147
+
148
+ function serializeRegistry(registry: NativeStageRegistryV1): string {
149
+ return `${JSON.stringify(registry, null, 2)}\n`;
150
+ }
151
+
152
+ function tokenHash(context: NativeProfileContext, stageId: string, leaseId: string, token: string): Buffer {
153
+ return createHash("sha256")
154
+ .update(TOKEN_DOMAIN)
155
+ .update(context.homeId)
156
+ .update("\0")
157
+ .update(stageId)
158
+ .update("\0")
159
+ .update(leaseId)
160
+ .update("\0")
161
+ .update(token)
162
+ .digest();
163
+ }
164
+
165
+ export class NativeProfileStageStore {
166
+ // Trust boundary: an authenticated OS user can rewrite their own files and is
167
+ // not treated as an adversarial tenant. Any link, type, or inode ambiguity
168
+ // that this process actually observes still fails closed before credential IO.
169
+ private readonly context: NativeProfileContext;
170
+ private readonly now: () => number;
171
+ private readonly uuid: () => string;
172
+ private readonly atomicWrite: AtomicWriter;
173
+ private readonly hardenPath: (path: string) => Promise<void>;
174
+ private readonly leaseMs: number;
175
+
176
+ constructor(options: NativeStageStoreOptions) {
177
+ this.context = options.context;
178
+ this.now = options.now;
179
+ this.uuid = options.randomUUID;
180
+ this.atomicWrite = options.atomicWrite;
181
+ this.hardenPath = options.hardenPath;
182
+ this.leaseMs = Math.max(1_000, options.leaseMs ?? NATIVE_STAGE_LEASE_MS);
183
+ }
184
+
185
+ private read(): NativeStageRegistryV1 {
186
+ if (!existsSync(this.context.stageRegistryPath)) {
187
+ return { version: 1, revision: 0, homeId: this.context.homeId, stages: [] };
188
+ }
189
+ try {
190
+ const parsed = JSON.parse(boundedRead(this.context.stageRegistryPath)) as Record<string, unknown>;
191
+ if (
192
+ parsed.version !== 1
193
+ || !isInteger(parsed.revision)
194
+ || parsed.homeId !== this.context.homeId
195
+ || !Array.isArray(parsed.stages)
196
+ || parsed.stages.length > MAX_STAGES
197
+ ) throw new Error("registry");
198
+ const stages = parsed.stages.map(parseRecord);
199
+ if (new Set(stages.map(stage => stage.stageId)).size !== stages.length) throw new Error("duplicate stage");
200
+ return { version: 1, revision: parsed.revision, homeId: this.context.homeId, stages };
201
+ } catch (error) {
202
+ if (error instanceof NativeProfileError) throw error;
203
+ throw storageUnsafe("The native-login stage registry is invalid.");
204
+ }
205
+ }
206
+
207
+ private async write(registry: NativeStageRegistryV1): Promise<void> {
208
+ registry.revision += 1;
209
+ await this.atomicWrite(this.context.stageRegistryPath, serializeRegistry(registry));
210
+ await this.hardenPath(this.context.stageRegistryPath);
211
+ }
212
+
213
+ private record(registry: NativeStageRegistryV1, stageId: string): NativeStageRecordV1 | undefined {
214
+ return registry.stages.find(stage => stage.stageId === stageId);
215
+ }
216
+
217
+ private assertToken(record: NativeStageRecordV1 | undefined, token: string): NativeStageRecordV1 {
218
+ const expected = record ? Buffer.from(record.writerTokenHash, "hex") : Buffer.alloc(32);
219
+ const actual = tokenHash(this.context, record?.stageId ?? "00000000-0000-4000-8000-000000000000", record?.leaseId ?? "00000000-0000-4000-8000-000000000000", token);
220
+ const valid = timingSafeEqual(expected, actual);
221
+ expected.fill(0);
222
+ actual.fill(0);
223
+ if (!record || !valid) throw new NativeProfileError("STAGING_NOT_FOUND", "The native-login staging session is missing or invalid.", 404);
224
+ return record;
225
+ }
226
+
227
+ private assertRoot(record: NativeStageRecordV1): void {
228
+ try {
229
+ const entry = lstatSync(record.stagingRoot, { bigint: true });
230
+ if (
231
+ !entry.isDirectory()
232
+ || entry.isSymbolicLink()
233
+ || resolve(realpathSync.native(record.stagingRoot)) !== resolve(record.stagingRoot)
234
+ || !sameIdentity({ dev: entry.dev, ino: entry.ino }, record.stagingRootDev, record.stagingRootIno)
235
+ ) throw new Error("root identity");
236
+ } catch {
237
+ throw storageUnsafe("The native-login staging root changed identity.");
238
+ }
239
+ }
240
+
241
+ stagePath(record: NativeStageRecordV1): string {
242
+ const path = resolve(join(record.stagingRoot, record.stageId));
243
+ const rel = relative(resolve(record.stagingRoot), path);
244
+ if (!rel || rel.startsWith("..") || rel.includes(sep)) throw storageUnsafe("The native-login staging path escaped its registered root.");
245
+ return path;
246
+ }
247
+
248
+ proofForRecord(record: NativeStageRecordV1, allowPartial = false): NativeStageProof | null {
249
+ this.assertRoot(record);
250
+ const path = this.stagePath(record);
251
+ if (!existsSync(path)) return null;
252
+ try {
253
+ const entry = lstatSync(path, { bigint: true });
254
+ if (!entry.isDirectory() || entry.isSymbolicLink()) throw new Error("stage type");
255
+ if (record.stageDev !== null && record.stageIno !== null && !sameIdentity({ dev: entry.dev, ino: entry.ino }, record.stageDev, record.stageIno)) {
256
+ throw new Error("stage identity");
257
+ }
258
+ if (!allowPartial) {
259
+ const markerPath = join(path, "stage.json");
260
+ const markerBefore = lstatSync(markerPath, { bigint: true });
261
+ if (!markerBefore.isFile() || markerBefore.isSymbolicLink() || markerBefore.size > 4096n) throw new Error("stage marker type");
262
+ const marker = JSON.parse(readFileSync(markerPath, "utf8")) as Record<string, unknown>;
263
+ const markerAfter = lstatSync(markerPath, { bigint: true });
264
+ if (markerAfter.dev !== markerBefore.dev || markerAfter.ino !== markerBefore.ino) throw new Error("stage marker identity");
265
+ if (
266
+ marker.version !== 2
267
+ || marker.stageId !== record.stageId
268
+ || marker.leaseId !== record.leaseId
269
+ || marker.homeId !== this.context.homeId
270
+ ) throw new Error("stage marker");
271
+ }
272
+ return {
273
+ record,
274
+ path,
275
+ identity: { dev: entry.dev, ino: entry.ino },
276
+ expired: this.now() > record.leaseExpiresAt,
277
+ };
278
+ } catch {
279
+ throw storageUnsafe("The native-login staging directory changed identity.");
280
+ }
281
+ }
282
+
283
+ async reserve(stagingRoot: string): Promise<NativeStageReservation> {
284
+ const registry = this.read();
285
+ if (registry.stages.length >= MAX_STAGES) throw new NativeProfileError("INVALID_REQUEST", "Native-login staging is limited to 32 concurrent sessions.", 400);
286
+ const canonicalRoot = resolve(realpathSync.native(stagingRoot));
287
+ const root = lstatSync(canonicalRoot, { bigint: true });
288
+ if (!root.isDirectory() || root.isSymbolicLink()) throw storageUnsafe("The native-login staging root is not a private directory.");
289
+ let stageId = this.uuid();
290
+ while (this.record(registry, stageId)) stageId = this.uuid();
291
+ const leaseId = this.uuid();
292
+ const writerToken = randomBytes(32).toString("base64url");
293
+ const now = this.now();
294
+ const digest = tokenHash(this.context, stageId, leaseId, writerToken);
295
+ const record: NativeStageRecordV1 = {
296
+ stageId,
297
+ leaseId,
298
+ writerTokenHash: digest.toString("hex"),
299
+ creatorInstanceId: this.context.instanceId,
300
+ stagingRoot: canonicalRoot,
301
+ stagingRootDev: root.dev.toString(),
302
+ stagingRootIno: root.ino.toString(),
303
+ stageDev: null,
304
+ stageIno: null,
305
+ state: "creating",
306
+ terminalOutcome: null,
307
+ createdAt: now,
308
+ lastHeartbeatAt: now,
309
+ leaseExpiresAt: now + this.leaseMs,
310
+ };
311
+ digest.fill(0);
312
+ registry.stages.push(record);
313
+ await this.write(registry);
314
+ return { stageId, leaseId, writerToken, stagingCodexHome: this.stagePath(record), leaseExpiresAt: record.leaseExpiresAt };
315
+ }
316
+
317
+ async activate(reservation: NativeStageReservation): Promise<void> {
318
+ const registry = this.read();
319
+ const record = this.assertToken(this.record(registry, reservation.stageId), reservation.writerToken);
320
+ if (record.state !== "creating" || record.leaseId !== reservation.leaseId) throw storageUnsafe("The native-login stage reservation changed state.");
321
+ const path = this.stagePath(record);
322
+ const identity = pathIdentity(path);
323
+ record.stageDev = identity.dev.toString();
324
+ record.stageIno = identity.ino.toString();
325
+ record.state = "open";
326
+ await this.write(registry);
327
+ }
328
+
329
+ verify(
330
+ stageId: string,
331
+ writerToken: string,
332
+ allowCleanupRequired = false,
333
+ allowInvalidMarker = false,
334
+ ): NativeStageProof {
335
+ if (!UUID_RE.test(stageId) || typeof writerToken !== "string" || writerToken.length < 32) {
336
+ throw new NativeProfileError("STAGING_NOT_FOUND", "The native-login staging session is missing or invalid.", 404);
337
+ }
338
+ const registry = this.read();
339
+ const record = this.assertToken(this.record(registry, stageId), writerToken);
340
+ if (record.creatorInstanceId !== this.context.instanceId) {
341
+ throw new NativeProfileError("STAGING_NOT_FOUND", "The native-login staging session is missing or invalid.", 404);
342
+ }
343
+ if (record.state === "creating" || (!allowCleanupRequired && record.state !== "open")) {
344
+ throw new NativeProfileError("STAGING_TERMINAL", "The native-login staging session is no longer writable.", 409, false, record.state === "cleanup-required" ? true : undefined, record.state === "cleanup-required" ? true : undefined);
345
+ }
346
+ const proof = this.proofForRecord(record, allowInvalidMarker);
347
+ if (!proof) throw new NativeProfileError("STAGING_NOT_FOUND", "The native-login staging session is missing or invalid.", 404);
348
+ return proof;
349
+ }
350
+
351
+ async heartbeat(proof: NativeStageProof): Promise<number> {
352
+ const registry = this.read();
353
+ const record = this.record(registry, proof.record.stageId);
354
+ if (!record || record.leaseId !== proof.record.leaseId || record.state !== "open") throw storageUnsafe("The native-login stage lease changed during heartbeat.");
355
+ const now = this.now();
356
+ record.lastHeartbeatAt = now;
357
+ record.leaseExpiresAt = now + this.leaseMs;
358
+ await this.write(registry);
359
+ this.proofForRecord(record);
360
+ return record.leaseExpiresAt;
361
+ }
362
+
363
+ async markCleanupRequired(proof: Pick<NativeStageProof, "record">, outcome: Exclude<NativeStageTerminalOutcome, null>): Promise<void> {
364
+ const registry = this.read();
365
+ const record = this.record(registry, proof.record.stageId);
366
+ if (!record || record.leaseId !== proof.record.leaseId) return;
367
+ record.state = "cleanup-required";
368
+ record.terminalOutcome = outcome;
369
+ await this.write(registry);
370
+ }
371
+
372
+ async remove(proof: Pick<NativeStageProof, "record">): Promise<void> {
373
+ const registry = this.read();
374
+ const index = registry.stages.findIndex(stage => stage.stageId === proof.record.stageId && stage.leaseId === proof.record.leaseId);
375
+ if (index < 0) return;
376
+ registry.stages.splice(index, 1);
377
+ await this.write(registry);
378
+ }
379
+
380
+ records(): NativeStageRecordV1[] {
381
+ return this.read().stages;
382
+ }
383
+
384
+ recordForStage(stageId: string): NativeStageRecordV1 | undefined {
385
+ return this.read().stages.find(stage => stage.stageId === stageId);
386
+ }
387
+ }