@stage-labs/metro 0.1.0-beta.21 → 0.1.0-beta.210

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 (436) hide show
  1. package/dist/api.js +9 -101
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +152 -0
  4. package/dist/cli.js +52 -109
  5. package/dist/control.js +47 -75
  6. package/dist/hold.js +210 -0
  7. package/dist/local.js +43 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +44 -0
  11. package/dist/provider-flags.js +9 -0
  12. package/dist/route.js +58 -0
  13. package/dist/runtime-install.js +96 -0
  14. package/dist/runtime.js +29 -52
  15. package/dist/serve.js +157 -0
  16. package/dist/service.js +304 -0
  17. package/dist/tail.js +119 -0
  18. package/dist/update.js +13 -1
  19. package/package.json +2 -23
  20. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  21. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
  22. package/runtime/marketplace/plugin/.mcp.json +1 -0
  23. package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
  24. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
  25. package/runtime/marketplace/plugin/bin/policy.mjs +97 -0
  26. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  27. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  28. package/runtime/marketplace/plugin/orchestrator.md +26 -0
  29. package/runtime/node_modules/@metro-labs/core/package.json +47 -0
  30. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  31. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
  32. package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
  33. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  34. package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +24 -28
  35. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
  36. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
  37. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  38. package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
  39. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +83 -0
  40. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
  41. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  44. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +6 -11
  45. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +84 -0
  47. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +18 -1
  48. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  49. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  50. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +89 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +144 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +37 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +89 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +142 -0
  73. package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
  74. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-map.ts → daemon/src/agents/map.ts} +58 -1
  75. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  76. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +150 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +218 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
  84. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
  85. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
  86. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
  87. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
  88. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +70 -45
  89. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
  90. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
  91. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
  92. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +355 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +284 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +273 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  104. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
  105. package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  108. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
  109. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
  110. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
  111. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
  112. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +347 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  120. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
  121. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
  122. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  123. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
  124. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
  125. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +195 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +128 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +224 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +278 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +14 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
  155. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
  156. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  157. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +49 -20
  158. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
  159. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
  160. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
  161. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +24 -79
  162. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
  163. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
  164. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
  173. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +33 -42
  174. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  176. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
  177. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +68 -119
  178. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
  179. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  180. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  181. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  182. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  187. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +59 -51
  188. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +205 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +66 -0
  190. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  191. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  192. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  193. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  194. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  195. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  196. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  197. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  198. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
  199. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +9 -36
  200. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
  201. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
  202. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
  203. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
  204. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +75 -9
  205. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
  206. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +22 -6
  207. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  208. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
  209. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
  210. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  211. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
  212. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  213. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  214. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  215. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  216. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  217. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  218. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +9 -0
  219. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  220. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  221. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  222. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +147 -0
  223. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
  224. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
  225. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/package.json +3 -5
  226. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +8 -28
  227. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +19 -187
  228. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +3 -3
  229. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +31 -72
  230. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +2 -2
  231. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +2 -2
  232. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +15 -21
  233. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/members.ts +2 -2
  234. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  235. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/send-files.ts +12 -13
  236. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +23 -0
  237. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  238. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
  239. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
  240. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  241. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  242. package/runtime/node_modules/@metro-labs/{telegram-user → outlook}/package.json +2 -5
  243. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +93 -0
  244. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
  245. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
  246. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
  247. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
  248. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
  249. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +140 -0
  250. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
  251. package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
  252. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
  253. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  254. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
  255. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
  256. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
  257. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
  258. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
  259. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  260. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -0
  261. package/runtime/node_modules/@metro-labs/telegram/package.json +3 -2
  262. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +38 -96
  263. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +214 -253
  264. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +67 -108
  265. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +6 -6
  266. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +101 -147
  267. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +4 -4
  268. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
  269. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
  270. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +6 -102
  271. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
  272. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +64 -151
  273. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +61 -33
  274. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  275. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +3 -3
  276. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +11 -1
  277. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +5 -47
  278. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  279. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +86 -0
  280. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +240 -0
  281. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  282. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +170 -0
  283. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  284. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +90 -0
  285. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  286. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
  287. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +21 -0
  288. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
  289. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  290. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +95 -0
  291. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -0
  292. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  293. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  294. package/runtime/node_modules/@metro-labs/threema/src/files.ts +47 -0
  295. package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -0
  296. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  297. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  298. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
  299. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  300. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  301. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  302. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  303. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  304. package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
  305. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
  306. package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
  307. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
  308. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
  309. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
  310. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
  311. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
  312. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
  313. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
  314. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
  315. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
  316. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
  317. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
  318. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
  319. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
  320. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
  321. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  322. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
  323. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
  324. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
  325. package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
  326. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
  327. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
  328. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +15 -204
  329. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +4 -43
  330. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +41 -219
  331. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
  332. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +3 -3
  333. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
  334. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
  335. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
  336. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
  337. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
  338. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +12 -20
  339. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +1 -19
  340. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
  341. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  342. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  343. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  344. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  345. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +5 -1
  346. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +4 -51
  347. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +30 -44
  348. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
  349. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
  350. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
  351. package/runtime/runtime.json +1 -1
  352. package/runtime/server.ts +1 -0
  353. package/runtime/stations.json +37 -0
  354. package/dist/prompt.js +0 -10
  355. package/dist/store.js +0 -70
  356. package/runtime/node_modules/@metro-labs/discord/src/station.ts +0 -19
  357. package/runtime/node_modules/@metro-labs/discord/src/voice-speak.ts +0 -195
  358. package/runtime/node_modules/@metro-labs/discord/src/voice-transcribe.ts +0 -292
  359. package/runtime/node_modules/@metro-labs/discord/src/voice.ts +0 -197
  360. package/runtime/node_modules/@metro-labs/discord/src/wire.ts +0 -5
  361. package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
  362. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
  363. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
  364. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -335
  365. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
  366. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -322
  367. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
  368. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
  369. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
  370. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
  371. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
  372. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
  373. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
  374. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
  375. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
  376. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
  377. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
  378. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
  379. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
  380. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
  381. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
  382. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
  383. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
  384. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
  385. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
  386. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
  387. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
  388. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
  389. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
  390. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
  391. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +0 -364
  392. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
  393. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
  394. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
  395. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
  396. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
  397. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
  398. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
  399. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
  400. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
  401. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
  402. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
  403. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
  404. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
  405. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
  406. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
  407. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
  408. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
  409. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -3
  410. package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
  411. package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
  412. package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
  413. package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
  414. package/runtime/node_modules/@metro-labs/telegram-user/src/inbound.ts +0 -78
  415. package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
  416. package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
  417. package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
  418. package/runtime/node_modules/@metro-labs/telegram-user/src/normalize.ts +0 -22
  419. package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
  420. package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
  421. package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
  422. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  423. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
  424. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
  425. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
  426. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
  427. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
  428. /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
  429. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
  430. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
  431. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
  432. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/api-base.ts +0 -0
  433. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.ts +0 -0
  434. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
  435. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
  436. /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
