@stage-labs/metro 0.1.0-beta.13 → 0.1.0-beta.131

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 (329) hide show
  1. package/dist/api.js +14 -0
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +176 -0
  4. package/dist/cli.js +101 -0
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +93 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +43 -0
  11. package/dist/plugin.js +120 -0
  12. package/dist/provider-flags.js +9 -0
  13. package/dist/route.js +38 -0
  14. package/dist/runtime-install.js +97 -0
  15. package/dist/runtime.js +74 -0
  16. package/dist/serve.js +150 -0
  17. package/dist/service.js +242 -0
  18. package/dist/tail.js +119 -0
  19. package/dist/update.js +45 -0
  20. package/dist/version.js +97 -0
  21. package/package.json +14 -27
  22. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  23. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +18 -0
  24. package/runtime/marketplace/plugin/.mcp.json +1 -0
  25. package/runtime/marketplace/plugin/bin/guard.mjs +55 -0
  26. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +122 -0
  27. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  28. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  29. package/runtime/marketplace/plugin/orchestrator.md +16 -0
  30. package/runtime/marketplace/plugin/skills/refresh/SKILL.md +15 -0
  31. package/runtime/node_modules/@metro-labs/core/package.json +43 -0
  32. package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
  33. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  34. package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
  35. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  36. package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
  37. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  38. package/runtime/node_modules/@metro-labs/core/src/lines.ts +87 -0
  39. package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
  40. package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
  41. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
  42. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  44. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
  47. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +108 -0
  48. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  49. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  50. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  51. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +70 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +365 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +43 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +300 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +122 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +371 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +155 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +45 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +95 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/seed.ts +33 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +209 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +309 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +239 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +310 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +175 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +77 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +296 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +286 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +332 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +229 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +269 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +207 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +229 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +372 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +223 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +96 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  174. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  175. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  176. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  177. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  178. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  179. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  180. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  181. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  182. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  183. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  191. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  192. package/runtime/node_modules/@metro-labs/http/package.json +33 -0
  193. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  194. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
  195. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  196. package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
  197. package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
  198. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  199. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  200. package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
  201. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  202. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  203. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  204. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  205. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  206. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  207. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  208. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  209. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  210. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  211. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  212. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  213. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  214. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  215. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  216. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  217. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  218. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  219. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  220. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  221. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  222. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  223. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  224. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  225. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  226. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  227. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  228. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  229. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  230. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  231. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  232. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +100 -0
  233. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +219 -0
  234. package/runtime/node_modules/@metro-labs/threema/src/api.ts +130 -0
  235. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +132 -0
  236. package/runtime/node_modules/@metro-labs/threema/src/format.ts +123 -0
  237. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  238. package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
  239. package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
  240. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  241. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  242. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  243. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  244. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  245. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  246. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +190 -0
  247. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  248. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  249. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +347 -0
  250. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  251. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  252. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  253. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  254. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  255. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  256. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  257. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  258. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  259. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  260. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  261. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  262. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +18 -0
  263. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  264. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  265. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  266. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  267. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  268. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  269. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  270. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  271. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  272. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  273. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  274. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  275. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  276. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  277. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  278. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  279. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  280. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  281. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  282. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  283. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  284. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  285. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  286. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  287. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  288. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  289. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  290. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  291. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  292. package/runtime/runtime.json +3 -0
  293. package/runtime/server.ts +1 -0
  294. package/runtime/stations.json +39 -0
  295. package/runtime/trains/.keep +0 -0
  296. package/LICENSE +0 -21
  297. package/README.md +0 -252
  298. package/dist/broker.js +0 -202
  299. package/dist/cache.js +0 -69
  300. package/dist/cli/actions.js +0 -206
  301. package/dist/cli/config.js +0 -185
  302. package/dist/cli/index.js +0 -188
  303. package/dist/cli/skill.js +0 -62
  304. package/dist/cli/tail.js +0 -164
  305. package/dist/cli/util.js +0 -72
  306. package/dist/cli/webhook.js +0 -81
  307. package/dist/codex-rc.js +0 -245
  308. package/dist/dispatcher.js +0 -128
  309. package/dist/history.js +0 -121
  310. package/dist/ipc.js +0 -72
  311. package/dist/log.js +0 -15
  312. package/dist/monitor.js +0 -194
  313. package/dist/paths.js +0 -68
  314. package/dist/registry.js +0 -48
  315. package/dist/stations/claude.js +0 -45
  316. package/dist/stations/codex.js +0 -68
  317. package/dist/stations/discord.js +0 -216
  318. package/dist/stations/index.js +0 -129
  319. package/dist/stations/telegram-md.js +0 -34
  320. package/dist/stations/telegram-upload.js +0 -113
  321. package/dist/stations/telegram.js +0 -234
  322. package/dist/stations/webhook.js +0 -103
  323. package/dist/tunnel.js +0 -64
  324. package/dist/webhooks.js +0 -41
  325. package/docs/broker.md +0 -214
  326. package/docs/monitor.md +0 -163
  327. package/docs/uri-scheme.md +0 -124
  328. package/docs/users.md +0 -226
  329. package/skills/metro/SKILL.md +0 -256
