@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,466 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import {
4
+ atomicWriteFile,
5
+ getConfigDir,
6
+ hardenExistingSecret,
7
+ } from "../config";
8
+ import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
9
+ import { hardenSecretDir } from "../lib/windows-secret-acl";
10
+
11
+ const ANDROID_REMOTE_STATE_VERSION = 1;
12
+ const ANDROID_REMOTE_STATE_MAX_BYTES = 1024 * 1024;
13
+ const CLIENT_ID_PATTERN = /^[a-zA-Z0-9._-]{1,128}$/;
14
+ const STORED_MODEL_OPTION_IDS = new Set([
15
+ "effort",
16
+ "reasoningEffort",
17
+ "serviceTier",
18
+ ]);
19
+
20
+ export type AndroidRemoteSettings = {
21
+ controlEnabled: boolean;
22
+ keepAwake: boolean;
23
+ tunnelMode: "quick" | "named";
24
+ namedTunnelHostname?: string;
25
+ };
26
+
27
+ /**
28
+ * Stored client data is deliberately separate from every existing Remodex token class.
29
+ * `credentialDigest` is private store data and must never appear in a management response.
30
+ */
31
+ export type AndroidRemoteStoredClient = {
32
+ id: string;
33
+ label: string;
34
+ deviceType: "mobile";
35
+ os: string;
36
+ address?: string;
37
+ scopes: string[];
38
+ createdAt: string;
39
+ lastSeenAt?: string;
40
+ credentialExpiresAt?: string;
41
+ credentialDigest: string;
42
+ };
43
+
44
+ /**
45
+ * Codex creates the durable task id, while Remodex creates a temporary task id
46
+ * before the first prompt is sent. This small mapping keeps the Android task id
47
+ * stable after Remodex restarts. It never contains prompts or transcript data.
48
+ */
49
+ export type AndroidRemoteThreadAlias = {
50
+ remoteThreadId: string;
51
+ nativeThreadId: string;
52
+ projectId: string;
53
+ title: string;
54
+ cwd: string;
55
+ instanceId?: string;
56
+ model: string;
57
+ runtimeMode: "approval-required" | "auto-accept-edits" | "full-access";
58
+ interactionMode: "default" | "plan";
59
+ createdAt: string;
60
+ updatedAt: string;
61
+ };
62
+
63
+ export type AndroidRemoteTaskSelectionSource = "android" | "desktop" | "migration";
64
+
65
+ /**
66
+ * Durable model state for one Codex task.
67
+ *
68
+ * This is intentionally provider/model metadata only. Provider credentials stay
69
+ * in the normal Remodex credential stores and are never projected to Android.
70
+ */
71
+ export type AndroidRemoteTaskSelection = {
72
+ nativeThreadId: string;
73
+ remoteThreadId: string;
74
+ providerInstanceId: string;
75
+ model: string;
76
+ options?: unknown;
77
+ /** Hash of the model capability row used to validate the stored options. */
78
+ capabilityVersion?: string;
79
+ revision: number;
80
+ source: AndroidRemoteTaskSelectionSource;
81
+ updateId: string;
82
+ updatedAt: string;
83
+ };
84
+
85
+ export type AndroidRemoteTaskSelectionWrite = Omit<AndroidRemoteTaskSelection, "revision"> & {
86
+ expectedRevision?: number;
87
+ };
88
+
89
+ export type AndroidRemoteTaskSelectionWriteResult = {
90
+ applied: boolean;
91
+ duplicate: boolean;
92
+ stale: boolean;
93
+ selection: AndroidRemoteTaskSelection;
94
+ state: AndroidRemoteState;
95
+ };
96
+
97
+ export type AndroidRemoteState = {
98
+ version: 1;
99
+ settings: AndroidRemoteSettings;
100
+ clients: AndroidRemoteStoredClient[];
101
+ threadAliases: AndroidRemoteThreadAlias[];
102
+ taskSelections: AndroidRemoteTaskSelection[];
103
+ };
104
+
105
+ export interface AndroidRemoteStore {
106
+ /** Present only for a filesystem-backed store. Tests and memory stores omit it. */
107
+ readonly stateRoot?: string;
108
+ read(): AndroidRemoteState;
109
+ updateSettings(patch: Partial<AndroidRemoteSettings>): AndroidRemoteState;
110
+ upsertClient(client: AndroidRemoteStoredClient): AndroidRemoteState;
111
+ updateClientMetadata(id: string, patch: { label?: string; os?: string }): AndroidRemoteState;
112
+ touchClient(id: string, patch: { address?: string; lastSeenAt: string }): AndroidRemoteState;
113
+ revokeClient(id: string): { removed: boolean; state: AndroidRemoteState };
114
+ upsertThreadAlias(alias: AndroidRemoteThreadAlias): AndroidRemoteState;
115
+ removeThreadAlias(remoteThreadId: string): AndroidRemoteState;
116
+ upsertTaskSelection(selection: AndroidRemoteTaskSelectionWrite): AndroidRemoteTaskSelectionWriteResult;
117
+ removeTaskSelection(threadId: string): AndroidRemoteState;
118
+ }
119
+
120
+ export const DEFAULT_ANDROID_REMOTE_SETTINGS: Readonly<AndroidRemoteSettings> = {
121
+ controlEnabled: false,
122
+ keepAwake: false,
123
+ tunnelMode: "quick",
124
+ };
125
+
126
+ function defaultState(): AndroidRemoteState {
127
+ return {
128
+ version: ANDROID_REMOTE_STATE_VERSION,
129
+ settings: { ...DEFAULT_ANDROID_REMOTE_SETTINGS },
130
+ clients: [],
131
+ threadAliases: [],
132
+ taskSelections: [],
133
+ };
134
+ }
135
+
136
+ function stringValue(value: unknown, maxLength: number): string | null {
137
+ if (typeof value !== "string") return null;
138
+ const trimmed = value.trim();
139
+ return trimmed.length > 0 && trimmed.length <= maxLength ? trimmed : null;
140
+ }
141
+
142
+ function storedClient(value: unknown): AndroidRemoteStoredClient | null {
143
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
144
+ const row = value as Record<string, unknown>;
145
+ const id = stringValue(row.id, 128);
146
+ const label = stringValue(row.label, 160);
147
+ const os = stringValue(row.os, 64);
148
+ const createdAt = stringValue(row.createdAt, 64);
149
+ const credentialDigest = stringValue(row.credentialDigest, 256);
150
+ if (!id || !CLIENT_ID_PATTERN.test(id) || !label || !os || !createdAt || !credentialDigest) return null;
151
+ if (row.deviceType !== "mobile") return null;
152
+ const scopes = Array.isArray(row.scopes)
153
+ ? row.scopes.flatMap(scope => {
154
+ const parsed = stringValue(scope, 96);
155
+ return parsed ? [parsed] : [];
156
+ }).slice(0, 32)
157
+ : [];
158
+ const address = stringValue(row.address, 256) ?? undefined;
159
+ const lastSeenAt = stringValue(row.lastSeenAt, 64) ?? undefined;
160
+ const credentialExpiresAt = stringValue(row.credentialExpiresAt, 64) ?? undefined;
161
+ return {
162
+ id,
163
+ label,
164
+ deviceType: "mobile",
165
+ os,
166
+ scopes,
167
+ createdAt,
168
+ credentialDigest,
169
+ ...(address ? { address } : {}),
170
+ ...(lastSeenAt ? { lastSeenAt } : {}),
171
+ ...(credentialExpiresAt ? { credentialExpiresAt } : {}),
172
+ };
173
+ }
174
+
175
+ function storedThreadAlias(value: unknown): AndroidRemoteThreadAlias | null {
176
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
177
+ const row = value as Record<string, unknown>;
178
+ const remoteThreadId = stringValue(row.remoteThreadId, 128);
179
+ const nativeThreadId = stringValue(row.nativeThreadId, 128);
180
+ const projectId = stringValue(row.projectId, 128);
181
+ const title = stringValue(row.title, 256);
182
+ const cwd = stringValue(row.cwd, 4096);
183
+ const instanceId = stringValue(row.instanceId, 64) ?? undefined;
184
+ const model = stringValue(row.model, 256);
185
+ const createdAt = stringValue(row.createdAt, 64);
186
+ const updatedAt = stringValue(row.updatedAt, 64);
187
+ const runtimeMode = row.runtimeMode;
188
+ const interactionMode = row.interactionMode;
189
+ if (!remoteThreadId || !nativeThreadId || !projectId || !title || !cwd || !model
190
+ || !createdAt || !updatedAt) return null;
191
+ if (!CLIENT_ID_PATTERN.test(remoteThreadId) || !CLIENT_ID_PATTERN.test(nativeThreadId)
192
+ || !CLIENT_ID_PATTERN.test(projectId)) return null;
193
+ if (runtimeMode !== "approval-required" && runtimeMode !== "auto-accept-edits"
194
+ && runtimeMode !== "full-access") return null;
195
+ if (interactionMode !== "default" && interactionMode !== "plan") return null;
196
+ return {
197
+ remoteThreadId,
198
+ nativeThreadId,
199
+ projectId,
200
+ title,
201
+ cwd,
202
+ ...(instanceId ? { instanceId } : {}),
203
+ model,
204
+ runtimeMode,
205
+ interactionMode,
206
+ createdAt,
207
+ updatedAt,
208
+ };
209
+ }
210
+
211
+ function storedModelOptions(value: unknown): unknown {
212
+ if (Array.isArray(value)) {
213
+ return value.flatMap(candidate => {
214
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return [];
215
+ const row = candidate as Record<string, unknown>;
216
+ const id = stringValue(row.id, 64);
217
+ const optionValue = row.value;
218
+ if (!id || !STORED_MODEL_OPTION_IDS.has(id)
219
+ || (typeof optionValue !== "string" && typeof optionValue !== "boolean"
220
+ && typeof optionValue !== "number")) return [];
221
+ return [{ id, value: typeof optionValue === "string" ? optionValue.slice(0, 256) : optionValue }];
222
+ }).slice(0, 32);
223
+ }
224
+ if (!value || typeof value !== "object") return undefined;
225
+ const entries = Object.entries(value as Record<string, unknown>).flatMap(([rawId, optionValue]) => {
226
+ const id = stringValue(rawId, 64);
227
+ if (!id || !STORED_MODEL_OPTION_IDS.has(id)
228
+ || (typeof optionValue !== "string" && typeof optionValue !== "boolean"
229
+ && typeof optionValue !== "number")) return [];
230
+ return [[id, typeof optionValue === "string" ? optionValue.slice(0, 256) : optionValue] as const];
231
+ }).slice(0, 32);
232
+ return Object.fromEntries(entries);
233
+ }
234
+
235
+ function storedTaskSelection(value: unknown): AndroidRemoteTaskSelection | null {
236
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
237
+ const row = value as Record<string, unknown>;
238
+ const nativeThreadId = stringValue(row.nativeThreadId, 128);
239
+ const remoteThreadId = stringValue(row.remoteThreadId, 128);
240
+ const providerInstanceId = stringValue(row.providerInstanceId, 64);
241
+ const model = stringValue(row.model, 256);
242
+ const updateId = stringValue(row.updateId, 128);
243
+ const updatedAt = stringValue(row.updatedAt, 64);
244
+ const capabilityVersion = stringValue(row.capabilityVersion, 128) ?? undefined;
245
+ const revision = typeof row.revision === "number" && Number.isSafeInteger(row.revision)
246
+ ? row.revision
247
+ : 0;
248
+ const source = row.source;
249
+ if (!nativeThreadId || !remoteThreadId || !providerInstanceId || !model || !updateId
250
+ || !updatedAt || revision < 1) return null;
251
+ if (!CLIENT_ID_PATTERN.test(nativeThreadId) || !CLIENT_ID_PATTERN.test(remoteThreadId)
252
+ || !CLIENT_ID_PATTERN.test(providerInstanceId)) return null;
253
+ if (source !== "android" && source !== "desktop" && source !== "migration") return null;
254
+ const options = storedModelOptions(row.options);
255
+ return {
256
+ nativeThreadId,
257
+ remoteThreadId,
258
+ providerInstanceId,
259
+ model,
260
+ ...(options !== undefined ? { options } : {}),
261
+ ...(capabilityVersion ? { capabilityVersion } : {}),
262
+ revision,
263
+ source,
264
+ updateId,
265
+ updatedAt,
266
+ };
267
+ }
268
+
269
+ function sameTaskSelection(
270
+ left: Pick<AndroidRemoteTaskSelection, "nativeThreadId" | "remoteThreadId" | "providerInstanceId" | "model" | "options" | "capabilityVersion">,
271
+ right: Pick<AndroidRemoteTaskSelection, "nativeThreadId" | "remoteThreadId" | "providerInstanceId" | "model" | "options" | "capabilityVersion">,
272
+ ): boolean {
273
+ return left.nativeThreadId === right.nativeThreadId
274
+ && left.remoteThreadId === right.remoteThreadId
275
+ && left.providerInstanceId === right.providerInstanceId
276
+ && left.model === right.model
277
+ && left.capabilityVersion === right.capabilityVersion
278
+ && JSON.stringify(left.options ?? null) === JSON.stringify(right.options ?? null);
279
+ }
280
+
281
+ function parseState(value: unknown): AndroidRemoteState {
282
+ if (!value || typeof value !== "object" || Array.isArray(value)) return defaultState();
283
+ const root = value as Record<string, unknown>;
284
+ const rawSettings = root.settings && typeof root.settings === "object" && !Array.isArray(root.settings)
285
+ ? root.settings as Record<string, unknown>
286
+ : {};
287
+ return {
288
+ version: ANDROID_REMOTE_STATE_VERSION,
289
+ settings: {
290
+ controlEnabled: rawSettings.controlEnabled === true,
291
+ keepAwake: rawSettings.keepAwake === true,
292
+ tunnelMode: rawSettings.tunnelMode === "named" ? "named" : "quick",
293
+ ...(stringValue(rawSettings.namedTunnelHostname, 253)
294
+ ? { namedTunnelHostname: stringValue(rawSettings.namedTunnelHostname, 253)! }
295
+ : {}),
296
+ },
297
+ clients: Array.isArray(root.clients)
298
+ ? root.clients.flatMap(client => {
299
+ const parsed = storedClient(client);
300
+ return parsed ? [parsed] : [];
301
+ }).slice(0, 256)
302
+ : [],
303
+ threadAliases: Array.isArray(root.threadAliases)
304
+ ? root.threadAliases.flatMap(alias => {
305
+ const parsed = storedThreadAlias(alias);
306
+ return parsed ? [parsed] : [];
307
+ }).slice(-1024)
308
+ : [],
309
+ taskSelections: Array.isArray(root.taskSelections)
310
+ ? root.taskSelections.flatMap(selection => {
311
+ const parsed = storedTaskSelection(selection);
312
+ return parsed ? [parsed] : [];
313
+ }).slice(-4096)
314
+ : [],
315
+ };
316
+ }
317
+
318
+ function ensureStateDirectory(root: string): void {
319
+ assertNotRealHomeUnderTest(root);
320
+ if (!existsSync(root)) mkdirSync(root, { recursive: true, mode: 0o700 });
321
+ try { chmodSync(root, 0o700); } catch { /* platform may ignore chmod */ }
322
+ if (process.platform === "win32") hardenSecretDir(root, { required: false });
323
+ }
324
+
325
+ export function androidRemoteStatePath(root = getConfigDir()): string {
326
+ return join(root, "android-remote.json");
327
+ }
328
+
329
+ export function createAndroidRemoteStore(root = getConfigDir()): AndroidRemoteStore {
330
+ const path = androidRemoteStatePath(root);
331
+
332
+ const read = (): AndroidRemoteState => {
333
+ try {
334
+ if (!existsSync(path)) return defaultState();
335
+ hardenExistingSecret(path);
336
+ if (statSync(path).size > ANDROID_REMOTE_STATE_MAX_BYTES) return defaultState();
337
+ return parseState(JSON.parse(readFileSync(path, "utf8")));
338
+ } catch {
339
+ // Fail closed. Invalid local state never enables remote control or admits a client.
340
+ return defaultState();
341
+ }
342
+ };
343
+
344
+ const write = (state: AndroidRemoteState): AndroidRemoteState => {
345
+ ensureStateDirectory(root);
346
+ atomicWriteFile(path, `${JSON.stringify(state, null, 2)}\n`);
347
+ return state;
348
+ };
349
+
350
+ return {
351
+ stateRoot: root,
352
+ read,
353
+ updateSettings(patch) {
354
+ const current = read();
355
+ return write({
356
+ ...current,
357
+ settings: {
358
+ controlEnabled: patch.controlEnabled ?? current.settings.controlEnabled,
359
+ keepAwake: patch.keepAwake ?? current.settings.keepAwake,
360
+ tunnelMode: patch.tunnelMode ?? current.settings.tunnelMode,
361
+ ...(patch.namedTunnelHostname !== undefined
362
+ ? patch.namedTunnelHostname.trim().length > 0
363
+ ? { namedTunnelHostname: patch.namedTunnelHostname.trim() }
364
+ : {}
365
+ : current.settings.namedTunnelHostname
366
+ ? { namedTunnelHostname: current.settings.namedTunnelHostname }
367
+ : {}),
368
+ },
369
+ });
370
+ },
371
+ upsertClient(client) {
372
+ const parsed = storedClient(client);
373
+ if (!parsed) throw new TypeError("invalid Android Remote client");
374
+ const current = read();
375
+ const clients = current.clients.filter(row => row.id !== parsed.id);
376
+ clients.push(parsed);
377
+ return write({ ...current, clients: clients.slice(-256) });
378
+ },
379
+ updateClientMetadata(id, patch) {
380
+ const current = read();
381
+ let changed = false;
382
+ const clients = current.clients.map(client => {
383
+ if (client.id !== id) return client;
384
+ const label = patch.label?.trim().slice(0, 160);
385
+ const os = patch.os?.trim().slice(0, 64);
386
+ changed = true;
387
+ return {
388
+ ...client,
389
+ ...(label ? { label } : {}),
390
+ ...(os ? { os } : {}),
391
+ };
392
+ });
393
+ return changed ? write({ ...current, clients }) : current;
394
+ },
395
+ touchClient(id, patch) {
396
+ const current = read();
397
+ let changed = false;
398
+ const clients = current.clients.map(client => {
399
+ if (client.id !== id) return client;
400
+ changed = true;
401
+ return {
402
+ ...client,
403
+ lastSeenAt: patch.lastSeenAt,
404
+ ...(patch.address ? { address: patch.address } : {}),
405
+ };
406
+ });
407
+ return changed ? write({ ...current, clients }) : current;
408
+ },
409
+ revokeClient(id) {
410
+ const current = read();
411
+ const clients = current.clients.filter(client => client.id !== id);
412
+ if (clients.length === current.clients.length) return { removed: false, state: current };
413
+ return { removed: true, state: write({ ...current, clients }) };
414
+ },
415
+ upsertThreadAlias(alias) {
416
+ const parsed = storedThreadAlias(alias);
417
+ if (!parsed) throw new TypeError("invalid Android Remote task mapping");
418
+ const current = read();
419
+ const threadAliases = current.threadAliases.filter(row =>
420
+ row.remoteThreadId !== parsed.remoteThreadId && row.nativeThreadId !== parsed.nativeThreadId);
421
+ threadAliases.push(parsed);
422
+ return write({ ...current, threadAliases: threadAliases.slice(-1024) });
423
+ },
424
+ removeThreadAlias(remoteThreadId) {
425
+ const current = read();
426
+ const threadAliases = current.threadAliases.filter(row => row.remoteThreadId !== remoteThreadId);
427
+ return threadAliases.length === current.threadAliases.length
428
+ ? current
429
+ : write({ ...current, threadAliases });
430
+ },
431
+ upsertTaskSelection(input) {
432
+ const current = read();
433
+ const existing = current.taskSelections.find(row =>
434
+ row.nativeThreadId === input.nativeThreadId || row.remoteThreadId === input.remoteThreadId);
435
+ if (existing?.updateId === input.updateId) {
436
+ return { applied: false, duplicate: true, stale: false, selection: existing, state: current };
437
+ }
438
+ const expectedRevision = input.expectedRevision;
439
+ if (expectedRevision !== undefined && expectedRevision !== (existing?.revision ?? 0)) {
440
+ if (!existing) throw new TypeError("invalid Android Remote task selection revision");
441
+ return { applied: false, duplicate: false, stale: true, selection: existing, state: current };
442
+ }
443
+ const candidate = storedTaskSelection({
444
+ ...input,
445
+ revision: (existing?.revision ?? 0) + 1,
446
+ });
447
+ if (!candidate) throw new TypeError("invalid Android Remote task selection");
448
+ if (existing && sameTaskSelection(existing, candidate)) {
449
+ return { applied: false, duplicate: true, stale: false, selection: existing, state: current };
450
+ }
451
+ const taskSelections = current.taskSelections.filter(row =>
452
+ row.nativeThreadId !== candidate.nativeThreadId && row.remoteThreadId !== candidate.remoteThreadId);
453
+ taskSelections.push(candidate);
454
+ const state = write({ ...current, taskSelections: taskSelections.slice(-4096) });
455
+ return { applied: true, duplicate: false, stale: false, selection: candidate, state };
456
+ },
457
+ removeTaskSelection(threadId) {
458
+ const current = read();
459
+ const taskSelections = current.taskSelections.filter(row =>
460
+ row.nativeThreadId !== threadId && row.remoteThreadId !== threadId);
461
+ return taskSelections.length === current.taskSelections.length
462
+ ? current
463
+ : write({ ...current, taskSelections });
464
+ },
465
+ };
466
+ }
@@ -0,0 +1,133 @@
1
+ type JsonRecord = Record<string, unknown>;
2
+
3
+ export type CodexThreadListCandidate = {
4
+ thread: JsonRecord;
5
+ archived: boolean;
6
+ };
7
+
8
+ export type CanonicalCodexThread = {
9
+ thread: JsonRecord;
10
+ archived: boolean;
11
+ };
12
+
13
+ function stringValue(value: unknown): string {
14
+ return typeof value === "string" ? value.trim() : "";
15
+ }
16
+
17
+ function finiteNumber(value: unknown): number | null {
18
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
19
+ }
20
+
21
+ function timestamp(thread: JsonRecord, key: "createdAt" | "updatedAt" | "recencyAt"): number {
22
+ return finiteNumber(thread[key]) ?? Number.NEGATIVE_INFINITY;
23
+ }
24
+
25
+ function candidateRecency(candidate: CodexThreadListCandidate): number {
26
+ const recencyAt = finiteNumber(candidate.thread.recencyAt);
27
+ if (recencyAt !== null) return recencyAt;
28
+ const updatedAt = finiteNumber(candidate.thread.updatedAt);
29
+ if (updatedAt !== null) return updatedAt;
30
+ return timestamp(candidate.thread, "createdAt");
31
+ }
32
+
33
+ function compareCandidates(
34
+ left: CodexThreadListCandidate,
35
+ right: CodexThreadListCandidate,
36
+ ): number {
37
+ const recency = candidateRecency(left) - candidateRecency(right);
38
+ if (recency !== 0) return recency;
39
+ const updated = timestamp(left.thread, "updatedAt") - timestamp(right.thread, "updatedAt");
40
+ if (updated !== 0) return updated;
41
+ const created = timestamp(left.thread, "createdAt") - timestamp(right.thread, "createdAt");
42
+ if (created !== 0) return created;
43
+ return stringValue(left.thread.path).localeCompare(stringValue(right.thread.path));
44
+ }
45
+
46
+ function maximumTimestamp(
47
+ candidates: readonly CodexThreadListCandidate[],
48
+ key: "updatedAt" | "recencyAt",
49
+ ): number | undefined {
50
+ const values = candidates
51
+ .map(candidate => finiteNumber(candidate.thread[key]))
52
+ .filter((value): value is number => value !== null);
53
+ return values.length > 0 ? Math.max(...values) : undefined;
54
+ }
55
+
56
+ function minimumCreatedAt(candidates: readonly CodexThreadListCandidate[]): number | undefined {
57
+ const values = candidates
58
+ .map(candidate => finiteNumber(candidate.thread.createdAt))
59
+ .filter((value): value is number => value !== null);
60
+ return values.length > 0 ? Math.min(...values) : undefined;
61
+ }
62
+
63
+ function sourcePaths(candidates: readonly CodexThreadListCandidate[]): string[] {
64
+ const paths = new Set<string>();
65
+ for (const candidate of [...candidates].sort(compareCandidates)) {
66
+ const path = stringValue(candidate.thread.path);
67
+ if (path) paths.add(path);
68
+ }
69
+ return [...paths];
70
+ }
71
+
72
+ /**
73
+ * Codex can retain more than one physical rollout for one logical thread
74
+ * (resume/revert/history repair). Android has one row per logical thread, so
75
+ * reconcile those physical records before applying aliases or projecting UI.
76
+ */
77
+ export function canonicalizeCodexThreadCandidates(
78
+ candidates: readonly CodexThreadListCandidate[],
79
+ ): CanonicalCodexThread[] {
80
+ const grouped = new Map<string, CodexThreadListCandidate[]>();
81
+ for (const candidate of candidates) {
82
+ const id = stringValue(candidate.thread.id);
83
+ if (!id) continue;
84
+ const rows = grouped.get(id) ?? [];
85
+ rows.push(candidate);
86
+ grouped.set(id, rows);
87
+ }
88
+
89
+ const canonical: CanonicalCodexThread[] = [];
90
+ for (const rows of grouped.values()) {
91
+ const active = rows.filter(candidate => !candidate.archived);
92
+ const selectable = active.length > 0 ? active : rows;
93
+ const preferred = [...selectable].sort(compareCandidates).at(-1)!;
94
+ const paths = sourcePaths(rows);
95
+ const createdAt = minimumCreatedAt(rows);
96
+ const updatedAt = maximumTimestamp(rows, "updatedAt");
97
+ const recencyAt = maximumTimestamp(rows, "recencyAt");
98
+ const preferredPath = stringValue(preferred.thread.path);
99
+ canonical.push({
100
+ archived: active.length === 0,
101
+ thread: {
102
+ ...preferred.thread,
103
+ ...(createdAt === undefined ? {} : { createdAt }),
104
+ ...(updatedAt === undefined ? {} : { updatedAt }),
105
+ ...(recencyAt === undefined ? {} : { recencyAt }),
106
+ ...(paths.length > 0
107
+ ? {
108
+ path: preferredPath || paths.at(-1),
109
+ androidRemoteSourcePaths: paths,
110
+ }
111
+ : {}),
112
+ },
113
+ });
114
+ }
115
+
116
+ return canonical.sort((left, right) => compareCandidates(right, left));
117
+ }
118
+
119
+ export function codexThreadSourcePaths(thread: JsonRecord): string[] {
120
+ const paths = new Set<string>();
121
+ if (Array.isArray(thread.androidRemoteSourcePaths)) {
122
+ for (const value of thread.androidRemoteSourcePaths) {
123
+ const path = stringValue(value);
124
+ if (path) paths.add(path);
125
+ }
126
+ }
127
+ const primary = stringValue(thread.path);
128
+ if (primary) {
129
+ paths.delete(primary);
130
+ paths.add(primary);
131
+ }
132
+ return [...paths];
133
+ }