@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,114 @@
1
+ import type { AdapterEvent } from "../types";
2
+
3
+ type QueueReader = (result: IteratorResult<AdapterEvent>) => void;
4
+
5
+ export const PREFLIGHT_HEARTBEAT_RETAIN_LIMIT = 16;
6
+
7
+ export interface AdapterEventQueue {
8
+ push(event: AdapterEvent): void;
9
+ close(): void;
10
+ stream(): AsyncIterable<AdapterEvent>;
11
+ collect(): Promise<AdapterEvent[]>;
12
+ }
13
+
14
+ export interface AdapterEventPreflight {
15
+ stream: AsyncIterable<AdapterEvent>;
16
+ error?: Extract<AdapterEvent, { type: "error" }>;
17
+ empty: boolean;
18
+ }
19
+
20
+ async function* replay(
21
+ buffered: readonly AdapterEvent[],
22
+ iterator: AsyncIterator<AdapterEvent>,
23
+ ): AsyncGenerator<AdapterEvent> {
24
+ try {
25
+ for (const event of buffered) yield event;
26
+ while (true) {
27
+ const next = await iterator.next();
28
+ if (next.done) return;
29
+ yield next.value;
30
+ }
31
+ } finally {
32
+ await iterator.return?.();
33
+ }
34
+ }
35
+
36
+ export async function preflightAdapterEvents(
37
+ source: AsyncIterable<AdapterEvent>,
38
+ ): Promise<AdapterEventPreflight> {
39
+ const iterator = source[Symbol.asyncIterator]();
40
+ const buffered: AdapterEvent[] = [];
41
+ while (true) {
42
+ const next = await iterator.next();
43
+ if (next.done) return { stream: replay(buffered, iterator), empty: true };
44
+ if (next.value.type === "heartbeat") {
45
+ buffered.push(next.value);
46
+ if (buffered.length > PREFLIGHT_HEARTBEAT_RETAIN_LIMIT) buffered.shift();
47
+ continue;
48
+ }
49
+ buffered.push(next.value);
50
+ if (next.value.type === "error") {
51
+ await iterator.return?.();
52
+ return { stream: replay(buffered, iterator), error: next.value, empty: false };
53
+ }
54
+ return { stream: replay(buffered, iterator), empty: false };
55
+ }
56
+ }
57
+
58
+ export function createAdapterEventQueue(opts?: {
59
+ maxBacklog?: number;
60
+ onBacklogExceeded?: () => void;
61
+ }): AdapterEventQueue {
62
+ const queued: AdapterEvent[] = [];
63
+ const readers: QueueReader[] = [];
64
+ const maxBacklog = opts?.maxBacklog ?? 1_024;
65
+ let closed = false;
66
+
67
+ const push = (event: AdapterEvent): void => {
68
+ if (closed) return;
69
+ const reader = readers.shift();
70
+ if (reader) {
71
+ reader({ done: false, value: event });
72
+ return;
73
+ }
74
+ if (queued.length >= maxBacklog) {
75
+ opts?.onBacklogExceeded?.();
76
+ queued.push({ type: "error", message: "consumer backlog exceeded — turn aborted" });
77
+ close();
78
+ return;
79
+ }
80
+ queued.push(event);
81
+ };
82
+
83
+ const close = (): void => {
84
+ if (closed) return;
85
+ closed = true;
86
+ while (readers.length > 0) {
87
+ readers.shift()?.({ done: true, value: undefined as never });
88
+ }
89
+ };
90
+
91
+ async function* stream(): AsyncIterable<AdapterEvent> {
92
+ while (true) {
93
+ const next = queued.shift();
94
+ if (next) {
95
+ yield next;
96
+ continue;
97
+ }
98
+ if (closed) return;
99
+ const result = await new Promise<IteratorResult<AdapterEvent>>(resolve => {
100
+ readers.push(resolve);
101
+ });
102
+ if (result.done) return;
103
+ yield result.value;
104
+ }
105
+ }
106
+
107
+ const collect = async (): Promise<AdapterEvent[]> => {
108
+ const events: AdapterEvent[] = [];
109
+ for await (const event of stream()) events.push(event);
110
+ return events;
111
+ };
112
+
113
+ return { push, close, stream, collect };
114
+ }
@@ -0,0 +1,71 @@
1
+ import {
2
+ isAllowedToolChoice,
3
+ namespacedToolName,
4
+ toolAllowedByChoice,
5
+ toolChoiceAliases,
6
+ type OcxRequestOptions,
7
+ type OcxTool,
8
+ type OcxProviderConfig,
9
+ } from "../types";
10
+
11
+ const NEIGHBOR_AGENT_TOOL_NAMES = ["Read", "Grep", "Glob", "Bash", "LS", "apply_patch"] as const;
12
+
13
+ function quoteNames(names: readonly string[]): string {
14
+ return names.map(name => `\`${name}\``).join(", ");
15
+ }
16
+
17
+ function uniqueNames(names: readonly string[]): string[] {
18
+ return [...new Set(names.filter(name => name.trim().length > 0))];
19
+ }
20
+
21
+ function toolChoiceAllows(tool: Pick<OcxTool, "namespace" | "name">, toolChoice: OcxRequestOptions["toolChoice"] | undefined): boolean {
22
+ if (!toolChoice || toolChoice === "auto" || toolChoice === "required") return true;
23
+ if (toolChoice === "none") return false;
24
+ if (isAllowedToolChoice(toolChoice)) return toolAllowedByChoice(tool, new Set(toolChoice.allowedTools));
25
+ return toolChoiceAliases(tool).includes(toolChoice.name);
26
+ }
27
+
28
+ function isOpenAIOrChatGPTHost(hostname: string): boolean {
29
+ return hostname === "openai.com"
30
+ || hostname.endsWith(".openai.com")
31
+ || hostname === "chatgpt.com"
32
+ || hostname.endsWith(".chatgpt.com");
33
+ }
34
+
35
+ export function shouldInjectNonOpenAIToolCatalogNudge(provider: Pick<OcxProviderConfig, "baseUrl">): boolean {
36
+ try {
37
+ return !isOpenAIOrChatGPTHost(new URL(provider.baseUrl).hostname);
38
+ } catch {
39
+ return true;
40
+ }
41
+ }
42
+
43
+ export function buildNonOpenAIToolCatalogNudgeFromNames(wireNames: readonly string[] | undefined): string | undefined {
44
+ const names = uniqueNames(wireNames ?? []);
45
+ if (names.length === 0) return undefined;
46
+
47
+ const advertised = new Set(names);
48
+ const unavailableNeighborNames = NEIGHBOR_AGENT_TOOL_NAMES.filter(name => !advertised.has(name));
49
+
50
+ return [
51
+ "Tool contract: use the current tool catalog as ground truth.",
52
+ `Valid tool names for this turn are exactly ${quoteNames(names)}.`,
53
+ "Call only listed names with their listed argument keys; do not invent, translate, or rename tools.",
54
+ unavailableNeighborNames.length > 0
55
+ ? `Do not use neighboring-agent tool names ${quoteNames(unavailableNeighborNames)} unless this turn's catalog lists those exact names.`
56
+ : undefined,
57
+ "If you need shell, file search, file read, edit, or discovery behavior, choose the listed tool that provides that capability.",
58
+ "Count a tool call only after its tool result returns; batch independent read-only calls when the runtime supports it.",
59
+ ].filter((line): line is string => typeof line === "string").join(" ");
60
+ }
61
+
62
+ export function buildNonOpenAIToolCatalogNudgeForTools(
63
+ tools: readonly Pick<OcxTool, "namespace" | "name">[] | undefined,
64
+ toolChoice?: OcxRequestOptions["toolChoice"],
65
+ toWireName: (tool: Pick<OcxTool, "namespace" | "name">) => string = tool => namespacedToolName(tool.namespace, tool.name),
66
+ ): string | undefined {
67
+ const visibleNames = tools
68
+ ?.filter(tool => toolChoiceAllows(tool, toolChoice))
69
+ .map(toWireName);
70
+ return buildNonOpenAIToolCatalogNudgeFromNames(visibleNames);
71
+ }
@@ -0,0 +1,48 @@
1
+ import { readBoundedResponseBody } from "../lib/bounded-body";
2
+ import { redactSecretString } from "../lib/redact";
3
+
4
+ const ABSOLUTE_PATH_PATTERN = /(?:\/Users\/[^ "';,]+|\/home\/[^ "';,]+|\/root\/[^ "';,]*|[A-Za-z]:\\Users\\[^ "';,]+)/g;
5
+
6
+ export function sanitizeUpstreamErrorText(value: string): string {
7
+ return redactSecretString(value).replace(ABSOLUTE_PATH_PATTERN, "[REDACTED_PATH]");
8
+ }
9
+
10
+ export function safeUpstreamErrorString(value: unknown): string | undefined {
11
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
12
+ }
13
+
14
+ export function parseUpstreamJsonPayload(payloadText: string): unknown | undefined {
15
+ const trimmed = payloadText.trim();
16
+ if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) return undefined;
17
+ try {
18
+ return JSON.parse(trimmed) as unknown;
19
+ } catch {
20
+ return undefined;
21
+ }
22
+ }
23
+
24
+ export async function readDisplaySafeErrorPayloadText(res: Response, signal?: AbortSignal): Promise<string> {
25
+ try {
26
+ const body = await readBoundedResponseBody(res, { signal });
27
+ return body.displaySafe ? body.text : "";
28
+ } catch (error) {
29
+ if (signal?.aborted) throw error;
30
+ return "";
31
+ }
32
+ }
33
+
34
+ export async function normalizeUpstreamHttpErrorResponse(
35
+ res: Response,
36
+ opts: { signal?: AbortSignal; formatMessage: (payloadText: string) => string | Promise<string> },
37
+ ): Promise<Response> {
38
+ if (res.ok) return res;
39
+ const payloadText = await readDisplaySafeErrorPayloadText(res, opts.signal);
40
+ const headers = new Headers(res.headers);
41
+ headers.delete("content-encoding");
42
+ headers.delete("content-length");
43
+ return new Response(await opts.formatMessage(payloadText), {
44
+ status: res.status,
45
+ statusText: res.statusText,
46
+ headers,
47
+ });
48
+ }
@@ -0,0 +1,218 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { randomBytes } from "node:crypto";
3
+ import { open, realpath, stat } from "node:fs/promises";
4
+ import {
5
+ basename,
6
+ isAbsolute,
7
+ relative,
8
+ resolve,
9
+ sep,
10
+ } from "node:path";
11
+
12
+ const ANDROID_ASSET_TTL_MS = 60 * 60 * 1_000;
13
+ const ANDROID_ASSET_MAX_BYTES = 40 * 1024 * 1024;
14
+ const ANDROID_ASSET_MAX_TICKETS = 512;
15
+ const ANDROID_ASSET_ROUTE_PREFIX = "/api/android-remote/assets/";
16
+
17
+ type AndroidImageAsset = {
18
+ readonly path: string;
19
+ readonly mimeType: string;
20
+ readonly size: number;
21
+ readonly expiresAt: number;
22
+ };
23
+
24
+ function comparablePath(value: string): string {
25
+ return process.platform === "win32" ? value.toLowerCase() : value;
26
+ }
27
+
28
+ function pathIsWithinRoot(root: string, candidate: string): boolean {
29
+ const child = relative(comparablePath(root), comparablePath(candidate));
30
+ return child !== ".."
31
+ && !child.startsWith(`..${sep}`)
32
+ && !isAbsolute(child);
33
+ }
34
+
35
+ async function imageMimeType(path: string): Promise<string | null> {
36
+ const handle = await open(path, "r");
37
+ try {
38
+ const bytes = Buffer.alloc(16);
39
+ const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);
40
+ const header = bytes.subarray(0, bytesRead);
41
+ if (
42
+ header.length >= 8
43
+ && header.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))
44
+ ) return "image/png";
45
+ if (
46
+ header.length >= 3
47
+ && header[0] === 0xff
48
+ && header[1] === 0xd8
49
+ && header[2] === 0xff
50
+ ) return "image/jpeg";
51
+ const ascii6 = header.subarray(0, 6).toString("ascii");
52
+ if (ascii6 === "GIF87a" || ascii6 === "GIF89a") return "image/gif";
53
+ if (
54
+ header.length >= 12
55
+ && header.subarray(0, 4).toString("ascii") === "RIFF"
56
+ && header.subarray(8, 12).toString("ascii") === "WEBP"
57
+ ) return "image/webp";
58
+ if (header.length >= 2 && header.subarray(0, 2).toString("ascii") === "BM") {
59
+ return "image/bmp";
60
+ }
61
+ return null;
62
+ } finally {
63
+ await handle.close();
64
+ }
65
+ }
66
+
67
+ function assetTokenFromPathname(pathname: string): string | null {
68
+ const remainder = pathname.slice(ANDROID_ASSET_ROUTE_PREFIX.length);
69
+ const token = remainder.split("/", 1)[0] ?? "";
70
+ return /^[a-zA-Z0-9_-]{32,128}$/.test(token) ? token : null;
71
+ }
72
+
73
+ /**
74
+ * Process-local, exact-file capabilities for Android image previews.
75
+ *
76
+ * Coil cannot attach the Android Remote bearer to a later image request, so
77
+ * the WebSocket RPC returns a short-lived opaque URL. The capability is bound
78
+ * to one canonical regular image inside the selected task's canonical root;
79
+ * no caller-controlled path is accepted by the HTTP route.
80
+ */
81
+ export class AndroidRemoteAssetStore {
82
+ private readonly tickets = new Map<string, AndroidImageAsset>();
83
+
84
+ constructor(private readonly now: () => number = Date.now) {}
85
+
86
+ async issueWorkspaceImage(input: {
87
+ readonly workspaceRoot: string;
88
+ readonly path: string;
89
+ }): Promise<{ readonly relativeUrl: string; readonly expiresAt: number }> {
90
+ const workspaceRoot = input.workspaceRoot.trim();
91
+ const requestedPath = input.path.trim();
92
+ if (!workspaceRoot) throw new TypeError("The selected Codex task has no project folder");
93
+ if (!requestedPath) throw new TypeError("Image path is required");
94
+ if (requestedPath.length > 32 * 1024) throw new RangeError("Image path is too long");
95
+
96
+ const canonicalRoot = await realpath(resolve(workspaceRoot));
97
+ const unresolvedFile = isAbsolute(requestedPath)
98
+ ? resolve(requestedPath)
99
+ : resolve(canonicalRoot, requestedPath);
100
+ const canonicalFile = await realpath(unresolvedFile);
101
+ if (!pathIsWithinRoot(canonicalRoot, canonicalFile)) {
102
+ throw new TypeError("Image must be inside the selected Codex project folder");
103
+ }
104
+
105
+ return this.issueCanonicalImage(canonicalFile);
106
+ }
107
+
108
+ /**
109
+ * Issues an exact-file capability after the gateway has verified that the
110
+ * assistant explicitly referenced this absolute image in the selected task.
111
+ * Keeping that authorization in the gateway prevents the phone from using
112
+ * this method as a general-purpose laptop filesystem reader.
113
+ */
114
+ async issueAssistantReferencedImage(path: string): Promise<{
115
+ readonly relativeUrl: string;
116
+ readonly expiresAt: number;
117
+ }> {
118
+ const requestedPath = path.trim();
119
+ if (!requestedPath) throw new TypeError("Image path is required");
120
+ if (requestedPath.length > 32 * 1024) throw new RangeError("Image path is too long");
121
+ if (!isAbsolute(requestedPath)) {
122
+ throw new TypeError("An external assistant image must use an absolute path");
123
+ }
124
+ return this.issueCanonicalImage(await realpath(resolve(requestedPath)));
125
+ }
126
+
127
+ private async issueCanonicalImage(canonicalFile: string): Promise<{
128
+ readonly relativeUrl: string;
129
+ readonly expiresAt: number;
130
+ }> {
131
+ const file = await stat(canonicalFile);
132
+ if (!file.isFile()) throw new TypeError("Image path must point to a regular file");
133
+ if (file.size <= 0 || file.size > ANDROID_ASSET_MAX_BYTES) {
134
+ throw new RangeError("Image exceeds the 40 MB preview limit");
135
+ }
136
+ const mimeType = await imageMimeType(canonicalFile);
137
+ if (!mimeType) throw new TypeError("Image must be a PNG, JPEG, GIF, WebP, or BMP file");
138
+
139
+ this.prune();
140
+ while (this.tickets.size >= ANDROID_ASSET_MAX_TICKETS) {
141
+ const oldest = this.tickets.keys().next().value;
142
+ if (typeof oldest !== "string") break;
143
+ this.tickets.delete(oldest);
144
+ }
145
+ const token = randomBytes(24).toString("base64url");
146
+ const expiresAt = this.now() + ANDROID_ASSET_TTL_MS;
147
+ this.tickets.set(token, {
148
+ path: canonicalFile,
149
+ mimeType,
150
+ size: file.size,
151
+ expiresAt,
152
+ });
153
+ return {
154
+ relativeUrl: `${ANDROID_ASSET_ROUTE_PREFIX}${token}/${encodeURIComponent(basename(canonicalFile))}`,
155
+ expiresAt,
156
+ };
157
+ }
158
+
159
+ response(pathname: string, method: string): Response | null {
160
+ if (!pathname.startsWith(ANDROID_ASSET_ROUTE_PREFIX)) return null;
161
+ const token = assetTokenFromPathname(pathname);
162
+ if (!token) {
163
+ return new Response(null, {
164
+ status: 404,
165
+ headers: {
166
+ "Cache-Control": "no-store",
167
+ "X-Content-Type-Options": "nosniff",
168
+ "X-Frame-Options": "DENY",
169
+ },
170
+ });
171
+ }
172
+ const asset = this.tickets.get(token);
173
+ if (!asset || asset.expiresAt <= this.now()) {
174
+ if (asset) this.tickets.delete(token);
175
+ return new Response(null, {
176
+ status: 404,
177
+ headers: {
178
+ "Cache-Control": "no-store",
179
+ "X-Content-Type-Options": "nosniff",
180
+ "X-Frame-Options": "DENY",
181
+ },
182
+ });
183
+ }
184
+ if (method !== "GET" && method !== "HEAD") {
185
+ return new Response(null, {
186
+ status: 405,
187
+ headers: {
188
+ Allow: "GET, HEAD",
189
+ "Cache-Control": "no-store",
190
+ "X-Content-Type-Options": "nosniff",
191
+ "X-Frame-Options": "DENY",
192
+ },
193
+ });
194
+ }
195
+ return new Response(method === "HEAD" ? null : Bun.file(asset.path), {
196
+ status: 200,
197
+ headers: {
198
+ "Content-Type": asset.mimeType,
199
+ "Content-Length": String(asset.size),
200
+ "Cache-Control": "private, max-age=3600, immutable",
201
+ "Content-Disposition": `inline; filename*=UTF-8''${encodeURIComponent(basename(asset.path))}`,
202
+ "X-Content-Type-Options": "nosniff",
203
+ "X-Frame-Options": "DENY",
204
+ },
205
+ });
206
+ }
207
+
208
+ clear(): void {
209
+ this.tickets.clear();
210
+ }
211
+
212
+ private prune(): void {
213
+ const now = this.now();
214
+ for (const [token, asset] of this.tickets) {
215
+ if (asset.expiresAt <= now) this.tickets.delete(token);
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,168 @@
1
+ import {
2
+ chmodSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ readdirSync,
6
+ rmSync,
7
+ statSync,
8
+ writeFileSync,
9
+ } from "node:fs";
10
+ import { basename, extname, join } from "node:path";
11
+ import { randomUUID } from "node:crypto";
12
+ import { getConfigDir } from "../config";
13
+ import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
14
+ import { hardenSecretDir } from "../lib/windows-secret-acl";
15
+
16
+ export const ANDROID_ATTACHMENT_MAX_COUNT = 8;
17
+ export const ANDROID_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
18
+ export const ANDROID_ATTACHMENT_MAX_TOTAL_BYTES = 40 * 1024 * 1024;
19
+ const STAGED_FILE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
20
+
21
+ type JsonRecord = Record<string, unknown>;
22
+
23
+ export type StagedAndroidAttachments = {
24
+ codexInputs: Array<{ type: "localImage"; path: string }>;
25
+ referencedFiles: string[];
26
+ stagedPaths: string[];
27
+ };
28
+
29
+ function record(value: unknown): JsonRecord | null {
30
+ return value && typeof value === "object" && !Array.isArray(value)
31
+ ? value as JsonRecord
32
+ : null;
33
+ }
34
+
35
+ function cleanFilename(value: unknown, fallback: string): string {
36
+ const raw = typeof value === "string" ? basename(value.trim()) : "";
37
+ const cleaned = raw.replace(/[^a-zA-Z0-9._ -]/g, "_").replace(/^\.+/, "").slice(0, 180);
38
+ return cleaned || fallback;
39
+ }
40
+
41
+ function decodeDataUrl(value: unknown): { mimeType: string; bytes: Buffer } {
42
+ if (typeof value !== "string") throw new TypeError("attachment data is missing");
43
+ const match = /^data:([^;,]{1,100});base64,([a-zA-Z0-9+/=\r\n]+)$/.exec(value);
44
+ if (!match) throw new TypeError("attachment must use a base64 data URL");
45
+ const bytes = Buffer.from(match[2]!.replace(/[\r\n]/g, ""), "base64");
46
+ if (bytes.length === 0 || bytes.length > ANDROID_ATTACHMENT_MAX_BYTES) {
47
+ throw new RangeError("attachment exceeds the 10 MB limit");
48
+ }
49
+ return { mimeType: match[1]!.toLowerCase(), bytes };
50
+ }
51
+
52
+ function isSupportedImage(mimeType: string, bytes: Buffer): boolean {
53
+ if (mimeType === "image/png") {
54
+ return bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
55
+ }
56
+ if (mimeType === "image/jpeg") {
57
+ return bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff;
58
+ }
59
+ if (mimeType === "image/gif") {
60
+ const signature = bytes.subarray(0, 6).toString("ascii");
61
+ return signature === "GIF87a" || signature === "GIF89a";
62
+ }
63
+ if (mimeType === "image/webp") {
64
+ return bytes.length >= 12
65
+ && bytes.subarray(0, 4).toString("ascii") === "RIFF"
66
+ && bytes.subarray(8, 12).toString("ascii") === "WEBP";
67
+ }
68
+ return false;
69
+ }
70
+
71
+ function ensureFolder(path: string): void {
72
+ assertNotRealHomeUnderTest(path);
73
+ if (!existsSync(path)) mkdirSync(path, { recursive: true, mode: 0o700 });
74
+ try { chmodSync(path, 0o700); } catch { /* platform may ignore chmod */ }
75
+ if (process.platform === "win32") hardenSecretDir(path, { required: false });
76
+ }
77
+
78
+ export function androidAttachmentRoot(root = getConfigDir()): string {
79
+ return join(root, "android-remote-files");
80
+ }
81
+
82
+ /**
83
+ * Stages phone-owned attachment bytes under Remodex's protected data folder.
84
+ * Images become native Codex localImage inputs. Other file types are supported
85
+ * by the gateway contract and are added to the prompt as explicit PC-local paths.
86
+ */
87
+ export function stageAndroidAttachments(input: {
88
+ clientId: string;
89
+ attachments: unknown;
90
+ root?: string;
91
+ }): StagedAndroidAttachments {
92
+ if (!Array.isArray(input.attachments)) throw new TypeError("attachments must be an array");
93
+ if (input.attachments.length > ANDROID_ATTACHMENT_MAX_COUNT) {
94
+ throw new RangeError(`a prompt can contain at most ${ANDROID_ATTACHMENT_MAX_COUNT} attachments`);
95
+ }
96
+ const root = androidAttachmentRoot(input.root);
97
+ const clientFolder = join(root, input.clientId.replace(/[^a-zA-Z0-9._-]/g, "_"));
98
+ const turnFolder = join(clientFolder, randomUUID());
99
+ ensureFolder(turnFolder);
100
+
101
+ const codexInputs: StagedAndroidAttachments["codexInputs"] = [];
102
+ const referencedFiles: string[] = [];
103
+ const stagedPaths: string[] = [];
104
+ let totalBytes = 0;
105
+ try {
106
+ for (let index = 0; index < input.attachments.length; index += 1) {
107
+ const attachment = record(input.attachments[index]);
108
+ if (!attachment || (attachment.type !== "image" && attachment.type !== "file")) {
109
+ throw new TypeError("unsupported Android attachment type");
110
+ }
111
+ const decoded = decodeDataUrl(attachment.dataUrl);
112
+ const declaredMime = typeof attachment.mimeType === "string"
113
+ ? attachment.mimeType.trim().toLowerCase()
114
+ : decoded.mimeType;
115
+ if (declaredMime !== decoded.mimeType) throw new TypeError("attachment media type does not match its data");
116
+ if (attachment.type === "image" && !declaredMime.startsWith("image/")) {
117
+ throw new TypeError("image attachment must use an image media type");
118
+ }
119
+ if (attachment.type === "image" && !isSupportedImage(declaredMime, decoded.bytes)) {
120
+ throw new TypeError("image attachment must be a valid PNG, JPEG, GIF, or WebP file");
121
+ }
122
+ if (typeof attachment.sizeBytes === "number"
123
+ && Number.isFinite(attachment.sizeBytes)
124
+ && Math.abs(attachment.sizeBytes - decoded.bytes.length) > 3) {
125
+ throw new TypeError("attachment size does not match its data");
126
+ }
127
+ totalBytes += decoded.bytes.length;
128
+ if (totalBytes > ANDROID_ATTACHMENT_MAX_TOTAL_BYTES) {
129
+ throw new RangeError("combined attachments exceed the 40 MB limit");
130
+ }
131
+ const originalName = cleanFilename(attachment.name, `attachment-${index + 1}`);
132
+ const extension = extname(originalName).slice(0, 20);
133
+ const stem = basename(originalName, extension).slice(0, 120) || `attachment-${index + 1}`;
134
+ const path = join(turnFolder, `${stem}-${randomUUID()}${extension}`);
135
+ writeFileSync(path, decoded.bytes, { mode: 0o600, flag: "wx" });
136
+ try { chmodSync(path, 0o600); } catch { /* platform may ignore chmod */ }
137
+ stagedPaths.push(path);
138
+ if (attachment.type === "image") codexInputs.push({ type: "localImage", path });
139
+ else referencedFiles.push(path);
140
+ }
141
+ return { codexInputs, referencedFiles, stagedPaths };
142
+ } catch (error) {
143
+ try { rmSync(turnFolder, { recursive: true, force: true }); } catch { /* best-effort rollback */ }
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ export function sweepOldAndroidAttachments(
149
+ root = androidAttachmentRoot(),
150
+ now = Date.now(),
151
+ ): void {
152
+ try {
153
+ if (!existsSync(root)) return;
154
+ for (const client of readdirSync(root, { withFileTypes: true })) {
155
+ if (!client.isDirectory()) continue;
156
+ const clientPath = join(root, client.name);
157
+ for (const turn of readdirSync(clientPath, { withFileTypes: true })) {
158
+ if (!turn.isDirectory()) continue;
159
+ const turnPath = join(clientPath, turn.name);
160
+ if (now - statSync(turnPath).mtimeMs > STAGED_FILE_TTL_MS) {
161
+ rmSync(turnPath, { recursive: true, force: true });
162
+ }
163
+ }
164
+ }
165
+ } catch {
166
+ // Attachment cleanup is maintenance. A locked file must not stop the gateway.
167
+ }
168
+ }