@@ -0,0 +1,208 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { isRecord } from '@metro-labs/core/is-record';
5
+ import { PendingLogins } from './pkce.js';
6
+ import { nonEmpty } from './text.js';
7
+
8
+ export const CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
9
+ export const CODEX_ISSUER = 'https://auth.openai.com';
10
+ export const CODEX_REDIRECT = 'http://localhost:1455/auth/callback';
11
+ const SCOPE = 'openid profile email offline_access api.connectors.read api.connectors.invoke';
12
+ const AUTH_CLAIM = 'https://api.openai.com/auth';
13
+ const ACCESS_TOKEN_TTL_MS = 55 * 60_000;
14
+
15
+ export interface CodexTokens {
16
+ accessToken: string;
17
+ refreshToken: string;
18
+ idToken: string;
19
+ accountId: string;
20
+ email: string | null;
21
+ plan: string | null;
22
+ savedAt: string;
23
+ }
24
+
25
+ export class CodexAuthError extends Error {}
26
+
27
+ const pending = new PendingLogins(24);
28
+
29
+ export function authorizeUrl(state: string, challenge: string, issuer = CODEX_ISSUER): string {
30
+ const query = new URLSearchParams({
31
+ response_type: 'code',
32
+ client_id: CODEX_CLIENT_ID,
33
+ redirect_uri: CODEX_REDIRECT,
34
+ scope: SCOPE,
35
+ code_challenge: challenge,
36
+ code_challenge_method: 'S256',
37
+ id_token_add_organizations: 'true',
38
+ codex_cli_simplified_flow: 'true',
39
+ state,
40
+ originator: 'codex_cli_rs',
41
+ });
42
+ return `${issuer}/oauth/authorize?${query.toString()}`;
43
+ }
44
+
45
+ export function beginLogin(issuer = CODEX_ISSUER, now = Date.now()): { url: string; state: string } {
46
+ const { state, challenge } = pending.begin(now);
47
+ return { url: authorizeUrl(state, challenge, issuer), state };
48
+ }
49
+
50
+ export function parseCallback(raw: string): { code: string; state: string } {
51
+ const text = raw.trim();
52
+ let query: URLSearchParams;
53
+ try {
54
+ query = new URL(text).searchParams;
55
+ } catch {
56
+ query = new URLSearchParams(text.replace(/^\?/, ''));
57
+ }
58
+ const code = query.get('code')?.trim() ?? '';
59
+ const state = query.get('state')?.trim() ?? '';
60
+ if (code === '' || state === '')
61
+ throw new CodexAuthError('paste the whole address the browser landed on; it carries code= and state=');
62
+ return { code, state };
63
+ }
64
+
65
+ function decodeJwtPayload(jwt: string): Record<string, unknown> {
66
+ const part = jwt.split('.')[1] ?? '';
67
+ try {
68
+ const parsed: unknown = JSON.parse(Buffer.from(part, 'base64url').toString('utf8'));
69
+ return isRecord(parsed) ? parsed : {};
70
+ } catch {
71
+ return {};
72
+ }
73
+ }
74
+
75
+
76
+ export function claimsOf(idToken: string): { accountId: string | null; email: string | null; plan: string | null } {
77
+ const payload = decodeJwtPayload(idToken);
78
+ const auth = isRecord(payload[AUTH_CLAIM]) ? payload[AUTH_CLAIM] : {};
79
+ return { accountId: nonEmpty(auth.chatgpt_account_id), email: nonEmpty(payload.email), plan: nonEmpty(auth.chatgpt_plan_type) };
80
+ }
81
+
82
+ interface TokenResponse {
83
+ id_token?: unknown;
84
+ access_token?: unknown;
85
+ refresh_token?: unknown;
86
+ }
87
+
88
+ const EMPTY_TOKENS: CodexTokens = { accessToken: '', refreshToken: '', idToken: '', accountId: '', email: null, plan: null, savedAt: '' };
89
+
90
+ function assertUsable(next: CodexTokens): CodexTokens {
91
+ if (next.accessToken === '' || next.refreshToken === '') throw new CodexAuthError('the token endpoint returned no usable tokens');
92
+ if (next.accountId === '') throw new CodexAuthError('the id token carries no ChatGPT account id; sign in with a ChatGPT account that has Codex');
93
+ return next;
94
+ }
95
+
96
+ const pick = (fresh: string | null, kept: string | null): string | null => fresh ?? kept;
97
+
98
+ export function tokensFrom(body: unknown, previous: CodexTokens | null, now = new Date()): CodexTokens {
99
+ const res = (isRecord(body) ? body : {}) as TokenResponse;
100
+ const kept = previous ?? EMPTY_TOKENS;
101
+ const idToken = pick(nonEmpty(res.id_token), kept.idToken) ?? '';
102
+ const claims = claimsOf(idToken);
103
+ return assertUsable({
104
+ accessToken: nonEmpty(res.access_token) ?? '',
105
+ refreshToken: pick(nonEmpty(res.refresh_token), kept.refreshToken) ?? '',
106
+ idToken,
107
+ accountId: pick(claims.accountId, kept.accountId) ?? '',
108
+ email: pick(claims.email, kept.email),
109
+ plan: pick(claims.plan, kept.plan),
110
+ savedAt: now.toISOString(),
111
+ });
112
+ }
113
+
114
+ async function tokenCall(issuer: string, init: RequestInit, fetchImpl: typeof fetch): Promise<unknown> {
115
+ const res = await fetchImpl(`${issuer}/oauth/token`, init);
116
+ const raw = await res.text();
117
+ if (!res.ok) {
118
+ let detail = raw.slice(0, 300);
119
+ try {
120
+ const parsed: unknown = JSON.parse(raw);
121
+ if (isRecord(parsed)) detail = nonEmpty(parsed.error_description) ?? nonEmpty(parsed.error) ?? detail;
122
+ } catch {
123
+ detail = raw.slice(0, 300);
124
+ }
125
+ throw new CodexAuthError(`OpenAI refused the token request (${String(res.status)}): ${detail}`);
126
+ }
127
+ return JSON.parse(raw) as unknown;
128
+ }
129
+
130
+ export async function finishLogin(
131
+ callback: string,
132
+ issuer = CODEX_ISSUER,
133
+ fetchImpl: typeof fetch = fetch,
134
+ now = Date.now(),
135
+ ): Promise<CodexTokens> {
136
+ const { code, state } = parseCallback(callback);
137
+ const verifier = pending.take(state, now);
138
+ if (verifier === null) throw new CodexAuthError('this sign-in link has expired or was started elsewhere; press Connect again');
139
+ return exchangeCode(issuer, code, verifier, CODEX_REDIRECT, fetchImpl, now);
140
+ }
141
+
142
+ export async function exchangeCode(
143
+ issuer: string,
144
+ code: string,
145
+ verifier: string,
146
+ redirectUri: string,
147
+ fetchImpl: typeof fetch,
148
+ now = Date.now(),
149
+ ): Promise<CodexTokens> {
150
+ const body = new URLSearchParams({
151
+ grant_type: 'authorization_code',
152
+ code,
153
+ redirect_uri: redirectUri,
154
+ client_id: CODEX_CLIENT_ID,
155
+ code_verifier: verifier,
156
+ });
157
+ const res = await tokenCall(
158
+ issuer,
159
+ { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString() },
160
+ fetchImpl,
161
+ );
162
+ return tokensFrom(res, null, new Date(now));
163
+ }
164
+
165
+ export async function refreshTokens(previous: CodexTokens, issuer = CODEX_ISSUER, fetchImpl: typeof fetch = fetch): Promise<CodexTokens> {
166
+ const res = await tokenCall(
167
+ issuer,
168
+ {
169
+ method: 'POST',
170
+ headers: { 'content-type': 'application/json' },
171
+ body: JSON.stringify({ client_id: CODEX_CLIENT_ID, grant_type: 'refresh_token', refresh_token: previous.refreshToken }),
172
+ },
173
+ fetchImpl,
174
+ );
175
+ return tokensFrom(res, previous);
176
+ }
177
+
178
+ export const tokensStale = (tokens: CodexTokens, now = Date.now()): boolean =>
179
+ now - Date.parse(tokens.savedAt) > ACCESS_TOKEN_TTL_MS;
180
+
181
+ function readAuthFile(home: string): Record<string, unknown> {
182
+ let raw: unknown;
183
+ try {
184
+ raw = JSON.parse(readFileSync(join(home, '.codex', 'auth.json'), 'utf8'));
185
+ } catch {
186
+ throw new CodexAuthError('no Codex CLI login on this machine (~/.codex/auth.json); run codex login there first, or connect here');
187
+ }
188
+ return isRecord(raw) ? raw : {};
189
+ }
190
+
191
+ export function readCodexCliAuth(home = homedir()): CodexTokens {
192
+ const raw = readAuthFile(home);
193
+ if (!isRecord(raw.tokens)) throw new CodexAuthError('~/.codex/auth.json holds no ChatGPT tokens (an API-key login cannot be reused here)');
194
+ const stored = raw.tokens;
195
+ const idToken = nonEmpty(stored.id_token) ?? '';
196
+ const claims = claimsOf(idToken);
197
+ const found: CodexTokens = {
198
+ accessToken: nonEmpty(stored.access_token) ?? '',
199
+ refreshToken: nonEmpty(stored.refresh_token) ?? '',
200
+ idToken,
201
+ accountId: nonEmpty(stored.account_id) ?? claims.accountId ?? '',
202
+ email: claims.email,
203
+ plan: claims.plan,
204
+ savedAt: nonEmpty(raw.last_refresh) ?? new Date(0).toISOString(),
205
+ };
206
+ if ([found.accessToken, found.refreshToken, found.accountId].includes('')) throw new CodexAuthError('~/.codex/auth.json is missing a token or the account id');
207
+ return found;
208
+ }
@@ -0,0 +1,91 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { isRecord } from '@metro-labs/core/is-record';
3
+ import { errMsg } from '@metro-labs/core/log';
4
+ import { CODEX_CLIENT_ID, CODEX_ISSUER, CodexAuthError, exchangeCode, type CodexTokens } from './codex-auth.js';
5
+ import { stringOf } from '@metro-labs/http/api-http';
6
+
7
+ const VERIFY_PATH = '/codex/device';
8
+ const DEVICE_TTL_MS = 15 * 60_000;
9
+ const DEFAULT_INTERVAL_S = 5;
10
+ const ID_BYTES = 18;
11
+
12
+ export interface DeviceLogin {
13
+ id: string;
14
+ userCode: string;
15
+ verifyUrl: string;
16
+ interval: number;
17
+ }
18
+
19
+ export type DeviceStatus = { status: 'pending' } | { status: 'done'; tokens: CodexTokens } | { status: 'failed'; error: string };
20
+
21
+ interface Entry {
22
+ deviceAuthId: string;
23
+ userCode: string;
24
+ interval: number;
25
+ issuer: string;
26
+ startedAt: number;
27
+ polledAt: number | null;
28
+ }
29
+
30
+ const logins = new Map<string, Entry>();
31
+
32
+
33
+ function intervalOf(value: unknown): number {
34
+ const n = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN;
35
+ return Number.isFinite(n) && n >= 1 ? Math.round(n) : DEFAULT_INTERVAL_S;
36
+ }
37
+
38
+ function sweep(now: number): void {
39
+ for (const [id, entry] of logins) if (now - entry.startedAt > DEVICE_TTL_MS) logins.delete(id);
40
+ }
41
+
42
+ const post = (url: string, body: unknown, fetchImpl: typeof fetch): Promise<Response> =>
43
+ fetchImpl(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
44
+
45
+ export async function beginDeviceLogin(issuer = CODEX_ISSUER, fetchImpl: typeof fetch = fetch, now = Date.now()): Promise<DeviceLogin> {
46
+ sweep(now);
47
+ const res = await post(`${issuer}/api/accounts/deviceauth/usercode`, { client_id: CODEX_CLIENT_ID }, fetchImpl);
48
+ if (res.status === 404) throw new CodexAuthError('OpenAI is not offering the device-code sign-in right now; use the browser sign-in instead');
49
+ if (!res.ok) throw new CodexAuthError(`OpenAI refused to start the device-code sign-in (${String(res.status)})`);
50
+ const body: unknown = await res.json();
51
+ const parsed = isRecord(body) ? body : {};
52
+ const deviceAuthId = stringOf(parsed.device_auth_id);
53
+ const userCode = stringOf(parsed.user_code) || stringOf(parsed.usercode);
54
+ if (deviceAuthId === '' || userCode === '') throw new CodexAuthError('OpenAI answered without a device code');
55
+ const id = randomBytes(ID_BYTES).toString('base64url');
56
+ const interval = intervalOf(parsed.interval);
57
+ logins.set(id, { deviceAuthId, userCode, interval, issuer, startedAt: now, polledAt: null });
58
+ return { id, userCode, verifyUrl: `${issuer}${VERIFY_PATH}`, interval };
59
+ }
60
+
61
+ async function claim(entry: Entry, fetchImpl: typeof fetch, now: number): Promise<DeviceStatus> {
62
+ const res = await post(`${entry.issuer}/api/accounts/deviceauth/token`, { device_auth_id: entry.deviceAuthId, user_code: entry.userCode }, fetchImpl);
63
+ if (res.status === 403 || res.status === 404) {
64
+ await res.body?.cancel();
65
+ return { status: 'pending' };
66
+ }
67
+ if (!res.ok) return { status: 'failed', error: `OpenAI ended the device-code sign-in (${String(res.status)})` };
68
+ const body: unknown = await res.json();
69
+ const parsed = isRecord(body) ? body : {};
70
+ const code = stringOf(parsed.authorization_code);
71
+ const verifier = stringOf(parsed.code_verifier);
72
+ if (code === '' || verifier === '') return { status: 'failed', error: 'OpenAI approved the sign-in but sent no code back' };
73
+ const tokens = await exchangeCode(entry.issuer, code, verifier, `${entry.issuer}/deviceauth/callback`, fetchImpl, now);
74
+ return { status: 'done', tokens };
75
+ }
76
+
77
+ export async function pollDeviceLogin(id: string, fetchImpl: typeof fetch = fetch, now = Date.now()): Promise<DeviceStatus> {
78
+ sweep(now);
79
+ const entry = logins.get(id);
80
+ if (entry === undefined) throw new CodexAuthError('this sign-in has expired or already finished; press Connect again');
81
+ if (entry.polledAt !== null && now - entry.polledAt < entry.interval * 1000) return { status: 'pending' };
82
+ entry.polledAt = now;
83
+ let result: DeviceStatus;
84
+ try {
85
+ result = await claim(entry, fetchImpl, now);
86
+ } catch (err) {
87
+ result = { status: 'failed', error: errMsg(err) };
88
+ }
89
+ if (result.status !== 'pending') logins.delete(id);
90
+ return result;
91
+ }
@@ -0,0 +1,173 @@
1
+ import { isRecord } from '@metro-labs/core/is-record';
2
+ import { encodeSignature } from './codex-translate.js';
3
+ import { errorFrame } from './forward.js';
4
+ import { frame, messageEnd, messageStart, type Usage } from './frames.js';
5
+ import { stringOf } from '@metro-labs/http/api-http';
6
+
7
+ type Item = Record<string, unknown>;
8
+
9
+ interface Block {
10
+ index: number;
11
+ kind: 'text' | 'thinking' | 'tool_use';
12
+ deltas: number;
13
+ }
14
+
15
+ const responseOf = (data: Item): Item => (isRecord(data.response) ? data.response : {});
16
+ const itemOf = (data: Item): Item => (isRecord(data.item) ? data.item : {});
17
+
18
+ function errorKind(code: string): string {
19
+ if (code === 'rate_limit_exceeded' || code === 'usage_limit_reached') return 'rate_limit_error';
20
+ if (code === 'context_length_exceeded' || code === 'invalid_prompt') return 'invalid_request_error';
21
+ if (code === 'insufficient_quota') return 'permission_error';
22
+ return 'api_error';
23
+ }
24
+
25
+ function usageOf(response: Item): Usage {
26
+ const usage = isRecord(response.usage) ? response.usage : {};
27
+ const details = isRecord(usage.input_tokens_details) ? usage.input_tokens_details : {};
28
+ const cached = typeof details.cached_tokens === 'number' ? details.cached_tokens : 0;
29
+ const input = typeof usage.input_tokens === 'number' ? usage.input_tokens : 0;
30
+ return {
31
+ input_tokens: Math.max(0, input - cached),
32
+ output_tokens: typeof usage.output_tokens === 'number' ? usage.output_tokens : 0,
33
+ cache_read_input_tokens: cached,
34
+ };
35
+ }
36
+
37
+ const startBlock = (kind: Block['kind'], id: string, name: string): Item =>
38
+ kind === 'text' ? { type: 'text', text: '' } : kind === 'thinking' ? { type: 'thinking', thinking: '' } : { type: 'tool_use', id, name, input: {} };
39
+
40
+ const messageText = (item: Item): string =>
41
+ Array.isArray(item.content) ? item.content.filter(isRecord).map((part) => stringOf(part.text)).join('') : '';
42
+
43
+ export class CodexEventTranslator {
44
+ private readonly blocks = new Map<string, Block>();
45
+ private next = 0;
46
+ private started = false;
47
+ private toolCalls = 0;
48
+ private done = false;
49
+ private readonly model: string;
50
+ private readonly restore: (name: string) => string;
51
+ private readonly handlers: Record<string, (data: Item) => string> = {
52
+ 'response.created': (d) => this.start(responseOf(d)),
53
+ 'response.output_item.added': (d) => this.start({}) + this.itemAdded(itemOf(d)),
54
+ 'response.output_text.delta': (d) => this.delta(stringOf(d.item_id), 'text', stringOf(d.delta)),
55
+ 'response.reasoning_summary_text.delta': (d) => this.delta(stringOf(d.item_id), 'thinking', stringOf(d.delta)),
56
+ 'response.reasoning_summary_part.added': (d) => this.partAdded(stringOf(d.item_id)),
57
+ 'response.function_call_arguments.delta': (d) => this.delta(stringOf(d.item_id), 'tool_use', stringOf(d.delta)),
58
+ 'response.output_item.done': (d) => this.itemDone(itemOf(d)),
59
+ 'response.completed': (d) => this.start(responseOf(d)) + this.completed(responseOf(d)),
60
+ 'response.failed': (d) => this.failed(responseOf(d), 'Codex failed the response'),
61
+ 'response.incomplete': (d) => this.incomplete(responseOf(d)),
62
+ error: (d) => this.failed({ error: d }, 'Codex stream error'),
63
+ };
64
+
65
+ constructor(model: string, restore: (name: string) => string = (name) => name) {
66
+ this.model = model;
67
+ this.restore = restore;
68
+ }
69
+
70
+ get finished(): boolean {
71
+ return this.done;
72
+ }
73
+
74
+ private open(key: string, kind: Block['kind'], id = '', name = ''): string {
75
+ const block: Block = { index: this.next, kind, deltas: 0 };
76
+ this.next += 1;
77
+ this.blocks.set(key, block);
78
+ return frame('content_block_start', { type: 'content_block_start', index: block.index, content_block: startBlock(kind, id, name) });
79
+ }
80
+
81
+ private start(response: Item): string {
82
+ if (this.started) return '';
83
+ this.started = true;
84
+ return messageStart(stringOf(response.id) || 'msg_codex', this.model);
85
+ }
86
+
87
+ private openCall(key: string, item: Item): string {
88
+ this.toolCalls += 1;
89
+ return this.open(key, 'tool_use', stringOf(item.call_id) || key, this.restore(stringOf(item.name)));
90
+ }
91
+
92
+ private itemAdded(item: Item): string {
93
+ const key = stringOf(item.id) || String(this.next);
94
+ if (item.type === 'function_call') return this.openCall(key, item);
95
+ if (item.type === 'reasoning') return this.open(key, 'thinking');
96
+ return '';
97
+ }
98
+
99
+ private partAdded(key: string): string {
100
+ const block = this.blocks.get(key);
101
+ return block !== undefined && block.deltas > 0 ? this.delta(key, 'thinking', '\n\n') : '';
102
+ }
103
+
104
+ private delta(key: string, kind: Block['kind'], text: string): string {
105
+ const opened = this.blocks.has(key) ? '' : this.open(key, kind);
106
+ const block = this.blocks.get(key);
107
+ if (block === undefined) return opened;
108
+ block.deltas += 1;
109
+ const delta =
110
+ kind === 'tool_use' ? { type: 'input_json_delta', partial_json: text } : kind === 'text' ? { type: 'text_delta', text } : { type: 'thinking_delta', thinking: text };
111
+ return opened + frame('content_block_delta', { type: 'content_block_delta', index: block.index, delta });
112
+ }
113
+
114
+ private stop(key: string): string {
115
+ const block = this.blocks.get(key);
116
+ if (block === undefined) return '';
117
+ this.blocks.delete(key);
118
+ return frame('content_block_stop', { type: 'content_block_stop', index: block.index });
119
+ }
120
+
121
+ private reasoningDone(key: string, item: Item): string {
122
+ const opened = this.blocks.has(key) ? '' : this.open(key, 'thinking');
123
+ const block = this.blocks.get(key);
124
+ if (block === undefined || typeof item.encrypted_content !== 'string') return opened + this.stop(key);
125
+ const signature = encodeSignature({ id: key, encrypted_content: item.encrypted_content });
126
+ return opened + frame('content_block_delta', { type: 'content_block_delta', index: block.index, delta: { type: 'signature_delta', signature } }) + this.stop(key);
127
+ }
128
+
129
+ private callDone(key: string, item: Item): string {
130
+ let out = this.blocks.has(key) ? '' : this.openCall(key, item);
131
+ if (this.blocks.get(key)?.deltas === 0 && stringOf(item.arguments) !== '') out += this.delta(key, 'tool_use', stringOf(item.arguments));
132
+ return out + this.stop(key);
133
+ }
134
+
135
+ private itemDone(item: Item): string {
136
+ const key = stringOf(item.id);
137
+ if (item.type === 'reasoning') return this.reasoningDone(key, item);
138
+ if (item.type === 'function_call') return this.callDone(key, item);
139
+ if (item.type === 'message' && !this.blocks.has(key) && messageText(item) !== '') return this.delta(key, 'text', messageText(item)) + this.stop(key);
140
+ return this.stop(key);
141
+ }
142
+
143
+ private completed(response: Item, forced: string | null = null): string {
144
+ this.done = true;
145
+ let out = '';
146
+ for (const key of [...this.blocks.keys()]) out += this.stop(key);
147
+ const stop = forced ?? (this.toolCalls > 0 ? 'tool_use' : 'end_turn');
148
+ return out + messageEnd(stop, usageOf(response));
149
+ }
150
+
151
+ private incomplete(response: Item): string {
152
+ const details = isRecord(response.incomplete_details) ? response.incomplete_details : {};
153
+ if (details.reason === 'max_output_tokens') return this.start(response) + this.completed(response, 'max_tokens');
154
+ return this.failed(response, `Codex left the response incomplete (${stringOf(details.reason) || 'no reason given'})`);
155
+ }
156
+
157
+ private failed(response: Item, fallback: string): string {
158
+ this.done = true;
159
+ const error = isRecord(response.error) ? response.error : {};
160
+ return errorFrame(errorKind(stringOf(error.code)), stringOf(error.message) || fallback);
161
+ }
162
+
163
+ push(event: string, data: Item): string {
164
+ const handler = this.handlers[event];
165
+ return handler === undefined ? '' : handler(data);
166
+ }
167
+
168
+ close(message = 'Codex ended the stream before completing the response'): string {
169
+ if (this.done) return '';
170
+ this.done = true;
171
+ return errorFrame('api_error', message);
172
+ }
173
+ }
@@ -0,0 +1,195 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { isRecord } from '@metro-labs/core/is-record';
3
+ import { resultText } from './text.js';
4
+ import { stringOf } from '@metro-labs/http/api-http';
5
+
6
+ const SIGNATURE_PREFIX = 'metro-codex:';
7
+ const DEFAULT_EFFORT = 'medium';
8
+ const EFFORTS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh']);
9
+ const EFFORT_ALIAS: Record<string, string> = { max: 'xhigh' };
10
+ const NAME_MAX = 64;
11
+ const NAME_RE = /^[a-zA-Z0-9_-]+$/;
12
+ const HASH_LEN = 8;
13
+
14
+ type Item = Record<string, unknown>;
15
+
16
+ export interface TranslateOptions {
17
+ promptCacheKey: string;
18
+ names?: ToolNames;
19
+ }
20
+
21
+ export interface ResponsesRequest {
22
+ model: string;
23
+ instructions?: string;
24
+ input: Item[];
25
+ tools: Item[];
26
+ tool_choice: unknown;
27
+ parallel_tool_calls: boolean;
28
+ reasoning: { effort: string; summary: string };
29
+ store: false;
30
+ stream: true;
31
+ include: string[];
32
+ prompt_cache_key: string;
33
+ }
34
+
35
+
36
+ export function codexToolName(name: string): string {
37
+ if (name.length <= NAME_MAX && NAME_RE.test(name)) return name;
38
+ const clean = name.replace(/[^a-zA-Z0-9_-]/g, '_');
39
+ const hash = createHash('sha256').update(name).digest('hex').slice(0, HASH_LEN);
40
+ return `${clean.slice(0, NAME_MAX - HASH_LEN - 1)}_${hash}`;
41
+ }
42
+
43
+ export class ToolNames {
44
+ private readonly original = new Map<string, string>();
45
+
46
+ alias(name: string): string {
47
+ const alias = codexToolName(name);
48
+ if (alias !== name) this.original.set(alias, name);
49
+ return alias;
50
+ }
51
+
52
+ restore(alias: string): string {
53
+ return this.original.get(alias) ?? alias;
54
+ }
55
+ }
56
+
57
+ function blocksText(content: unknown): string {
58
+ if (typeof content === 'string') return content;
59
+ if (!Array.isArray(content)) return '';
60
+ return content
61
+ .filter(isRecord)
62
+ .filter((block) => block.type === 'text')
63
+ .map((block) => stringOf(block.text))
64
+ .join('\n');
65
+ }
66
+
67
+ export function systemText(system: unknown): string {
68
+ return blocksText(system).trim();
69
+ }
70
+
71
+ export function encodeSignature(item: Item): string {
72
+ return `${SIGNATURE_PREFIX}${Buffer.from(JSON.stringify(item)).toString('base64url')}`;
73
+ }
74
+
75
+ export function decodeSignature(signature: unknown): Item | null {
76
+ if (typeof signature !== 'string' || !signature.startsWith(SIGNATURE_PREFIX)) return null;
77
+ try {
78
+ const parsed: unknown = JSON.parse(Buffer.from(signature.slice(SIGNATURE_PREFIX.length), 'base64url').toString('utf8'));
79
+ return isRecord(parsed) ? parsed : null;
80
+ } catch {
81
+ return null;
82
+ }
83
+ }
84
+
85
+ function userPart(block: Item): Item | null {
86
+ if (block.type === 'text') return { type: 'input_text', text: stringOf(block.text) };
87
+ if (block.type === 'image' && isRecord(block.source) && block.source.type === 'base64')
88
+ return { type: 'input_image', image_url: `data:${stringOf(block.source.media_type)};base64,${stringOf(block.source.data)}` };
89
+ return null;
90
+ }
91
+
92
+ function userItems(content: unknown): Item[] {
93
+ if (typeof content === 'string') return [{ type: 'message', role: 'user', content: [{ type: 'input_text', text: content }] }];
94
+ if (!Array.isArray(content)) return [];
95
+ const out: Item[] = [];
96
+ let parts: Item[] = [];
97
+ const flush = (): void => {
98
+ if (parts.length > 0) out.push({ type: 'message', role: 'user', content: parts });
99
+ parts = [];
100
+ };
101
+ for (const block of content.filter(isRecord)) {
102
+ if (block.type === 'tool_result') {
103
+ flush();
104
+ out.push({ type: 'function_call_output', call_id: stringOf(block.tool_use_id), output: resultText(block) });
105
+ continue;
106
+ }
107
+ const part = userPart(block);
108
+ if (part !== null) parts.push(part);
109
+ }
110
+ flush();
111
+ return out;
112
+ }
113
+
114
+ function assistantItems(content: unknown, names: ToolNames): Item[] {
115
+ if (typeof content === 'string') return [{ type: 'message', role: 'assistant', content: [{ type: 'output_text', text: content }] }];
116
+ if (!Array.isArray(content)) return [];
117
+ const out: Item[] = [];
118
+ for (const block of content.filter(isRecord)) {
119
+ if (block.type === 'text') out.push({ type: 'message', role: 'assistant', content: [{ type: 'output_text', text: stringOf(block.text) }] });
120
+ else if (block.type === 'tool_use')
121
+ out.push({ type: 'function_call', call_id: stringOf(block.id), name: names.alias(stringOf(block.name)), arguments: JSON.stringify(block.input ?? {}) });
122
+ else if (block.type === 'thinking') {
123
+ const reasoning = decodeSignature(block.signature);
124
+ if (reasoning !== null) out.push({ type: 'reasoning', ...reasoning, summary: [] });
125
+ }
126
+ }
127
+ return out;
128
+ }
129
+
130
+ export function inputItems(messages: unknown, names = new ToolNames()): Item[] {
131
+ const out: Item[] = [];
132
+ if (!Array.isArray(messages)) return out;
133
+ for (const message of messages.filter(isRecord)) {
134
+ if (message.role === 'assistant') out.push(...assistantItems(message.content, names));
135
+ else out.push(...userItems(message.content));
136
+ }
137
+ return out;
138
+ }
139
+
140
+ export function toolItems(tools: unknown, names = new ToolNames()): Item[] {
141
+ if (!Array.isArray(tools)) return [];
142
+ return tools
143
+ .filter(isRecord)
144
+ .filter((tool) => typeof tool.name === 'string' && isRecord(tool.input_schema))
145
+ .map((tool) => ({
146
+ type: 'function',
147
+ name: names.alias(stringOf(tool.name)),
148
+ description: stringOf(tool.description),
149
+ parameters: tool.input_schema,
150
+ strict: false,
151
+ }));
152
+ }
153
+
154
+ export function toolChoiceOf(choice: unknown, names = new ToolNames()): unknown {
155
+ if (!isRecord(choice)) return 'auto';
156
+ if (choice.type === 'any') return 'required';
157
+ if (choice.type === 'none') return 'none';
158
+ if (choice.type === 'tool' && typeof choice.name === 'string') return { type: 'function', name: names.alias(choice.name) };
159
+ return 'auto';
160
+ }
161
+
162
+ function configuredEffort(body: Item): string | null {
163
+ const config = isRecord(body.output_config) ? body.output_config : {};
164
+ const effort = typeof config.effort === 'string' ? (EFFORT_ALIAS[config.effort] ?? config.effort) : '';
165
+ return EFFORTS.has(effort) ? effort : null;
166
+ }
167
+
168
+ export function effortOf(body: Item): string {
169
+ const configured = configuredEffort(body);
170
+ if (configured !== null) return configured;
171
+ const thinking = isRecord(body.thinking) ? body.thinking : {};
172
+ if (thinking.type === 'disabled') return 'low';
173
+ const budget = typeof thinking.budget_tokens === 'number' ? thinking.budget_tokens : 0;
174
+ if (budget > 0 && budget <= 4_000) return 'low';
175
+ if (budget > 16_000) return 'high';
176
+ return DEFAULT_EFFORT;
177
+ }
178
+
179
+ export function toResponsesRequest(body: Item, model: string, opts: TranslateOptions): ResponsesRequest {
180
+ const system = systemText(body.system);
181
+ const names = opts.names ?? new ToolNames();
182
+ return {
183
+ model,
184
+ ...(system === '' ? {} : { instructions: system }),
185
+ input: inputItems(body.messages, names),
186
+ tools: toolItems(body.tools, names),
187
+ tool_choice: toolChoiceOf(body.tool_choice, names),
188
+ parallel_tool_calls: true,
189
+ reasoning: { effort: effortOf(body), summary: 'auto' },
190
+ store: false,
191
+ stream: true,
192
+ include: ['reasoning.encrypted_content'],
193
+ prompt_cache_key: opts.promptCacheKey,
194
+ };
195
+ }