@@ -0,0 +1,231 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import type { IncomingMessage, ServerResponse } from 'node:http';
3
+ import { arch, platform, release } from 'node:os';
4
+ import { isRecord } from '@metro-labs/core/is-record';
5
+ import { errMsg, log } from '@metro-labs/core/log';
6
+ import { refreshTokens, tokensStale, type CodexTokens } from './codex-auth.js';
7
+ import { assembleMessage, CodexEventTranslator, parseEvent, SseParser } from './codex-stream.js';
8
+ import { ToolNames, toResponsesRequest } from './codex-translate.js';
9
+ import { GatewayError, idleMessage, providerStatus, sendError, upstreamMessage, type Watch } from './forward.js';
10
+ import type { ModelConfig } from './model-config.js';
11
+ import { noteUsageHeaders, UsageScanner } from './usage.js';
12
+
13
+ export const CODEX_BASE = 'https://chatgpt.com/backend-api/codex';
14
+ const CODEX_VERSION = '0.153.4';
15
+ const VERSION_RE = /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.]+)?$/;
16
+ const PING_MS = 25_000;
17
+ const STATUS_OF: Record<string, number> = { rate_limit_error: 429, invalid_request_error: 400, permission_error: 403, overloaded_error: 529 };
18
+
19
+ export interface CodexDeps {
20
+ base?: string;
21
+ issuer?: string;
22
+ fetchImpl?: typeof fetch;
23
+ save: (tokens: CodexTokens) => void;
24
+ }
25
+
26
+ export interface CodexState {
27
+ refreshing: Promise<CodexTokens> | null;
28
+ latest: CodexTokens | null;
29
+ }
30
+
31
+ export const freshCodexState = (): CodexState => ({ refreshing: null, latest: null });
32
+
33
+ const OS_NAMES: Record<string, string> = { darwin: 'Mac OS', linux: 'Linux', win32: 'Windows' };
34
+
35
+ export function codexVersion(): string {
36
+ const wanted = process.env.METRO_CODEX_VERSION?.trim() ?? '';
37
+ if (wanted === '') return CODEX_VERSION;
38
+ if (VERSION_RE.test(wanted)) return wanted;
39
+ log.warn({ value: wanted }, 'gateway: METRO_CODEX_VERSION is not a version like 0.153.4; using the built-in one');
40
+ return CODEX_VERSION;
41
+ }
42
+
43
+ export const userAgent = (): string => `codex_cli_rs/${codexVersion()} (${OS_NAMES[platform()] ?? platform()} ${release()}; ${arch()}) metro`;
44
+
45
+ function headersFor(tokens: CodexTokens, sessionId: string): Record<string, string> {
46
+ return {
47
+ authorization: `Bearer ${tokens.accessToken}`,
48
+ 'chatgpt-account-id': tokens.accountId,
49
+ 'content-type': 'application/json',
50
+ accept: 'text/event-stream',
51
+ originator: 'codex_cli_rs',
52
+ 'user-agent': userAgent(),
53
+ 'openai-beta': 'responses=experimental',
54
+ 'session-id': sessionId,
55
+ };
56
+ }
57
+
58
+ const newerThan = (a: CodexTokens, b: CodexTokens): boolean => Date.parse(a.savedAt) > Date.parse(b.savedAt);
59
+
60
+ function refreshed(tokens: CodexTokens, deps: CodexDeps, state: CodexState): Promise<CodexTokens> {
61
+ const latest = state.latest;
62
+ if (latest !== null && newerThan(latest, tokens) && !tokensStale(latest)) return Promise.resolve(latest);
63
+ if (state.refreshing !== null) return state.refreshing;
64
+ const run = refreshTokens(tokens, deps.issuer, deps.fetchImpl)
65
+ .then((fresh) => {
66
+ deps.save(fresh);
67
+ state.latest = fresh;
68
+ return fresh;
69
+ })
70
+ .catch((err: unknown) => {
71
+ throw new GatewayError(403, 'permission_error', `Codex sign-in expired (${errMsg(err)}): connect again on the Model page`);
72
+ })
73
+ .finally(() => {
74
+ state.refreshing = null;
75
+ });
76
+ state.refreshing = run;
77
+ return run;
78
+ }
79
+
80
+ export function currentTokens(cfg: ModelConfig, deps: CodexDeps, state: CodexState): Promise<CodexTokens> {
81
+ const tokens = cfg.codex.auth;
82
+ if (tokens === null) throw new GatewayError(400, 'invalid_request_error', 'Codex is not connected: sign in on the Model page.');
83
+ return tokensStale(tokens) ? refreshed(tokens, deps, state) : Promise.resolve(tokens);
84
+ }
85
+
86
+ function errorKind(status: number): string {
87
+ if (status === 401 || status === 403) return 'permission_error';
88
+ if (status === 429) return 'rate_limit_error';
89
+ if (status === 400 || status === 404 || status === 422) return 'invalid_request_error';
90
+ return 'api_error';
91
+ }
92
+
93
+ const sessionIdOf = (req: IncomingMessage): string => {
94
+ const raw = req.headers['x-claude-code-session-id'];
95
+ const given = (Array.isArray(raw) ? raw[0] : raw)?.trim() ?? '';
96
+ return given === '' ? randomUUID() : given;
97
+ };
98
+
99
+ interface Call {
100
+ body: Record<string, unknown>;
101
+ model: string;
102
+ sessionId: string;
103
+ watch: Watch;
104
+ deps: CodexDeps;
105
+ names: ToolNames;
106
+ }
107
+
108
+ function send(call: Call, tokens: CodexTokens): Promise<Response> {
109
+ const request = toResponsesRequest(call.body, call.model, { promptCacheKey: call.sessionId, names: call.names });
110
+ return (call.deps.fetchImpl ?? fetch)(`${call.deps.base ?? CODEX_BASE}/responses`, {
111
+ method: 'POST',
112
+ headers: headersFor(tokens, call.sessionId),
113
+ body: JSON.stringify(request),
114
+ signal: call.watch.signal,
115
+ redirect: 'manual',
116
+ });
117
+ }
118
+
119
+ async function reach(call: Call, cfg: ModelConfig, state: CodexState): Promise<Response> {
120
+ let tokens = await currentTokens(cfg, call.deps, state);
121
+ let upstream = await send(call, tokens);
122
+ if (upstream.status === 401) {
123
+ await upstream.body?.cancel();
124
+ tokens = await refreshed(tokens, call.deps, state);
125
+ upstream = await send(call, tokens);
126
+ }
127
+ return upstream;
128
+ }
129
+
130
+ async function relayStream(upstream: Response, res: ServerResponse, model: string, names: ToolNames, watch: Watch): Promise<void> {
131
+ res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-store', connection: 'keep-alive' });
132
+ const translator = new CodexEventTranslator(model, (name) => names.restore(name));
133
+ const body = upstream.body;
134
+ if (body === null) {
135
+ res.end(translator.close());
136
+ return;
137
+ }
138
+ const ping = setInterval(() => res.write('event: ping\ndata: {"type":"ping"}\n\n'), PING_MS);
139
+ const scanner = new UsageScanner('codex');
140
+ const emit = (frames: string): void => {
141
+ scanner.feed(frames);
142
+ res.write(frames);
143
+ };
144
+ try {
145
+ const parser = new SseParser();
146
+ const decoder = new TextDecoder();
147
+ const reader = body.getReader();
148
+ for (;;) {
149
+ const { done, value } = await reader.read();
150
+ if (done) break;
151
+ watch.touch();
152
+ for (const raw of parser.push(decoder.decode(value, { stream: true }))) {
153
+ const parsed = parseEvent(raw);
154
+ if (parsed !== null) emit(translator.push(parsed.event, parsed.data));
155
+ }
156
+ }
157
+ emit(translator.close());
158
+ } catch (err) {
159
+ if (!watch.idle()) throw err;
160
+ res.write(translator.finished ? '' : translator.close(idleMessage(watch.ms)));
161
+ } finally {
162
+ clearInterval(ping);
163
+ watch.stop();
164
+ res.end();
165
+ scanner.done();
166
+ }
167
+ }
168
+
169
+ async function relayWhole(upstream: Response, res: ServerResponse, model: string, names: ToolNames): Promise<void> {
170
+ const translator = new CodexEventTranslator(model, (name) => names.restore(name));
171
+ let frames = '';
172
+ const parser = new SseParser();
173
+ for (const raw of parser.push(await upstream.text())) {
174
+ const parsed = parseEvent(raw);
175
+ if (parsed !== null) frames += translator.push(parsed.event, parsed.data);
176
+ }
177
+ frames += translator.close();
178
+ const scanner = new UsageScanner('codex');
179
+ scanner.feed(frames);
180
+ scanner.done();
181
+ const message = assembleMessage(frames);
182
+ const kind = isRecord(message.error) ? String(message.error.type) : '';
183
+ res.writeHead(message.type === 'error' ? (STATUS_OF[kind] ?? 502) : 200, { 'content-type': 'application/json' });
184
+ res.end(JSON.stringify(message));
185
+ }
186
+
187
+ export async function codexMessages(
188
+ req: IncomingMessage,
189
+ res: ServerResponse,
190
+ body: Record<string, unknown>,
191
+ model: string,
192
+ cfg: ModelConfig,
193
+ deps: CodexDeps,
194
+ state: CodexState,
195
+ watch: Watch,
196
+ ): Promise<void> {
197
+ const call: Call = { body, model, sessionId: sessionIdOf(req), watch, deps, names: new ToolNames() };
198
+ const upstream = await reach(call, cfg, state);
199
+ noteUsageHeaders('codex', upstream.headers);
200
+ if (!upstream.ok) {
201
+ const text = await upstream.text();
202
+ sendError(res, providerStatus(upstream.status), errorKind(upstream.status), upstreamMessage(text, `Codex answered ${String(upstream.status)}`));
203
+ return;
204
+ }
205
+ if (body.stream === true) await relayStream(upstream, res, model, call.names, watch);
206
+ else await relayWhole(upstream, res, model, call.names);
207
+ }
208
+
209
+ export function codexCount(res: ServerResponse, body: Record<string, unknown>): void {
210
+ res.writeHead(200, { 'content-type': 'application/json' });
211
+ res.end(JSON.stringify({ input_tokens: Math.ceil(JSON.stringify(body).length / 4) }));
212
+ }
213
+
214
+ export async function codexModels(tokens: CodexTokens, deps: Omit<CodexDeps, 'save'>): Promise<string[]> {
215
+ const res = await (deps.fetchImpl ?? fetch)(`${deps.base ?? CODEX_BASE}/models?client_version=${codexVersion()}`, {
216
+ headers: { ...headersFor(tokens, randomUUID()), accept: 'application/json' },
217
+ redirect: 'manual',
218
+ });
219
+ if (!res.ok) throw new GatewayError(res.status, errorKind(res.status), `Codex would not list models (${String(res.status)})`);
220
+ const slugs = new Set<string>();
221
+ const walk = (value: unknown): void => {
222
+ if (Array.isArray(value)) value.forEach(walk);
223
+ else if (typeof value === 'object' && value !== null) {
224
+ const record = value as Record<string, unknown>;
225
+ if (typeof record.slug === 'string' && record.slug !== '') slugs.add(record.slug);
226
+ Object.values(record).forEach(walk);
227
+ }
228
+ };
229
+ walk(await res.json());
230
+ return [...slugs];
231
+ }
@@ -0,0 +1,123 @@
1
+ export interface EventStreamMessage {
2
+ headers: Record<string, string>;
3
+ payload: Buffer;
4
+ }
5
+
6
+ const PRELUDE_BYTES = 12;
7
+ const CRC_BYTES = 4;
8
+
9
+ const HEADER_TRUE = 0;
10
+ const HEADER_FALSE = 1;
11
+ const HEADER_BYTE = 2;
12
+ const HEADER_SHORT = 3;
13
+ const HEADER_INT = 4;
14
+ const HEADER_LONG = 5;
15
+ const HEADER_BYTES = 6;
16
+ const HEADER_STRING = 7;
17
+ const HEADER_TIMESTAMP = 8;
18
+ const HEADER_UUID = 9;
19
+
20
+ const FIXED_WIDTH: Record<number, number> = {
21
+ [HEADER_TRUE]: 0,
22
+ [HEADER_FALSE]: 0,
23
+ [HEADER_BYTE]: 1,
24
+ [HEADER_SHORT]: 2,
25
+ [HEADER_INT]: 4,
26
+ [HEADER_LONG]: 8,
27
+ [HEADER_TIMESTAMP]: 8,
28
+ [HEADER_UUID]: 16,
29
+ };
30
+
31
+ function readHeaderValue(buf: Buffer, at: number, type: number): { value: string; next: number } {
32
+ if (type === HEADER_BYTES || type === HEADER_STRING) {
33
+ const len = buf.readUInt16BE(at);
34
+ const raw = buf.subarray(at + 2, at + 2 + len);
35
+ const value = type === HEADER_STRING ? raw.toString('utf8') : raw.toString('base64');
36
+ return { value, next: at + 2 + len };
37
+ }
38
+ const width = FIXED_WIDTH[type];
39
+ if (width === undefined) throw new Error(`eventstream: unknown header type ${String(type)}`);
40
+ if (type === HEADER_TRUE) return { value: 'true', next: at };
41
+ if (type === HEADER_FALSE) return { value: 'false', next: at };
42
+ return { value: buf.subarray(at, at + width).toString('hex'), next: at + width };
43
+ }
44
+
45
+ function readHeaders(buf: Buffer): Record<string, string> {
46
+ const out: Record<string, string> = {};
47
+ let at = 0;
48
+ while (at < buf.length) {
49
+ const nameLen = buf.readUInt8(at);
50
+ const name = buf.subarray(at + 1, at + 1 + nameLen).toString('utf8');
51
+ const type = buf.readUInt8(at + 1 + nameLen);
52
+ const parsed = readHeaderValue(buf, at + 2 + nameLen, type);
53
+ out[name] = parsed.value;
54
+ at = parsed.next;
55
+ }
56
+ return out;
57
+ }
58
+
59
+ export function decodeFrame(buf: Buffer, offset: number): { message: EventStreamMessage; end: number } | null {
60
+ if (buf.length - offset < PRELUDE_BYTES) return null;
61
+ const total = buf.readUInt32BE(offset);
62
+ const headersLen = buf.readUInt32BE(offset + 4);
63
+ if (total < PRELUDE_BYTES + CRC_BYTES || headersLen > total) throw new Error('eventstream: malformed frame prelude');
64
+ if (buf.length - offset < total) return null;
65
+ const headersStart = offset + PRELUDE_BYTES;
66
+ const payloadStart = headersStart + headersLen;
67
+ const payloadEnd = offset + total - CRC_BYTES;
68
+ return {
69
+ message: {
70
+ headers: readHeaders(buf.subarray(headersStart, payloadStart)),
71
+ payload: Buffer.from(buf.subarray(payloadStart, payloadEnd)),
72
+ },
73
+ end: offset + total,
74
+ };
75
+ }
76
+
77
+ export class EventStreamDecoder {
78
+ private pending: Buffer = Buffer.alloc(0);
79
+
80
+ push(chunk: Buffer): EventStreamMessage[] {
81
+ this.pending = this.pending.length === 0 ? chunk : Buffer.concat([this.pending, chunk]);
82
+ const out: EventStreamMessage[] = [];
83
+ let offset = 0;
84
+ for (;;) {
85
+ const frame = decodeFrame(this.pending, offset);
86
+ if (frame === null) break;
87
+ out.push(frame.message);
88
+ offset = frame.end;
89
+ }
90
+ this.pending = this.pending.subarray(offset);
91
+ return out;
92
+ }
93
+ }
94
+
95
+ function encodeHeader(name: string, value: string): Buffer {
96
+ const nameBuf = Buffer.from(name, 'utf8');
97
+ const valueBuf = Buffer.from(value, 'utf8');
98
+ const out = Buffer.alloc(1 + nameBuf.length + 1 + 2 + valueBuf.length);
99
+ let at = 0;
100
+ out.writeUInt8(nameBuf.length, at);
101
+ at += 1;
102
+ nameBuf.copy(out, at);
103
+ at += nameBuf.length;
104
+ out.writeUInt8(HEADER_STRING, at);
105
+ at += 1;
106
+ out.writeUInt16BE(valueBuf.length, at);
107
+ at += 2;
108
+ valueBuf.copy(out, at);
109
+ return out;
110
+ }
111
+
112
+ export function encodeFrame(headers: Record<string, string>, payload: Buffer): Buffer {
113
+ const headerBuf = Buffer.concat(Object.entries(headers).map(([name, value]) => encodeHeader(name, value)));
114
+ const total = PRELUDE_BYTES + headerBuf.length + payload.length + CRC_BYTES;
115
+ const out = Buffer.alloc(total);
116
+ out.writeUInt32BE(total, 0);
117
+ out.writeUInt32BE(headerBuf.length, 4);
118
+ out.writeUInt32BE(0, 8);
119
+ headerBuf.copy(out, PRELUDE_BYTES);
120
+ payload.copy(out, PRELUDE_BYTES + headerBuf.length);
121
+ out.writeUInt32BE(0, total - CRC_BYTES);
122
+ return out;
123
+ }
@@ -0,0 +1,187 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { UsageScanner } from './usage.js';
3
+
4
+ export class GatewayError extends Error {
5
+ constructor(
6
+ readonly status: number,
7
+ readonly kind: string,
8
+ message: string,
9
+ ) {
10
+ super(message);
11
+ }
12
+ }
13
+
14
+ export const BODY_MAX = 64 * 1024 * 1024;
15
+ const DRAIN_FACTOR = 2;
16
+ const PING_MS = 25_000;
17
+ const IDLE_MS = 300_000;
18
+ const HOP_BY_HOP = new Set([
19
+ 'host',
20
+ 'connection',
21
+ 'content-length',
22
+ 'transfer-encoding',
23
+ 'keep-alive',
24
+ 'proxy-authorization',
25
+ 'te',
26
+ 'trailer',
27
+ 'upgrade',
28
+ 'accept-encoding',
29
+ 'x-metro-key',
30
+ ]);
31
+ const RESPONSE_DROP = new Set(['content-length', 'content-encoding', 'transfer-encoding', 'connection', 'keep-alive']);
32
+
33
+ export function sendError(res: ServerResponse, status: number, kind: string, message: string): void {
34
+ res.writeHead(status, { 'content-type': 'application/json' });
35
+ res.end(JSON.stringify({ type: 'error', error: { type: kind, message } }));
36
+ }
37
+
38
+ export const errorFrame = (kind: string, message: string): string => `event: error\ndata: ${JSON.stringify({ type: 'error', error: { type: kind, message } })}\n\n`;
39
+
40
+ export const providerStatus = (status: number): number => (status === 401 ? 403 : status);
41
+
42
+ export const idleMs = (): number => Number(process.env.METRO_GATEWAY_IDLE_MS) || IDLE_MS;
43
+
44
+ export const idleMessage = (ms: number): string => `metro gateway: the provider sent nothing for ${String(Math.round(ms / 1000))}s; giving up on this request`;
45
+
46
+ export interface Watch {
47
+ signal: AbortSignal;
48
+ ms: number;
49
+ touch: () => void;
50
+ idle: () => boolean;
51
+ stop: () => void;
52
+ }
53
+
54
+ export function watchUpstream(res: ServerResponse, ms = idleMs()): Watch {
55
+ const control = new AbortController();
56
+ let expired = false;
57
+ let timer: NodeJS.Timeout | null = null;
58
+ const stop = (): void => {
59
+ if (timer !== null) clearTimeout(timer);
60
+ timer = null;
61
+ };
62
+ const arm = (): void => {
63
+ stop();
64
+ timer = setTimeout(() => {
65
+ expired = true;
66
+ control.abort();
67
+ }, ms);
68
+ timer.unref();
69
+ };
70
+ res.on('close', () => {
71
+ stop();
72
+ if (!res.writableEnded) control.abort();
73
+ });
74
+ arm();
75
+ return { signal: control.signal, ms, touch: arm, idle: () => expired, stop };
76
+ }
77
+
78
+ export async function readBody(req: IncomingMessage): Promise<Buffer> {
79
+ const chunks: Buffer[] = [];
80
+ let total = 0;
81
+ let over = false;
82
+ for await (const chunk of req) {
83
+ const buf = chunk as Buffer;
84
+ total += buf.length;
85
+ if (total > BODY_MAX) {
86
+ over = true;
87
+ if (total > BODY_MAX * DRAIN_FACTOR) break;
88
+ continue;
89
+ }
90
+ chunks.push(buf);
91
+ }
92
+ if (over) throw new GatewayError(413, 'invalid_request_error', 'request too large');
93
+ return Buffer.concat(chunks);
94
+ }
95
+
96
+ export function parseJson(raw: Buffer): Record<string, unknown> {
97
+ let parsed: unknown;
98
+ try {
99
+ parsed = JSON.parse(raw.toString('utf8'));
100
+ } catch {
101
+ throw new GatewayError(400, 'invalid_request_error', 'body must be JSON');
102
+ }
103
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
104
+ throw new GatewayError(400, 'invalid_request_error', 'body must be a JSON object');
105
+ return parsed as Record<string, unknown>;
106
+ }
107
+
108
+ export function forwardedHeaders(req: IncomingMessage): Record<string, string> {
109
+ const out: Record<string, string> = {};
110
+ for (const [name, value] of Object.entries(req.headers)) {
111
+ if (HOP_BY_HOP.has(name) || value === undefined) continue;
112
+ out[name] = Array.isArray(value) ? value.join(', ') : value;
113
+ }
114
+ return out;
115
+ }
116
+
117
+ export function anthropicHeaders(req: IncomingMessage, apiKey: string): Record<string, string> {
118
+ const headers = forwardedHeaders(req);
119
+ delete headers.authorization;
120
+ const betas = (headers['anthropic-beta'] ?? '')
121
+ .split(',')
122
+ .map((beta) => beta.trim())
123
+ .filter((beta) => beta !== '' && !beta.includes('oauth'));
124
+ if (betas.length > 0) headers['anthropic-beta'] = betas.join(',');
125
+ else delete headers['anthropic-beta'];
126
+ headers['x-api-key'] = apiKey;
127
+ return headers;
128
+ }
129
+
130
+ export interface PipeOptions {
131
+ keepalive?: boolean;
132
+ ownCredential?: boolean;
133
+ scanner?: UsageScanner;
134
+ }
135
+
136
+ function openResponse(upstream: Response, res: ServerResponse, opts: PipeOptions): boolean {
137
+ const headers: Record<string, string> = {};
138
+ upstream.headers.forEach((value, name) => {
139
+ if (!RESPONSE_DROP.has(name)) headers[name] = value;
140
+ });
141
+ res.writeHead(opts.ownCredential === true ? providerStatus(upstream.status) : upstream.status, headers);
142
+ return (upstream.headers.get('content-type') ?? '').startsWith('text/event-stream');
143
+ }
144
+
145
+ async function pump(body: ReadableStream<Uint8Array>, res: ServerResponse, watch: Watch, scanner?: UsageScanner): Promise<void> {
146
+ const reader = body.getReader();
147
+ const decoder = scanner === undefined ? null : new TextDecoder();
148
+ for (;;) {
149
+ const { done, value } = await reader.read();
150
+ if (done) return;
151
+ watch.touch();
152
+ if (decoder !== null) scanner?.feed(decoder.decode(value, { stream: true }));
153
+ res.write(value);
154
+ }
155
+ }
156
+
157
+ export async function pipeResponse(upstream: Response, res: ServerResponse, watch: Watch, opts: PipeOptions = {}): Promise<void> {
158
+ const streaming = openResponse(upstream, res, opts);
159
+ const body = upstream.body;
160
+ if (body === null) {
161
+ res.end();
162
+ return;
163
+ }
164
+ const ping = opts.keepalive === true && streaming ? setInterval(() => res.write('event: ping\ndata: {"type":"ping"}\n\n'), PING_MS) : null;
165
+ try {
166
+ await pump(body, res, watch, opts.scanner);
167
+ } catch (err) {
168
+ if (!watch.idle()) throw err;
169
+ if (streaming) res.write(errorFrame('api_error', idleMessage(watch.ms)));
170
+ } finally {
171
+ if (ping !== null) clearInterval(ping);
172
+ watch.stop();
173
+ res.end();
174
+ opts.scanner?.done();
175
+ }
176
+ }
177
+
178
+ export function upstreamMessage(text: string, fallback: string): string {
179
+ if (text === '') return fallback;
180
+ try {
181
+ const parsed = JSON.parse(text) as { message?: unknown; Message?: unknown; error?: { message?: unknown } };
182
+ const message = parsed.message ?? parsed.Message ?? parsed.error?.message;
183
+ return typeof message === 'string' && message !== '' ? message : text;
184
+ } catch {
185
+ return text;
186
+ }
187
+ }