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

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 (328) 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/senders.ts +38 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +207 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +309 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +239 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +310 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +175 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +77 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +296 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +286 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +332 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +229 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +269 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +207 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +229 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +372 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +223 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +96 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  174. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  175. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  176. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  177. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  178. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  179. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  180. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  181. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  182. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  183. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  191. package/runtime/node_modules/@metro-labs/http/package.json +33 -0
  192. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  193. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
  194. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  195. package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
  196. package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
  197. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  198. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  199. package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
  200. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  201. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  202. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  203. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  204. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  205. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  206. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  207. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  208. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  209. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  210. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  211. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  212. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  213. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  214. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  215. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  216. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  217. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  218. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  219. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  220. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  221. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  222. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  223. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  224. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  225. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  226. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  227. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  228. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  229. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  230. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  231. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +100 -0
  232. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +219 -0
  233. package/runtime/node_modules/@metro-labs/threema/src/api.ts +130 -0
  234. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +132 -0
  235. package/runtime/node_modules/@metro-labs/threema/src/format.ts +123 -0
  236. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  237. package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
  238. package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
  239. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  240. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  241. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  242. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  243. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  244. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  245. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +190 -0
  246. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  247. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  248. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +347 -0
  249. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  250. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  251. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  252. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  253. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  254. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  255. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  256. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  257. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  258. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  259. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  260. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  261. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +18 -0
  262. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  263. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  264. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  265. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  266. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  267. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  268. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  269. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  270. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  271. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  272. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  273. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  274. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  275. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  276. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  277. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  278. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  279. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  280. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  281. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  282. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  283. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  284. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  285. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  286. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  287. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  288. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  289. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  290. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  291. package/runtime/runtime.json +3 -0
  292. package/runtime/server.ts +1 -0
  293. package/runtime/stations.json +39 -0
  294. package/runtime/trains/.keep +0 -0
  295. package/LICENSE +0 -21
  296. package/README.md +0 -252
  297. package/dist/broker.js +0 -202
  298. package/dist/cache.js +0 -69
  299. package/dist/cli/actions.js +0 -206
  300. package/dist/cli/config.js +0 -185
  301. package/dist/cli/index.js +0 -188
  302. package/dist/cli/skill.js +0 -62
  303. package/dist/cli/tail.js +0 -164
  304. package/dist/cli/util.js +0 -72
  305. package/dist/cli/webhook.js +0 -81
  306. package/dist/codex-rc.js +0 -245
  307. package/dist/dispatcher.js +0 -128
  308. package/dist/history.js +0 -121
  309. package/dist/ipc.js +0 -72
  310. package/dist/log.js +0 -15
  311. package/dist/monitor.js +0 -194
  312. package/dist/paths.js +0 -68
  313. package/dist/registry.js +0 -48
  314. package/dist/stations/claude.js +0 -45
  315. package/dist/stations/codex.js +0 -68
  316. package/dist/stations/discord.js +0 -216
  317. package/dist/stations/index.js +0 -129
  318. package/dist/stations/telegram-md.js +0 -34
  319. package/dist/stations/telegram-upload.js +0 -113
  320. package/dist/stations/telegram.js +0 -234
  321. package/dist/stations/webhook.js +0 -103
  322. package/dist/tunnel.js +0 -64
  323. package/dist/webhooks.js +0 -41
  324. package/docs/broker.md +0 -214
  325. package/docs/monitor.md +0 -163
  326. package/docs/uri-scheme.md +0 -124
  327. package/docs/users.md +0 -226
  328. package/skills/metro/SKILL.md +0 -256
