@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,263 @@
1
+ import type { TranslatorBudget } from "../lib/translator-budget";
2
+
3
+ /**
4
+ * Shared client-facing SSE payload rewrite shell.
5
+ *
6
+ * Multiple opt-in transforms (image-gen namespace restore, item-id repair, …) compose into one
7
+ * parse/stringify pass so a tee'd stream is not re-framed twice per event.
8
+ */
9
+
10
+ export type SsePayloadRewrite = (payload: string) => string;
11
+
12
+ /**
13
+ * Block-level SSE rewrite: maps one complete SSE event block (without its
14
+ * blank-line delimiter) to zero or more replacement blocks. This is the
15
+ * contract lifecycle repair needs: injecting missing canonical events
16
+ * (#893) is impossible in the one-payload-in/one-payload-out model.
17
+ *
18
+ * `dispose` releases any retained state (budget-charged collectors) when the
19
+ * relay tears down — terminal, EOF, cancel, or error. Relays call it exactly
20
+ * once per teardown path.
21
+ */
22
+ export type SseBlockRewrite = ((block: string) => readonly string[]) & {
23
+ dispose?: () => void;
24
+ };
25
+
26
+ /** Adapt a payload rewrite to the block contract (replace only on change). */
27
+ export function payloadRewriteAsBlockRewrite(rewrite: SsePayloadRewrite): SseBlockRewrite {
28
+ return (block) => {
29
+ const payload = sseDataPayload(block);
30
+ if (payload === null) return [block];
31
+ const rewritten = rewrite(payload);
32
+ return rewritten !== payload ? [replaceSseDataPayload(block, rewritten)] : [block];
33
+ };
34
+ }
35
+
36
+ /** Chain block rewrites: every block stage N emits feeds stage N+1. */
37
+ export function composeSseBlockRewrites(...rewrites: SseBlockRewrite[]): SseBlockRewrite {
38
+ const active = rewrites.filter(Boolean);
39
+ if (active.length === 0) return Object.assign((block: string) => [block], {});
40
+ const composed: SseBlockRewrite = (block: string) => {
41
+ let blocks: readonly string[] = [block];
42
+ for (const rewrite of active) {
43
+ const next: string[] = [];
44
+ for (const current of blocks) next.push(...rewrite(current));
45
+ blocks = next;
46
+ }
47
+ return blocks;
48
+ };
49
+ // Child disposal is part of the contract: one idempotent disposer for the
50
+ // whole chain, so relay teardown never leaks a nested collector.
51
+ let disposed = false;
52
+ composed.dispose = () => {
53
+ if (disposed) return;
54
+ disposed = true;
55
+ for (const rewrite of active) {
56
+ try { rewrite.dispose?.(); } catch { /* teardown must not throw */ }
57
+ }
58
+ };
59
+ return composed;
60
+ }
61
+
62
+ /** Split one complete SSE event block while retaining its original blank-line delimiter. */
63
+ export function nextSseBlock(buffer: string): { block: string; delimiter: string; rest: string } | null {
64
+ const match = buffer.match(/\r?\n\r?\n/);
65
+ if (!match || match.index === undefined) return null;
66
+ return {
67
+ block: buffer.slice(0, match.index),
68
+ delimiter: match[0],
69
+ rest: buffer.slice(match.index + match[0].length),
70
+ };
71
+ }
72
+
73
+ /** Join all data lines from one SSE event according to the event-stream field rules. */
74
+ export function sseDataPayload(block: string): string | null {
75
+ const data: string[] = [];
76
+ for (const line of block.split(/\r?\n/)) {
77
+ if (!line.startsWith("data:")) continue;
78
+ const value = line.slice(5);
79
+ data.push(value.startsWith(" ") ? value.slice(1) : value);
80
+ }
81
+ return data.length > 0 ? data.join("\n") : null;
82
+ }
83
+
84
+ /** Replace an SSE event's data field while preserving non-data fields and newline style. */
85
+ export function replaceSseDataPayload(block: string, payload: string): string {
86
+ const newline = block.includes("\r\n") ? "\r\n" : "\n";
87
+ const lines = block.split(/\r?\n/);
88
+ const rewritten: string[] = [];
89
+ let replaced = false;
90
+ for (const line of lines) {
91
+ if (!line.startsWith("data:")) {
92
+ rewritten.push(line);
93
+ continue;
94
+ }
95
+ if (!replaced) {
96
+ rewritten.push(`data: ${payload}`);
97
+ replaced = true;
98
+ }
99
+ }
100
+ return replaced ? rewritten.join(newline) : block;
101
+ }
102
+
103
+ /** Apply rewrites left-to-right; empty list is identity. */
104
+ export function composeSsePayloadRewrites(...rewrites: SsePayloadRewrite[]): SsePayloadRewrite {
105
+ if (rewrites.length === 0) return (payload) => payload;
106
+ if (rewrites.length === 1) return rewrites[0]!;
107
+ return (payload) => {
108
+ let next = payload;
109
+ for (const rewrite of rewrites) next = rewrite(next);
110
+ return next;
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Relay an SSE body through a single JS pull wrapper, rewriting each event's data payload in place.
116
+ * Non-data fields and framing are preserved; invalid JSON payloads are left to the rewrite callback.
117
+ */
118
+ export function relaySseWithPayloadRewrite(
119
+ body: ReadableStream<Uint8Array>,
120
+ rewrite: SsePayloadRewrite,
121
+ translatorBudget: TranslatorBudget,
122
+ ): ReadableStream<Uint8Array> {
123
+ return relaySseWithBlockRewrite(body, payloadRewriteAsBlockRewrite(rewrite), translatorBudget);
124
+ }
125
+
126
+ /**
127
+ * Relay an SSE body through a single JS pull wrapper, applying a block-level
128
+ * rewrite that may emit zero or more blocks per upstream event (lifecycle
129
+ * event injection, #893). The original stream's delimiter style is preserved
130
+ * for every emitted block.
131
+ */
132
+ export function relaySseWithBlockRewrite(
133
+ body: ReadableStream<Uint8Array>,
134
+ rewrite: SseBlockRewrite,
135
+ translatorBudget: TranslatorBudget,
136
+ ): ReadableStream<Uint8Array> {
137
+ const reader = body.getReader();
138
+ const decoder = new TextDecoder();
139
+ const encoder = new TextEncoder();
140
+ let buffer = "";
141
+ let bufferBytes = 0;
142
+ // Relays have several independent teardown paths; disposal is exactly once.
143
+ let disposed = false;
144
+ let cancelled = false;
145
+ const disposeRewrite = (): void => {
146
+ if (disposed) return;
147
+ disposed = true;
148
+ try { rewrite.dispose?.(); } catch { /* teardown must not throw */ }
149
+ };
150
+
151
+ const appendBuffer = (fragment: string): void => {
152
+ if (!fragment) return;
153
+ const nextBytes = bufferBytes + encoder.encode(fragment).byteLength;
154
+ const reservation = translatorBudget.reserveTransient(nextBytes, { kind: "live_transient" });
155
+ try {
156
+ buffer += fragment;
157
+ reservation.commitRetained();
158
+ translatorBudget.releaseRetained(bufferBytes, { kind: "live_transient" });
159
+ bufferBytes = nextBytes;
160
+ } catch (error) {
161
+ reservation.release();
162
+ throw error;
163
+ }
164
+ };
165
+
166
+ const replaceBuffer = (next: string): void => {
167
+ const nextBytes = encoder.encode(next).byteLength;
168
+ const reservation = translatorBudget.reserveTransient(nextBytes, { kind: "live_transient" });
169
+ reservation.commitRetained();
170
+ buffer = next;
171
+ translatorBudget.releaseRetained(bufferBytes, { kind: "live_transient" });
172
+ bufferBytes = nextBytes;
173
+ };
174
+
175
+ const enqueueText = (
176
+ controller: ReadableStreamDefaultController<Uint8Array>,
177
+ text: string,
178
+ ): void => {
179
+ const bytes = encoder.encode(text).byteLength;
180
+ const reservation = translatorBudget.reserveTransient(bytes, { kind: "live_transient" });
181
+ try {
182
+ const encoded = encoder.encode(text);
183
+ reservation.commitRetained();
184
+ controller.enqueue(encoded);
185
+ translatorBudget.releaseRetained(bytes, { kind: "live_transient" });
186
+ } catch (error) {
187
+ reservation.release();
188
+ throw error;
189
+ }
190
+ };
191
+
192
+ const releaseBuffer = (): void => {
193
+ translatorBudget.releaseRetained(bufferBytes, { kind: "live_transient" });
194
+ buffer = "";
195
+ bufferBytes = 0;
196
+ };
197
+
198
+ const emitProcessedBlocks = (
199
+ controller: ReadableStreamDefaultController<Uint8Array>,
200
+ flushFinal = false,
201
+ ): number => {
202
+ let emitted = 0;
203
+ let next: { block: string; delimiter: string; rest: string } | null;
204
+ while ((next = nextSseBlock(buffer))) {
205
+ replaceBuffer(next.rest);
206
+ for (const outBlock of rewrite(next.block)) {
207
+ enqueueText(controller, outBlock + next.delimiter);
208
+ emitted += 1;
209
+ }
210
+ }
211
+ if (flushFinal && buffer.length > 0) {
212
+ const tailBlocks = rewrite(buffer);
213
+ // A trailing fragment has no delimiter of its own; multiple emitted
214
+ // blocks must still be framed as separate events (#893 review).
215
+ const tailDelimiter = buffer.includes("\r\n") ? "\r\n\r\n" : "\n\n";
216
+ for (let i = 0; i < tailBlocks.length; i++) {
217
+ enqueueText(controller, tailBlocks[i]! + (i < tailBlocks.length - 1 ? tailDelimiter : ""));
218
+ emitted += 1;
219
+ }
220
+ releaseBuffer();
221
+ }
222
+ return emitted;
223
+ };
224
+
225
+ return new ReadableStream<Uint8Array>({
226
+ async pull(controller) {
227
+ try {
228
+ // A network chunk is not an SSE-event boundary. Bun may not issue a
229
+ // second pull after a fulfilled pull enqueues nothing, so keep reading
230
+ // until at least one complete rewritten block is available or EOF is
231
+ // reached. This also handles block rewrites that intentionally drop an
232
+ // event without parking the client stream.
233
+ for (;;) {
234
+ const { done, value } = await reader.read();
235
+ // A cancel raced this pending read: never feed the rewriter again
236
+ // after its disposal (#893 review).
237
+ if (cancelled) return;
238
+ if (done) {
239
+ appendBuffer(decoder.decode());
240
+ emitProcessedBlocks(controller, true);
241
+ releaseBuffer();
242
+ disposeRewrite();
243
+ controller.close();
244
+ return;
245
+ }
246
+ appendBuffer(decoder.decode(value, { stream: true }));
247
+ if (emitProcessedBlocks(controller) > 0) return;
248
+ }
249
+ } catch (error) {
250
+ releaseBuffer();
251
+ disposeRewrite();
252
+ try { await reader.cancel(error); } catch { /* already closed */ }
253
+ controller.error(error);
254
+ }
255
+ },
256
+ cancel(reason) {
257
+ cancelled = true;
258
+ releaseBuffer();
259
+ disposeRewrite();
260
+ reader.cancel(reason).catch(() => {});
261
+ },
262
+ });
263
+ }
@@ -0,0 +1,308 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { execFile } from "node:child_process";
3
+ import { join } from "node:path";
4
+ import { durableBunPath } from "../lib/bun-runtime";
5
+ import {
6
+ WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED_MARKER,
7
+ isWindowsSchtasksCreateAccessDenied,
8
+ } from "../lib/windows-elevation";
9
+ import {
10
+ finalizeWindowsSchedulerServiceRegistration,
11
+ type ElevatedReconciliationOutcome,
12
+ type FinalizeWindowsSchedulerOptions,
13
+ type FinalizeWindowsSchedulerResult,
14
+ type WindowsSchedulerTaskProbe,
15
+ } from "../service";
16
+
17
+ export type StartupInstallAction = "install-service" | "install-shim";
18
+
19
+ export type StartupInstallState =
20
+ | { status: "idle" }
21
+ | {
22
+ status: "running";
23
+ action: StartupInstallAction;
24
+ attemptId: string;
25
+ startedAt: number;
26
+ }
27
+ | {
28
+ status: "indeterminate";
29
+ action: "install-service";
30
+ attemptId: string;
31
+ startedAt: number;
32
+ timedOutAt: number;
33
+ reconciliation: Promise<ElevatedReconciliationOutcome>;
34
+ }
35
+ | {
36
+ status: "blocked";
37
+ reason: "partial-elevated-install";
38
+ action: "install-service";
39
+ attemptId: string;
40
+ detail: string;
41
+ };
42
+
43
+ const INSTALL_DETAIL_LIMIT = 2_000;
44
+ const INDETERMINATE_MESSAGE =
45
+ "Windows elevation timed out, but the elevated Task Scheduler transaction may still be running. New service installation attempts are temporarily blocked while Remodex reconciles its final state.";
46
+ const RECONCILING_MESSAGE =
47
+ "A previous elevated Windows service installation is still being reconciled. Wait for it to finish or inspect the Task Scheduler state before retrying.";
48
+ const BLOCKED_PARTIAL_MESSAGE =
49
+ "A previous elevated Windows service installation left a partial Task Scheduler state. Remove the Remodex scheduler task (or confirm it is absent), then clear the install block before retrying.";
50
+
51
+ let installState: StartupInstallState = { status: "idle" };
52
+
53
+ type FinalizeFn = (
54
+ script?: string,
55
+ options?: FinalizeWindowsSchedulerOptions,
56
+ ) => Promise<FinalizeWindowsSchedulerResult>;
57
+ let finalizeImpl: FinalizeFn = finalizeWindowsSchedulerServiceRegistration;
58
+
59
+ /** Test-only seam so elevation retry tests do not mock.module the service package. */
60
+ export function setStartupInstallFinalizeForTests(next: FinalizeFn | null): void {
61
+ finalizeImpl = next ?? finalizeWindowsSchedulerServiceRegistration;
62
+ }
63
+
64
+ /** Snapshot of the install lock for diagnostics and tests. */
65
+ export function getStartupInstallState(): StartupInstallState {
66
+ return installState;
67
+ }
68
+
69
+ /** True when this attempt still owns the active/indeterminate/blocked lock. */
70
+ export function ownsStartupInstallAttempt(attemptId: string): boolean {
71
+ return (installState.status === "running"
72
+ || installState.status === "indeterminate"
73
+ || installState.status === "blocked")
74
+ && installState.attemptId === attemptId;
75
+ }
76
+
77
+ /**
78
+ * Clear a partial-install block after the operator has cleaned up Task Scheduler.
79
+ * Requires an observed probe and clears only when the task is proven absent.
80
+ * Does not itself delete the task.
81
+ */
82
+ export function clearStartupInstallPartialBlock(options: {
83
+ probe: WindowsSchedulerTaskProbe;
84
+ }): { cleared: boolean; detail: string } {
85
+ if (installState.status !== "blocked") {
86
+ return { cleared: false, detail: `Install lock is ${installState.status}, not blocked.` };
87
+ }
88
+ if (options.probe.status === "present") {
89
+ return {
90
+ cleared: false,
91
+ detail: "Remodex Task Scheduler task is still present; remove it before clearing the block.",
92
+ };
93
+ }
94
+ if (options.probe.status === "unknown") {
95
+ return {
96
+ cleared: false,
97
+ detail: `Could not verify Task Scheduler absence (${options.probe.detail}); not clearing the block.`,
98
+ };
99
+ }
100
+ installState = { status: "idle" };
101
+ return { cleared: true, detail: "Partial-install block cleared." };
102
+ }
103
+
104
+ /** Test-only reset of the install lock. */
105
+ export function resetStartupInstallStateForTests(): void {
106
+ installState = { status: "idle" };
107
+ }
108
+
109
+ export function startupInstallArgv(
110
+ action: StartupInstallAction,
111
+ options?: { repair?: boolean },
112
+ ): string[] {
113
+ if (action === "install-service") {
114
+ return options?.repair ? ["service", "repair"] : ["service", "install"];
115
+ }
116
+ return ["codex-shim", "install"];
117
+ }
118
+
119
+ export interface CliInstallFailure {
120
+ /** Machine marker such as OCX_ERROR_CODE=..., when present in any stream. */
121
+ code: string | null;
122
+ stdout: string;
123
+ stderr: string;
124
+ message: string;
125
+ /** Bounded user-facing diagnostic (may append the marker if truncated away). */
126
+ detail: string;
127
+ }
128
+
129
+ function extractOcxErrorCode(text: string): string | null {
130
+ const match = text.match(/OCX_ERROR_CODE=[A-Z0-9_]+/);
131
+ return match ? match[0] : null;
132
+ }
133
+
134
+ /**
135
+ * Classify a CLI install failure, keeping the machine marker separate from the
136
+ * truncated user-facing diagnostic.
137
+ */
138
+ export function classifyCliInstallFailure(stdout: string, stderr: string, error: Error): CliInstallFailure {
139
+ const stdoutText = stdout.trim();
140
+ const stderrText = stderr.trim();
141
+ const message = error.message.trim();
142
+ const combined = [stderrText, stdoutText, message].filter(Boolean).join("\n");
143
+ const code = extractOcxErrorCode(combined);
144
+ let detail = combined || message;
145
+ if (detail.length > INSTALL_DETAIL_LIMIT) {
146
+ const truncated = detail.slice(0, INSTALL_DETAIL_LIMIT);
147
+ detail = code && !truncated.includes(code)
148
+ ? `${truncated}\n${code}… (truncated)`
149
+ : `${truncated}… (truncated)`;
150
+ }
151
+ return { code, stdout: stdoutText, stderr: stderrText, message, detail };
152
+ }
153
+
154
+ /** @deprecated Prefer classifyCliInstallFailure; kept for existing tests. */
155
+ export function installFailureDetail(stdout: string, stderr: string, error: Error): string {
156
+ return classifyCliInstallFailure(stdout, stderr, error).detail;
157
+ }
158
+
159
+ function runCliInstall(
160
+ action: StartupInstallAction,
161
+ options?: { repair?: boolean },
162
+ ): Promise<{ stdout: string; stderr: string }> {
163
+ const bun = durableBunPath();
164
+ const cli = join(import.meta.dir, "..", "cli", "index.ts");
165
+ const argv = [cli, ...startupInstallArgv(action, options)];
166
+ return new Promise((resolve, reject) => {
167
+ execFile(bun, argv, {
168
+ encoding: "utf8",
169
+ env: process.env,
170
+ timeout: 60_000,
171
+ windowsHide: true,
172
+ maxBuffer: 256 * 1024,
173
+ }, (error, stdout, stderr) => {
174
+ if (error) {
175
+ const failure = classifyCliInstallFailure(stdout, stderr, error);
176
+ reject(Object.assign(new Error(failure.detail), { ocxInstallFailure: failure }));
177
+ return;
178
+ }
179
+ resolve({ stdout, stderr });
180
+ });
181
+ });
182
+ }
183
+
184
+ function installFailureCode(error: unknown): string | null {
185
+ if (error && typeof error === "object" && "ocxInstallFailure" in error) {
186
+ const failure = (error as { ocxInstallFailure?: CliInstallFailure }).ocxInstallFailure;
187
+ if (failure?.code) return failure.code;
188
+ }
189
+ const detail = error instanceof Error ? error.message : String(error);
190
+ return extractOcxErrorCode(detail);
191
+ }
192
+
193
+ function rejectIfBusy(_action: StartupInstallAction): Error | null {
194
+ if (installState.status === "running") {
195
+ return new Error(`Another startup installation is already running: ${installState.action}`);
196
+ }
197
+ if (installState.status === "indeterminate") {
198
+ return new Error(RECONCILING_MESSAGE);
199
+ }
200
+ if (installState.status === "blocked") {
201
+ return new Error(BLOCKED_PARTIAL_MESSAGE);
202
+ }
203
+ return null;
204
+ }
205
+
206
+ function applyReconciliationOutcome(
207
+ attemptId: string,
208
+ outcome: ElevatedReconciliationOutcome,
209
+ ): void {
210
+ if (installState.status !== "indeterminate" || installState.attemptId !== attemptId) {
211
+ return;
212
+ }
213
+ if (outcome === "blocked-partial") {
214
+ installState = {
215
+ status: "blocked",
216
+ reason: "partial-elevated-install",
217
+ action: "install-service",
218
+ attemptId,
219
+ detail: BLOCKED_PARTIAL_MESSAGE,
220
+ };
221
+ return;
222
+ }
223
+ installState = { status: "idle" };
224
+ }
225
+
226
+ /**
227
+ * Execute the existing fixed CLI installer outside the proxy event loop.
228
+ *
229
+ * Repair mode (`options.repair`) runs `rmx service repair` — asset rewrite + restart
230
+ * without Task Scheduler re-registration, so it must not enter the UAC elevation path.
231
+ *
232
+ * After an elevation request timeout the lock becomes `indeterminate` until the
233
+ * original elevated transaction completes and is reconciled. A process restart
234
+ * clears this in-memory lock — callers must then inspect Task Scheduler reality
235
+ * (see evaluateSchedulerInstallRestartReconciliation) before installing again.
236
+ */
237
+ export function runStartupInstallAction(
238
+ action: StartupInstallAction,
239
+ options?: { repair?: boolean },
240
+ ): Promise<{ message: string }> {
241
+ const busy = rejectIfBusy(action);
242
+ if (busy) return Promise.reject(busy);
243
+
244
+ const repair = options?.repair === true;
245
+ const attemptId = randomUUID();
246
+ const startedAt = Date.now();
247
+ installState = { status: "running", action, attemptId, startedAt };
248
+
249
+ const operation = (async () => {
250
+ try {
251
+ await runCliInstall(action, { repair });
252
+ } catch (error) {
253
+ const code = installFailureCode(error);
254
+ const detail = error instanceof Error ? error.message : String(error);
255
+ // Elevate only for fresh install + structured Task Scheduler /create access denial —
256
+ // never for repair, WinSW removal, asset writes, or generic permission errors.
257
+ if (
258
+ !repair
259
+ && action === "install-service"
260
+ && process.platform === "win32"
261
+ && (code === WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED_MARKER
262
+ || isWindowsSchtasksCreateAccessDenied(detail))
263
+ ) {
264
+ const finalized = await finalizeImpl(undefined, {
265
+ attemptId,
266
+ stillOwnsAttempt: ownsStartupInstallAttempt,
267
+ });
268
+ if (finalized.kind === "indeterminate") {
269
+ const ownedAttemptId = finalized.attemptId;
270
+ installState = {
271
+ status: "indeterminate",
272
+ action: "install-service",
273
+ attemptId: ownedAttemptId,
274
+ startedAt,
275
+ timedOutAt: Date.now(),
276
+ reconciliation: finalized.reconciliation,
277
+ };
278
+ void finalized.reconciliation.then(
279
+ outcome => {
280
+ applyReconciliationOutcome(ownedAttemptId, outcome);
281
+ },
282
+ () => {
283
+ // Fail closed: a rejected reconciliation must not leave an unhandled rejection
284
+ // or silently idle the lock while Task Scheduler state may still be partial.
285
+ applyReconciliationOutcome(ownedAttemptId, "blocked-partial");
286
+ },
287
+ );
288
+ throw new Error(INDETERMINATE_MESSAGE);
289
+ }
290
+ } else {
291
+ throw error;
292
+ }
293
+ }
294
+ if (action === "install-service") {
295
+ return { message: repair ? "Background service repaired." : "Background service installed." };
296
+ }
297
+ return {
298
+ message: repair ? "Codex launcher shim repaired." : "Codex launcher shim installed.",
299
+ };
300
+ })();
301
+
302
+ return operation.finally(() => {
303
+ // Do not clear an indeterminate or blocked lock from a timed-out elevation.
304
+ if (installState.status === "running" && installState.attemptId === attemptId) {
305
+ installState = { status: "idle" };
306
+ }
307
+ });
308
+ }
@@ -0,0 +1,119 @@
1
+ import { execFile } from "node:child_process";
2
+ import { join } from "node:path";
3
+ import { codexAutoStartEnabled } from "../config";
4
+ import { deriveStartupHealth, type StartupHealth } from "../codex/autostart-health";
5
+ import { getCodexRoutingKind } from "../codex/inject";
6
+ import { diagnoseCodexShim } from "../codex/shim";
7
+ import { durableBunPath } from "../lib/bun-runtime";
8
+ import type { OcxConfig } from "../types";
9
+ import { truncateRetainedUtf8 } from "../lib/admission";
10
+
11
+ const CACHE_TTL_MS = 30_000;
12
+ const PROBE_TIMEOUT_MS = 5_000;
13
+ const MAX_DIAGNOSTIC_VALUE_BYTES = 8 * 1024;
14
+ let cached: { timestamp: number; value: StartupHealth } | null = null;
15
+ let inflight: Promise<StartupHealth> | null = null;
16
+ let generation = 0;
17
+
18
+ export function markStartupHealthDiagnosticStale(value: StartupHealth): StartupHealth {
19
+ if (!value.localRoutingDependency) return { ...value, diagnosticStale: true };
20
+ return {
21
+ ...value,
22
+ status: "at-risk",
23
+ rebootSafe: false,
24
+ protection: "none",
25
+ diagnosticStale: true,
26
+ // Mirror deriveStartupHealth's choice: an already-registered service is refreshed in
27
+ // place. Hardcoding installService here silently undid that for every stale-cache
28
+ // read, which is the path the dashboard hits while a probe is revalidating.
29
+ recommendedCommand: value.routingKind === "custom-local" || value.routingKind === "unknown"
30
+ ? value.commands.restoreNative
31
+ : value.serviceInstalled && !value.serviceConflict
32
+ ? value.commands.repairService
33
+ : value.commands.installService,
34
+ };
35
+ }
36
+
37
+ function conservativeFallback(config: Pick<OcxConfig, "codexAutoStart">): StartupHealth {
38
+ const shim = diagnoseCodexShim();
39
+ return deriveStartupHealth({
40
+ routingKind: getCodexRoutingKind(),
41
+ autostartEnabled: codexAutoStartEnabled(config),
42
+ serviceInstalled: false,
43
+ serviceViable: false,
44
+ serviceEnabled: false,
45
+ serviceRunning: false,
46
+ serviceStale: false,
47
+ serviceConflict: false,
48
+ serviceSupported: process.platform === "win32" || process.platform === "darwin" || process.platform === "linux",
49
+ shimInstalled: shim.installed,
50
+ shimHealthy: shim.healthy,
51
+ platform: process.platform,
52
+ diagnosticStale: true,
53
+ });
54
+ }
55
+
56
+ function runProbe(config: Pick<OcxConfig, "codexAutoStart">): Promise<StartupHealth> {
57
+ const bun = durableBunPath();
58
+ const cli = join(import.meta.dir, "..", "cli", "index.ts");
59
+ return new Promise(resolve => {
60
+ execFile(bun, [cli, "__startup-health"], {
61
+ encoding: "utf8",
62
+ env: process.env,
63
+ timeout: PROBE_TIMEOUT_MS,
64
+ windowsHide: true,
65
+ maxBuffer: 256 * 1024,
66
+ }, (error, stdout) => {
67
+ if (!error) {
68
+ const lines = stdout.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
69
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
70
+ try {
71
+ const parsed = JSON.parse(lines[index]) as StartupHealth;
72
+ if (["native", "protected", "at-risk"].includes(parsed.status) && typeof parsed.rebootSafe === "boolean") {
73
+ resolve({
74
+ ...parsed,
75
+ recommendedCommand: parsed.recommendedCommand === null
76
+ ? null
77
+ : truncateRetainedUtf8(parsed.recommendedCommand, MAX_DIAGNOSTIC_VALUE_BYTES),
78
+ commands: {
79
+ installService: truncateRetainedUtf8(parsed.commands.installService, MAX_DIAGNOSTIC_VALUE_BYTES),
80
+ repairService: truncateRetainedUtf8(parsed.commands.repairService, MAX_DIAGNOSTIC_VALUE_BYTES),
81
+ installShim: truncateRetainedUtf8(parsed.commands.installShim, MAX_DIAGNOSTIC_VALUE_BYTES),
82
+ restoreNative: truncateRetainedUtf8(parsed.commands.restoreNative, MAX_DIAGNOSTIC_VALUE_BYTES),
83
+ },
84
+ diagnosticStale: false,
85
+ });
86
+ return;
87
+ }
88
+ } catch { /* scan earlier output; config repair messages may precede JSON */ }
89
+ }
90
+ }
91
+ resolve(cached ? markStartupHealthDiagnosticStale(cached.value) : conservativeFallback(config));
92
+ });
93
+ });
94
+ }
95
+
96
+ function refreshInBackground(config: Pick<OcxConfig, "codexAutoStart">): void {
97
+ if (inflight) return;
98
+ const startedGeneration = generation;
99
+ const probe = runProbe(config).then(value => {
100
+ if (startedGeneration === generation) cached = { timestamp: Date.now(), value };
101
+ return value;
102
+ });
103
+ inflight = probe.finally(() => {
104
+ if (inflight === probe || startedGeneration === generation) inflight = null;
105
+ });
106
+ }
107
+
108
+ /** Stale-while-revalidate: service-manager probes never hold open a model/UI request. */
109
+ export async function getCachedStartupHealth(config: Pick<OcxConfig, "codexAutoStart">): Promise<StartupHealth> {
110
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) return cached.value;
111
+ refreshInBackground(config);
112
+ return cached ? markStartupHealthDiagnosticStale(cached.value) : conservativeFallback(config);
113
+ }
114
+
115
+ export function invalidateStartupHealthCache(): void {
116
+ generation += 1;
117
+ cached = null;
118
+ inflight = null;
119
+ }