@@ -0,0 +1,117 @@
1
+ import { createPrivateKey, createPublicKey } from 'node:crypto';
2
+ import {
3
+ GATEWAY_API,
4
+ isGatewayId,
5
+ normalizeThreemaId,
6
+ parsePrivateKey,
7
+ } from './ids.js';
8
+
9
+ export { parsePrivateKey } from './ids.js';
10
+
11
+ export class ThreemaGatewayError extends Error {}
12
+
13
+ export interface ThreemaCredentials {
14
+ gatewayId: string;
15
+ secret: string;
16
+ privateKey: string;
17
+ }
18
+
19
+ export interface ThreemaGatewayIdentity {
20
+ gatewayId: string;
21
+ publicKey: string;
22
+ credits: number;
23
+ }
24
+
25
+ const PKCS8_X25519_PREFIX = Buffer.from(
26
+ '302e020100300506032b656e04220420',
27
+ 'hex',
28
+ );
29
+ const TIMEOUT_MS = 10_000;
30
+ const PUBLIC_KEY_RE = /^[0-9a-f]{64}$/;
31
+
32
+ export function publicKeyOf(privateKeyHex: string): string {
33
+ const der = Buffer.concat([
34
+ PKCS8_X25519_PREFIX,
35
+ Buffer.from(privateKeyHex, 'hex'),
36
+ ]);
37
+ const key = createPrivateKey({ key: der, format: 'der', type: 'pkcs8' });
38
+ const jwk = createPublicKey(key).export({ format: 'jwk' });
39
+ return Buffer.from(String(jwk.x), 'base64url').toString('hex');
40
+ }
41
+
42
+ async function gatewayGet(
43
+ path: string,
44
+ gatewayId: string,
45
+ secret: string,
46
+ ): Promise<Response> {
47
+ const query = new URLSearchParams({ from: gatewayId, secret });
48
+ try {
49
+ return await fetch(`${GATEWAY_API}${path}?${query.toString()}`, {
50
+ signal: AbortSignal.timeout(TIMEOUT_MS),
51
+ });
52
+ } catch {
53
+ throw new ThreemaGatewayError(
54
+ 'could not reach the Threema Gateway to check those credentials',
55
+ );
56
+ }
57
+ }
58
+
59
+ function refused(res: Response, what: string): ThreemaGatewayError {
60
+ if (res.status === 401)
61
+ return new ThreemaGatewayError(
62
+ 'Threema rejected that Gateway ID or API secret',
63
+ );
64
+ if (res.status === 404)
65
+ return new ThreemaGatewayError(`Threema does not know ${what}`);
66
+ return new ThreemaGatewayError(
67
+ `the Threema Gateway answered ${res.status} while checking ${what}`,
68
+ );
69
+ }
70
+
71
+ async function fetchCredits(gatewayId: string, secret: string): Promise<number> {
72
+ const res = await gatewayGet('/credits', gatewayId, secret);
73
+ if (!res.ok) throw refused(res, 'the credit balance');
74
+ const credits = Number((await res.text()).trim());
75
+ if (!Number.isInteger(credits))
76
+ throw new ThreemaGatewayError(
77
+ 'the Threema Gateway gave an unexpected answer for the credit balance',
78
+ );
79
+ return credits;
80
+ }
81
+
82
+ async function fetchPublicKey(gatewayId: string, secret: string): Promise<string> {
83
+ const res = await gatewayGet(
84
+ `/pubkeys/${encodeURIComponent(gatewayId)}`,
85
+ gatewayId,
86
+ secret,
87
+ );
88
+ if (!res.ok) throw refused(res, `the public key of ${gatewayId}`);
89
+ const hex = (await res.text()).trim().toLowerCase();
90
+ if (!PUBLIC_KEY_RE.test(hex))
91
+ throw new ThreemaGatewayError(
92
+ 'the Threema Gateway gave an unexpected answer for the public key',
93
+ );
94
+ return hex;
95
+ }
96
+
97
+ export async function verifyThreemaGateway(
98
+ input: ThreemaCredentials,
99
+ ): Promise<ThreemaGatewayIdentity> {
100
+ const gatewayId = normalizeThreemaId(input.gatewayId);
101
+ if (!isGatewayId(gatewayId))
102
+ throw new ThreemaGatewayError(
103
+ 'a Threema Gateway ID is a * followed by 7 letters or digits, as in *ABCDEFG',
104
+ );
105
+ const privateKey = parsePrivateKey(input.privateKey);
106
+ if (privateKey === null)
107
+ throw new ThreemaGatewayError(
108
+ 'the private key is the 64 hex characters from the key file Threema gave you, with or without its private: prefix',
109
+ );
110
+ const credits = await fetchCredits(gatewayId, input.secret);
111
+ const registered = await fetchPublicKey(gatewayId, input.secret);
112
+ if (registered !== publicKeyOf(privateKey))
113
+ throw new ThreemaGatewayError(
114
+ `that private key does not belong to ${gatewayId}: the Gateway holds a different public key for it`,
115
+ );
116
+ return { gatewayId, publicKey: registered, credits };
117
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@metro-labs/webhook",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/station.ts"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "tsc --noEmit && bun test test/"
12
+ },
13
+ "dependencies": {
14
+ "@metro-labs/core": "workspace:*"
15
+ },
16
+ "devDependencies": {
17
+ "@stage-labs/config": "0.1.0-beta.2",
18
+ "eslint": "^10.3.0",
19
+ "knip": "^6.15.0",
20
+ "madge": "^8.0.0",
21
+ "typescript": "^5"
22
+ }
23
+ }
@@ -0,0 +1,37 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { Line } from '@metro-labs/core/lines';
3
+ import { mintId, type MetroEvent } from '@metro-labs/core/events';
4
+ import type { Endpoint } from '@metro-labs/core/endpoints';
5
+ import type { Station, Verb } from '@metro-labs/core/stations/types';
6
+
7
+ export const webhookStation: Station = {
8
+ name: 'webhook',
9
+ hasAccounts: true,
10
+ hasTrain: false,
11
+ messageVerbs: new Set<Verb>(),
12
+ attachmentMode: 'none',
13
+ tools: [],
14
+ };
15
+
16
+ export function webhookEntry(
17
+ endpoint: Endpoint,
18
+ headers: Record<string, string>,
19
+ body: unknown,
20
+ method: string,
21
+ url: string,
22
+ ): MetroEvent {
23
+ const line = Line.webhook(endpoint.id);
24
+ return {
25
+ id: mintId(),
26
+ ts: new Date().toISOString(),
27
+ station: 'webhook',
28
+ line,
29
+ lineName: endpoint.label,
30
+ from: line,
31
+ to: line,
32
+ messageId:
33
+ headers['x-github-delivery'] ?? headers['x-request-id'] ?? randomUUID(),
34
+ text: `${headers['x-github-event'] ?? headers['x-intercom-topic'] ?? 'event'} ${method} ${url}`,
35
+ payload: { headers, body },
36
+ };
37
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@metro-labs/whatsapp",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/station.ts",
8
+ "./train": "./src/index.ts",
9
+ "./login": "./src/login.ts",
10
+ "./tokens": "./src/token-store.ts"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "test": "tsc --noEmit && bun test test/"
15
+ },
16
+ "dependencies": {
17
+ "@metro-labs/core": "workspace:*",
18
+ "baileys": "7.0.0-rc14"
19
+ },
20
+ "devDependencies": {
21
+ "@stage-labs/config": "0.1.0-beta.2",
22
+ "eslint": "^10.3.0",
23
+ "knip": "^6.15.0",
24
+ "madge": "^8.0.0",
25
+ "typescript": "^5"
26
+ }
27
+ }
@@ -0,0 +1,66 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import {
4
+ makeAccountStore,
5
+ resolveAccountId,
6
+ type Die,
7
+ } from '@metro-labs/core/stations/account-store';
8
+ import type { WhatsAppAccount } from './types.js';
9
+
10
+ const ACCOUNTS_FILE =
11
+ process.env.WHATSAPP_ACCOUNTS_FILE ??
12
+ join(homedir(), '.metro', 'whatsapp-accounts.json');
13
+
14
+ const isJid = (s: string): boolean => s.includes('@');
15
+
16
+ function validateAccount(
17
+ a: WhatsAppAccount,
18
+ seen: Set<string>,
19
+ die: Die,
20
+ ): void {
21
+ if (!a.id) die('account missing id');
22
+ if (!a.phone || typeof a.phone !== 'string')
23
+ die(`account '${a.id}' missing phone`);
24
+ if (seen.has(a.id)) die(`duplicate account id '${a.id}'`);
25
+ seen.add(a.id);
26
+ }
27
+
28
+ export const { loadAccounts } = makeAccountStore<WhatsAppAccount>({
29
+ prefix: 'whatsapp',
30
+ file: ACCOUNTS_FILE,
31
+ validate(raw, die) {
32
+ const seen = new Set<string>();
33
+ for (const a of raw) validateAccount(a, seen, die);
34
+ },
35
+ });
36
+
37
+ export const accounts = new Map<string, WhatsAppAccount>();
38
+
39
+ export function accountFor(args: { account?: string; line?: string }): string {
40
+ return resolveAccountId(accounts, args, (line) => targetOf(line)?.accountId);
41
+ }
42
+
43
+ export function lineOf(accountId: string, jid: string): string {
44
+ return `metro://whatsapp/${accountId}/${jid}`;
45
+ }
46
+
47
+ interface Target {
48
+ accountId: string;
49
+ jid: string;
50
+ }
51
+
52
+ function splitScoped(path: string[]): { accountId: string; jid?: string } {
53
+ const first = path[0];
54
+ if (path.length >= 2 && first !== undefined && !isJid(first))
55
+ return { accountId: first, jid: path[1] };
56
+ return { accountId: 'default', jid: first };
57
+ }
58
+
59
+ export function targetOf(line: string): Target | undefined {
60
+ const prefix = 'metro://whatsapp/';
61
+ if (!line.startsWith(prefix)) return undefined;
62
+ const path = line.slice(prefix.length).split('/').filter(Boolean);
63
+ const { accountId, jid } = splitScoped(path);
64
+ if (jid === undefined || !isJid(jid)) return undefined;
65
+ return { accountId, jid };
66
+ }
@@ -0,0 +1,101 @@
1
+ import { TrainError } from '@metro-labs/core/train-error';
2
+ import type { BinaryNode, WASocket } from 'baileys';
3
+
4
+ const DEFAULT_ACK_WAIT_MS = 5000;
5
+
6
+ export function ackWaitMs(raw: string | undefined): number {
7
+ if (raw === undefined || raw.trim() === '') return DEFAULT_ACK_WAIT_MS;
8
+ const ms = Number(raw);
9
+ return Number.isFinite(ms) && ms >= 0 ? ms : DEFAULT_ACK_WAIT_MS;
10
+ }
11
+
12
+ export const ACK_WAIT_MS = ackWaitMs(process.env.METRO_WHATSAPP_ACK_WAIT_MS);
13
+
14
+ const RECENT_MAX = 256;
15
+
16
+ const RESTRICTION = '463';
17
+
18
+ export interface SendAck {
19
+ messageId: string;
20
+ jid: string;
21
+ error?: string;
22
+ }
23
+
24
+ export function ackOf(node: BinaryNode): SendAck | undefined {
25
+ const { id, from, error } = node.attrs;
26
+ if (!id || !from) return undefined;
27
+ return { messageId: id, jid: from, ...(error ? { error } : {}) };
28
+ }
29
+
30
+ export function rejection(ack: SendAck): TrainError | undefined {
31
+ if (!ack.error) return undefined;
32
+ if (ack.error === RESTRICTION)
33
+ return new TrainError(
34
+ 'whatsapp_account_restricted',
35
+ `WhatsApp refused the message to ${ack.jid} with ack error 463 (MessageAccountRestriction): the send carried no trusted-contact token, so WhatsApp counted it as reaching out to a stranger and this account is under a reach-out timelock. Nothing was delivered, and it must not be retried — every retry counts as another reach-out and deepens the lock.`,
36
+ { retryable: false },
37
+ );
38
+ return new TrainError(
39
+ 'whatsapp_send_refused',
40
+ `WhatsApp refused the message to ${ack.jid} with ack error ${ack.error}, so it was not delivered`,
41
+ { retryable: false },
42
+ );
43
+ }
44
+
45
+ export interface AckWatch {
46
+ record(node: BinaryNode): SendAck | undefined;
47
+ wait(messageId: string, timeoutMs: number): Promise<SendAck | undefined>;
48
+ }
49
+
50
+ export function makeAckWatch(max: number = RECENT_MAX): AckWatch {
51
+ const recent = new Map<string, SendAck>();
52
+ const waiting = new Map<string, (ack: SendAck) => void>();
53
+ return {
54
+ record(node) {
55
+ const ack = ackOf(node);
56
+ if (!ack) return undefined;
57
+ const waiter = waiting.get(ack.messageId);
58
+ if (waiter) {
59
+ waiting.delete(ack.messageId);
60
+ waiter(ack);
61
+ return ack;
62
+ }
63
+ recent.delete(ack.messageId);
64
+ recent.set(ack.messageId, ack);
65
+ while (recent.size > max) {
66
+ const oldest = recent.keys().next().value;
67
+ if (oldest === undefined) break;
68
+ recent.delete(oldest);
69
+ }
70
+ return ack;
71
+ },
72
+ wait(messageId, timeoutMs) {
73
+ const seen = recent.get(messageId);
74
+ if (seen) {
75
+ recent.delete(messageId);
76
+ return Promise.resolve(seen);
77
+ }
78
+ return new Promise<SendAck | undefined>((resolve) => {
79
+ const timer = setTimeout(() => {
80
+ waiting.delete(messageId);
81
+ resolve(undefined);
82
+ }, timeoutMs);
83
+ waiting.set(messageId, (ack) => {
84
+ clearTimeout(timer);
85
+ resolve(ack);
86
+ });
87
+ });
88
+ },
89
+ };
90
+ }
91
+
92
+ export function bindAcks(
93
+ sock: WASocket,
94
+ watch: AckWatch,
95
+ onError: (ack: SendAck) => void,
96
+ ): void {
97
+ sock.ws.on('CB:ack,class:message', (node: BinaryNode) => {
98
+ const ack = watch.record(node);
99
+ if (ack?.error) onError(ack);
100
+ });
101
+ }
@@ -0,0 +1,190 @@
1
+ import { TrainError } from '@metro-labs/core/train-error';
2
+ import { errMsg } from '@metro-labs/core/log';
3
+ import { kindOf } from '@metro-labs/core/stations/attachments';
4
+ import {
5
+ makeStation,
6
+ respond,
7
+ type CallMsg,
8
+ type StationHandler,
9
+ } from '@metro-labs/core/stations/station-runtime';
10
+ import { accountFor, accounts, targetOf } from './accounts.js';
11
+ import { normalizeWhatsApp } from './normalize.js';
12
+ import { phoneNumberOf, senderLookup } from './resolve.js';
13
+ import type { WAClient } from './client.js';
14
+
15
+ type Args = Record<string, unknown>;
16
+
17
+ interface Resolved {
18
+ accountId: string;
19
+ client: WAClient;
20
+ jid: string;
21
+ }
22
+
23
+ type ClientFor = (accountId: string) => WAClient;
24
+
25
+ function str(v: unknown): string | undefined {
26
+ return typeof v === 'string' ? v : undefined;
27
+ }
28
+
29
+ function resolve(args: Args, clientFor: ClientFor): Resolved {
30
+ const line = str(args.line);
31
+ if (!line) throw new TrainError('bad_request', 'missing line');
32
+ const target = targetOf(line);
33
+ if (!target) throw new TrainError('bad_request', `bad line '${line}'`);
34
+ const accountId = accountFor({ account: str(args.account), line });
35
+ return { accountId, client: clientFor(accountId), jid: target.jid };
36
+ }
37
+
38
+ async function guard<T>(run: () => Promise<T>): Promise<T> {
39
+ try {
40
+ return await run();
41
+ } catch (e) {
42
+ if (e instanceof TrainError) throw e;
43
+ const msg = errMsg(e);
44
+ if (/rate.?over.?limit|too many|429/i.test(msg))
45
+ throw new TrainError('rate_limited', msg);
46
+ throw new TrainError('whatsapp_call', msg);
47
+ }
48
+ }
49
+
50
+ interface WireAttachment {
51
+ kind?: string;
52
+ path?: string;
53
+ url?: string;
54
+ mime?: string;
55
+ name?: string;
56
+ }
57
+
58
+ function attachmentsOf(args: Args): WireAttachment[] {
59
+ const raw = args.attachments;
60
+ if (!Array.isArray(raw)) return [];
61
+ return (raw as WireAttachment[]).filter((a) => Boolean(a.path ?? a.url));
62
+ }
63
+
64
+ async function sendMediaSet(
65
+ client: WAClient,
66
+ jid: string,
67
+ atts: WireAttachment[],
68
+ text: string,
69
+ replyTo: string | undefined,
70
+ ): Promise<{ messageId: string; labels: string[] }> {
71
+ const labels: string[] = [];
72
+ let messageId = '';
73
+ for (let i = 0; i < atts.length; i++) {
74
+ const att = atts[i];
75
+ if (!att) continue;
76
+ const path = att.path ?? att.url ?? '';
77
+ const kind = att.kind ?? kindOf(att.mime ?? '', path);
78
+ messageId = await guard(() =>
79
+ client.sendMedia(
80
+ jid,
81
+ {
82
+ kind,
83
+ path,
84
+ mime: att.mime ?? 'application/octet-stream',
85
+ name: att.name ?? path.split('/').pop() ?? 'attachment',
86
+ ...(i === 0 && text ? { caption: text } : {}),
87
+ },
88
+ replyTo,
89
+ ),
90
+ );
91
+ labels.push(kind);
92
+ }
93
+ return { messageId, labels };
94
+ }
95
+
96
+ function makeSend(clientFor: ClientFor): StationHandler {
97
+ return async (id, args) => {
98
+ const { accountId, client, jid } = resolve(args, clientFor);
99
+ const text = str(args.text) ?? '';
100
+ const replyTo = str(args.replyTo);
101
+ const atts = attachmentsOf(args);
102
+ if (atts.length) {
103
+ const { messageId, labels } = await sendMediaSet(
104
+ client,
105
+ jid,
106
+ atts,
107
+ text,
108
+ replyTo,
109
+ );
110
+ respond(id, {
111
+ result: { messageId, account: accountId, attachments: labels },
112
+ });
113
+ return;
114
+ }
115
+ const messageId = await guard(() => client.sendText(jid, text, replyTo));
116
+ respond(id, { result: { messageId, account: accountId } });
117
+ };
118
+ }
119
+
120
+ function makeReact(clientFor: ClientFor): StationHandler {
121
+ return async (id, args) => {
122
+ const { accountId, client, jid } = resolve(args, clientFor);
123
+ const messageId = str(args.messageId) ?? '';
124
+ const emoji = str(args.emoji) ?? '';
125
+ await guard(() => client.sendReaction(jid, messageId, emoji));
126
+ respond(id, { result: { ok: true, account: accountId } });
127
+ };
128
+ }
129
+
130
+ function makeEdit(clientFor: ClientFor): StationHandler {
131
+ return async (id, args) => {
132
+ const { accountId, client, jid } = resolve(args, clientFor);
133
+ const messageId = str(args.messageId) ?? '';
134
+ const text = str(args.text) ?? '';
135
+ await guard(() => client.editMessage(jid, messageId, text));
136
+ respond(id, { result: { ok: true, account: accountId } });
137
+ };
138
+ }
139
+
140
+ function makeDelete(clientFor: ClientFor): StationHandler {
141
+ return async (id, args) => {
142
+ const { accountId, client, jid } = resolve(args, clientFor);
143
+ const messageId = str(args.messageId) ?? '';
144
+ await guard(() => client.deleteMessage(jid, messageId));
145
+ respond(id, { result: { ok: true, account: accountId } });
146
+ };
147
+ }
148
+
149
+ function makeAccounts(clientFor: ClientFor): StationHandler {
150
+ return (id) => {
151
+ const list = [...accounts.values()].map((a) => {
152
+ const phone = clientFor(a.id).self();
153
+ return {
154
+ id: a.id,
155
+ handle: phone === null ? null : `+${phone}`,
156
+ url: phone === null ? null : `https://wa.me/${phone}`,
157
+ connected: phone !== null,
158
+ owner: a.owner ?? null,
159
+ };
160
+ });
161
+ respond(id, { result: { accounts: list } });
162
+ return Promise.resolve();
163
+ };
164
+ }
165
+
166
+ function makeResolveSender(clientFor: ClientFor): StationHandler {
167
+ return async (id, args) => {
168
+ const accountId = accountFor({ account: str(args.account) });
169
+ const query = str(args.query) ?? '';
170
+ const number = phoneNumberOf(query);
171
+ const found = await guard(() => clientFor(accountId).lookupSender(number));
172
+ respond(id, { result: { account: accountId, ...senderLookup(query, number, found) } });
173
+ };
174
+ }
175
+
176
+ export function makeHandleCall(
177
+ clientFor: ClientFor,
178
+ ): (msg: CallMsg) => Promise<void> {
179
+ return makeStation({
180
+ handlers: {
181
+ accounts: makeAccounts(clientFor),
182
+ send: makeSend(clientFor),
183
+ react: makeReact(clientFor),
184
+ edit: makeEdit(clientFor),
185
+ delete: makeDelete(clientFor),
186
+ resolve_sender: makeResolveSender(clientFor),
187
+ },
188
+ normalize: normalizeWhatsApp,
189
+ });
190
+ }
@@ -0,0 +1,72 @@
1
+ import { downloadMediaMessage, type WAMessage } from 'baileys';
2
+ import {
3
+ assertAttachmentSize,
4
+ saveStreamToCache,
5
+ type SavedAttachment,
6
+ } from '@metro-labs/core/stations/attachments';
7
+ import { TrainError } from '@metro-labs/core/train-error';
8
+ import { baileysLogger } from './logger.js';
9
+ import type { WAMediaRef } from './media.js';
10
+
11
+ export type { SavedAttachment };
12
+
13
+ const IDLE_TIMEOUT_MS = 60_000;
14
+
15
+ export type ReuploadRequest = (m: WAMessage) => Promise<WAMessage>;
16
+
17
+ export async function* withIdleTimeout(
18
+ chunks: AsyncIterable<Uint8Array>,
19
+ ms: number,
20
+ ): AsyncGenerator<Uint8Array> {
21
+ const iterator = chunks[Symbol.asyncIterator]();
22
+ for (;;) {
23
+ let timer: ReturnType<typeof setTimeout> | undefined;
24
+ const idle = new Promise<never>((_resolve, reject) => {
25
+ timer = setTimeout(() => {
26
+ reject(
27
+ new TrainError(
28
+ 'whatsapp_media_stalled',
29
+ `WhatsApp sent no media bytes for ${ms}ms, so the download was abandoned`,
30
+ ),
31
+ );
32
+ }, ms);
33
+ });
34
+ try {
35
+ const next = await Promise.race([iterator.next(), idle]);
36
+ if (next.done === true) return;
37
+ yield next.value;
38
+ } finally {
39
+ clearTimeout(timer);
40
+ }
41
+ }
42
+ }
43
+
44
+ export async function saveWhatsAppMedia(
45
+ accountId: string,
46
+ raw: WAMessage,
47
+ ref: WAMediaRef,
48
+ messageId: string,
49
+ index = 0,
50
+ reupload?: ReuploadRequest,
51
+ ): Promise<SavedAttachment> {
52
+ if (ref.bytes !== undefined) assertAttachmentSize(ref.bytes);
53
+ const stream = await downloadMediaMessage(
54
+ raw,
55
+ 'stream',
56
+ {},
57
+ reupload
58
+ ? { reuploadRequest: reupload, logger: baileysLogger(accountId) }
59
+ : undefined,
60
+ );
61
+ try {
62
+ return await saveStreamToCache(
63
+ withIdleTimeout(stream, IDLE_TIMEOUT_MS),
64
+ messageId,
65
+ index,
66
+ { mime: ref.mime, name: ref.name },
67
+ );
68
+ } catch (err) {
69
+ stream.destroy();
70
+ throw err;
71
+ }
72
+ }