@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,166 @@
1
+ import { TelegramClient } from '@mtcute/bun';
2
+
3
+ export class TelegramLoginError extends Error {}
4
+
5
+ export interface TelegramCredentials {
6
+ apiId: number;
7
+ apiHash: string;
8
+ phone: string;
9
+ }
10
+
11
+ export interface TelegramSignedIn {
12
+ config: { session: string; apiId: number; apiHash: string };
13
+ identity: { userId: string; displayName: string };
14
+ }
15
+
16
+ export type TelegramStep = 'code' | 'password';
17
+
18
+ const PHONE_RE = /^[0-9]{6,15}$/;
19
+ const API_HASH_RE = /^[a-f0-9]{32}$/i;
20
+
21
+ export function rpcErrorText(err: unknown): string {
22
+ if (typeof err !== 'object' || err === null) return '';
23
+ const text = (err as { text?: unknown }).text;
24
+ return typeof text === 'string' ? text : '';
25
+ }
26
+
27
+ export function needsPassword(err: unknown): boolean {
28
+ return rpcErrorText(err) === 'SESSION_PASSWORD_NEEDED';
29
+ }
30
+
31
+ export function readableAuthError(err: unknown): string {
32
+ const text = rpcErrorText(err);
33
+ if (text.startsWith('PHONE_CODE_INVALID')) return 'that login code is not right';
34
+ if (text.startsWith('PHONE_CODE_EXPIRED'))
35
+ return 'that login code has expired, start again';
36
+ if (text.startsWith('PHONE_NUMBER_INVALID'))
37
+ return 'Telegram does not recognise that phone number';
38
+ if (text.startsWith('PHONE_NUMBER_BANNED'))
39
+ return 'Telegram has banned that phone number';
40
+ if (text.startsWith('PASSWORD_HASH_INVALID'))
41
+ return 'that two-step verification password is not right';
42
+ if (text.startsWith('FLOOD_WAIT'))
43
+ return 'Telegram is rate-limiting this number, try again later';
44
+ return text === '' ? 'Telegram refused the sign-in' : `Telegram said ${text}`;
45
+ }
46
+
47
+ export function validateCredentials(raw: {
48
+ apiId: unknown;
49
+ apiHash: unknown;
50
+ phone: unknown;
51
+ }): TelegramCredentials {
52
+ const apiId = Number(raw.apiId);
53
+ if (!Number.isInteger(apiId) || apiId <= 0)
54
+ throw new TelegramLoginError(
55
+ 'api id must be the integer from my.telegram.org',
56
+ );
57
+ const apiHash = typeof raw.apiHash === 'string' ? raw.apiHash.trim() : '';
58
+ if (!API_HASH_RE.test(apiHash))
59
+ throw new TelegramLoginError(
60
+ 'api hash must be the 32-character hash from my.telegram.org',
61
+ );
62
+ const phone =
63
+ typeof raw.phone === 'string' ? raw.phone.replace(/[^0-9]/g, '') : '';
64
+ if (!PHONE_RE.test(phone))
65
+ throw new TelegramLoginError(
66
+ 'phone must be an international number, digits only',
67
+ );
68
+ return { apiId, apiHash, phone };
69
+ }
70
+
71
+ interface SentCodeLike {
72
+ phoneCodeHash: string;
73
+ length: number;
74
+ type: string;
75
+ }
76
+
77
+ const isSentCode = (v: unknown): v is SentCodeLike =>
78
+ typeof v === 'object' &&
79
+ v !== null &&
80
+ typeof (v as { phoneCodeHash?: unknown }).phoneCodeHash === 'string';
81
+
82
+ interface UserLike {
83
+ id: number;
84
+ displayName: string;
85
+ }
86
+
87
+ export class TelegramLogin {
88
+ private tg: TelegramClient;
89
+ private phoneCodeHash = '';
90
+ private closed = false;
91
+
92
+ constructor(private creds: TelegramCredentials) {
93
+ this.tg = new TelegramClient({
94
+ apiId: creds.apiId,
95
+ apiHash: creds.apiHash,
96
+ storage: ':memory:',
97
+ });
98
+ }
99
+
100
+ async requestCode(): Promise<{ step: TelegramStep; length: number }> {
101
+ await this.tg.connect();
102
+ const sent = await this.tg
103
+ .sendCode({ phone: this.creds.phone })
104
+ .catch((err: unknown) => {
105
+ throw new TelegramLoginError(readableAuthError(err));
106
+ });
107
+ if (!isSentCode(sent))
108
+ throw new TelegramLoginError(
109
+ 'Telegram signed this number in without a code, which Metro cannot use',
110
+ );
111
+ this.phoneCodeHash = sent.phoneCodeHash;
112
+ return { step: 'code', length: sent.length };
113
+ }
114
+
115
+ async submitCode(raw: string): Promise<TelegramSignedIn | 'password'> {
116
+ const phoneCode = raw.replace(/[^0-9]/g, '');
117
+ if (phoneCode === '')
118
+ throw new TelegramLoginError('enter the login code Telegram sent you');
119
+ try {
120
+ const user = await this.tg.signIn({
121
+ phone: this.creds.phone,
122
+ phoneCodeHash: this.phoneCodeHash,
123
+ phoneCode,
124
+ });
125
+ return await this.exportFor(user);
126
+ } catch (err) {
127
+ if (needsPassword(err)) return 'password';
128
+ throw new TelegramLoginError(readableAuthError(err));
129
+ }
130
+ }
131
+
132
+ async submitPassword(password: string): Promise<TelegramSignedIn> {
133
+ if (password === '')
134
+ throw new TelegramLoginError(
135
+ 'enter your Telegram two-step verification password',
136
+ );
137
+ try {
138
+ const user = await this.tg.checkPassword(password);
139
+ return await this.exportFor(user);
140
+ } catch (err) {
141
+ throw new TelegramLoginError(readableAuthError(err));
142
+ }
143
+ }
144
+
145
+ private async exportFor(user: UserLike): Promise<TelegramSignedIn> {
146
+ const session = await this.tg.exportSession();
147
+ await this.close();
148
+ return {
149
+ config: {
150
+ session,
151
+ apiId: this.creds.apiId,
152
+ apiHash: this.creds.apiHash,
153
+ },
154
+ identity: {
155
+ userId: String(user.id),
156
+ displayName: user.displayName,
157
+ },
158
+ };
159
+ }
160
+
161
+ async close(): Promise<void> {
162
+ if (this.closed) return;
163
+ this.closed = true;
164
+ await this.tg.destroy().catch(() => undefined);
165
+ }
166
+ }
@@ -0,0 +1,80 @@
1
+ import type { InputMediaLike, Message } from '@mtcute/bun';
2
+ import { InputMedia } from '@mtcute/bun';
3
+ import { isImageMime, isImageExt } from '@metro-labs/core/stations/attachments';
4
+ import type { UserClient } from './client.js';
5
+
6
+ export interface CanonicalAttachment {
7
+ kind?: string;
8
+ path?: string;
9
+ url?: string;
10
+ mime?: string;
11
+ name?: string;
12
+ }
13
+
14
+ const srcOf = (att: CanonicalAttachment): string => att.path ?? att.url ?? '';
15
+
16
+ function isImage(att: CanonicalAttachment): boolean {
17
+ if (att.kind === 'image') return true;
18
+ if (att.mime !== undefined) return isImageMime(att.mime);
19
+ const src = srcOf(att);
20
+ return src !== '' && isImageExt(src);
21
+ }
22
+
23
+ function fileRef(att: CanonicalAttachment): string {
24
+ const src = srcOf(att);
25
+ if (src === '' || /^https?:\/\//i.test(src)) return src;
26
+ return src.startsWith('file:') ? src : `file:${src}`;
27
+ }
28
+
29
+ export interface OutgoingMedia {
30
+ media: InputMediaLike;
31
+ kind: string;
32
+ }
33
+
34
+ export function buildInputMedia(
35
+ att: CanonicalAttachment,
36
+ caption: string | undefined,
37
+ ): OutgoingMedia {
38
+ const file = fileRef(att);
39
+ const params = {
40
+ ...(caption ? { caption } : {}),
41
+ ...(att.name ? { fileName: att.name } : {}),
42
+ };
43
+ return isImage(att)
44
+ ? { media: InputMedia.photo(file, params), kind: 'image' }
45
+ : { media: InputMedia.document(file, params), kind: 'file' };
46
+ }
47
+
48
+ interface SendMediaTarget {
49
+ client: UserClient;
50
+ chatId: number;
51
+ replyTo?: number;
52
+ }
53
+
54
+ export interface SentMedia {
55
+ message: Message;
56
+ delivered: string[];
57
+ }
58
+
59
+ export async function sendAttachments(
60
+ target: SendMediaTarget,
61
+ attachments: CanonicalAttachment[],
62
+ text: string,
63
+ ): Promise<SentMedia> {
64
+ const { client, chatId, replyTo } = target;
65
+ const peer = await client.tg.resolvePeer(chatId);
66
+ let last: Message | undefined;
67
+ const delivered: string[] = [];
68
+ for (let i = 0; i < attachments.length; i++) {
69
+ const att = attachments[i];
70
+ if (!att || srcOf(att) === '') continue;
71
+ const caption = i === 0 ? text : undefined;
72
+ const out = buildInputMedia(att, caption);
73
+ last = await client.tg.sendMedia(peer, out.media, {
74
+ ...(replyTo !== undefined ? { replyTo } : {}),
75
+ });
76
+ delivered.push(out.kind);
77
+ }
78
+ if (!last) throw new Error('no attachments were sent');
79
+ return { message: last, delivered };
80
+ }
@@ -0,0 +1,75 @@
1
+ import type { MemberList, MetroMember } from '@metro-labs/core/stations/types';
2
+ import type { UserClient } from './client.js';
3
+
4
+ export interface RawUserMember {
5
+ user: {
6
+ id: number;
7
+ username?: string | null;
8
+ displayName?: string;
9
+ isBot?: boolean;
10
+ };
11
+ status: string;
12
+ title?: string | null;
13
+ }
14
+
15
+ const PAGE = 200;
16
+ const HARD_CAP = 2000;
17
+
18
+ export function mapUserMember(m: RawUserMember): MetroMember {
19
+ const u = m.user;
20
+ const member: MetroMember = { id: String(u.id) };
21
+ if (u.username) member.name = u.username;
22
+ if (u.displayName) member.display_name = u.displayName;
23
+ if (m.title) member.roles = [m.title];
24
+ member.is_admin = m.status === 'creator' || m.status === 'admin';
25
+ member.is_bot = Boolean(u.isBot);
26
+ return member;
27
+ }
28
+
29
+ export function restrictedMemberList(reason: string): MemberList {
30
+ return { members: [], capability: { supported: false, complete: false, reason } };
31
+ }
32
+
33
+ const RESTRICTED_RE =
34
+ /ADMIN|PRIVATE|CHAT_ADMIN_REQUIRED|not (a )?participant|permission|not enough rights/i;
35
+
36
+ export function isRestricted(msg: string): boolean {
37
+ return RESTRICTED_RE.test(msg);
38
+ }
39
+
40
+ type Chunk = RawUserMember[] & { total?: number };
41
+
42
+ export async function fetchMembers(
43
+ client: UserClient,
44
+ chatId: number,
45
+ limit?: number,
46
+ ): Promise<MemberList> {
47
+ const target = Math.min(Math.max(1, limit ?? PAGE), HARD_CAP);
48
+ const collected: RawUserMember[] = [];
49
+ let total = 0;
50
+ for (let offset = 0; collected.length < target; ) {
51
+ const want = Math.min(PAGE, target - collected.length);
52
+ const getChatMembers = (
53
+ client.tg as unknown as {
54
+ getChatMembers: (
55
+ id: number,
56
+ p: { offset: number; limit: number },
57
+ ) => Promise<Chunk>;
58
+ }
59
+ ).getChatMembers;
60
+ const chunk = await getChatMembers.call(client.tg, chatId, {
61
+ offset,
62
+ limit: want,
63
+ });
64
+ total = chunk.total ?? collected.length + chunk.length;
65
+ if (!chunk.length) break;
66
+ collected.push(...chunk);
67
+ offset += chunk.length;
68
+ if (collected.length >= total || chunk.length < want) break;
69
+ }
70
+ const members = collected.map(mapUserMember);
71
+ return {
72
+ members,
73
+ capability: { supported: true, complete: members.length >= total, total },
74
+ };
75
+ }
@@ -0,0 +1,22 @@
1
+ import type { Normalized } from '@metro-labs/core/stations/messaging-normalize';
2
+
3
+ type Args = Record<string, unknown>;
4
+
5
+ export function normalizeTelegramUser(action: string, env: Args): Normalized {
6
+ if (action === 'reply') {
7
+ return {
8
+ action: 'send',
9
+ args: {
10
+ line: env.line,
11
+ text: env.text,
12
+ replyTo: env.replyTo,
13
+ account: env.account,
14
+ attachments: env.attachments,
15
+ },
16
+ };
17
+ }
18
+ if (action === 'unreact') {
19
+ return { action: 'react', args: { ...env, emoji: '' } };
20
+ }
21
+ return { action, args: env };
22
+ }
@@ -0,0 +1,167 @@
1
+ import type { tl } from '@mtcute/bun';
2
+ import { getBarePeerId, getMarkedPeerId } from '@mtcute/bun';
3
+ import { errMsg } from '@metro-labs/core/log';
4
+ import { emit } from './wire.js';
5
+ import { reactionEnvelope } from './format.js';
6
+ import type { UserClient } from './client.js';
7
+
8
+ interface SeenReaction {
9
+ reactorId: number;
10
+ emoji: string;
11
+ }
12
+
13
+ type CountMap = Map<string, number>;
14
+
15
+ interface SeenState {
16
+ recent: SeenReaction[];
17
+ counts: CountMap;
18
+ }
19
+
20
+ const SEEN_BY_KEY_MAX = 5000;
21
+ const seenByKey = new Map<string, SeenState>();
22
+
23
+ function emojiOf(reaction: tl.TypeReaction): string | undefined {
24
+ if (reaction._ === 'reactionEmoji') return reaction.emoticon;
25
+ if (reaction._ === 'reactionCustomEmoji')
26
+ return `custom:${String(reaction.documentId)}`;
27
+ if (reaction._ === 'reactionPaid') return '⭐';
28
+ return undefined;
29
+ }
30
+
31
+ function recentOf(reactions: tl.TypeMessageReactions): SeenReaction[] {
32
+ const recent = reactions.recentReactions ?? [];
33
+ const out: SeenReaction[] = [];
34
+ for (const r of recent) {
35
+ if (r.my === true) continue;
36
+ const emoji = emojiOf(r.reaction);
37
+ if (emoji === undefined) continue;
38
+ out.push({ reactorId: getBarePeerId(r.peerId), emoji });
39
+ }
40
+ return out;
41
+ }
42
+
43
+ function countsOf(reactions: tl.TypeMessageReactions): CountMap {
44
+ const out: CountMap = new Map();
45
+ for (const r of reactions.results) {
46
+ const emoji = emojiOf(r.reaction);
47
+ if (emoji === undefined) continue;
48
+ const count = r.chosenOrder === undefined ? r.count : r.count - 1;
49
+ if (count > 0) out.set(emoji, count);
50
+ }
51
+ return out;
52
+ }
53
+
54
+ function has(list: SeenReaction[], item: SeenReaction): boolean {
55
+ return list.some(
56
+ (r) => r.reactorId === item.reactorId && r.emoji === item.emoji,
57
+ );
58
+ }
59
+
60
+ interface DiffResult {
61
+ added: SeenReaction[];
62
+ removed: SeenReaction[];
63
+ }
64
+
65
+ function diffRecent(prev: SeenReaction[], next: SeenReaction[]): DiffResult {
66
+ return {
67
+ added: next.filter((n) => !has(prev, n)),
68
+ removed: prev.filter((p) => !has(next, p)),
69
+ };
70
+ }
71
+
72
+ function diffCounts(
73
+ prev: CountMap,
74
+ next: CountMap,
75
+ reactorId: number,
76
+ ): DiffResult {
77
+ const added: SeenReaction[] = [];
78
+ const removed: SeenReaction[] = [];
79
+ const emojis = new Set([...prev.keys(), ...next.keys()]);
80
+ for (const emoji of emojis) {
81
+ const before = prev.get(emoji) ?? 0;
82
+ const after = next.get(emoji) ?? 0;
83
+ if (after > before) added.push({ reactorId, emoji });
84
+ else if (after < before) removed.push({ reactorId, emoji });
85
+ }
86
+ return { added, removed };
87
+ }
88
+
89
+ interface ReactionContext {
90
+ accountId: string;
91
+ chatId: number;
92
+ messageId: number;
93
+ isPrivate: boolean;
94
+ }
95
+
96
+ function emitDiff(ctx: ReactionContext, change: DiffResult): void {
97
+ const base = {
98
+ accountId: ctx.accountId,
99
+ chatId: ctx.chatId,
100
+ messageId: ctx.messageId,
101
+ date: new Date(),
102
+ isPrivate: ctx.isPrivate,
103
+ };
104
+ for (const a of change.added)
105
+ emit(reactionEnvelope({ ...base, emoji: a.emoji, senderId: a.reactorId }));
106
+ for (const r of change.removed)
107
+ emit(
108
+ reactionEnvelope({
109
+ ...base,
110
+ emoji: r.emoji,
111
+ senderId: r.reactorId,
112
+ removed: true,
113
+ }),
114
+ );
115
+ }
116
+
117
+ function computeChange(
118
+ update: tl.RawUpdateMessageReactions,
119
+ prev: SeenState,
120
+ next: SeenState,
121
+ isPrivate: boolean,
122
+ ): DiffResult {
123
+ if (next.recent.length > 0 || prev.recent.length > 0)
124
+ return diffRecent(prev.recent, next.recent);
125
+ if (!isPrivate) return { added: [], removed: [] };
126
+ return diffCounts(prev.counts, next.counts, getBarePeerId(update.peer));
127
+ }
128
+
129
+ function handleUpdate(
130
+ client: UserClient,
131
+ update: tl.RawUpdateMessageReactions,
132
+ ): void {
133
+ const chatId = getMarkedPeerId(update.peer);
134
+ const isPrivate = update.peer._ === 'peerUser';
135
+ const key = `${chatId}:${update.msgId}`;
136
+ const next: SeenState = {
137
+ recent: recentOf(update.reactions),
138
+ counts: countsOf(update.reactions),
139
+ };
140
+ const prev = seenByKey.get(key) ?? { recent: [], counts: new Map() };
141
+ const change = computeChange(update, prev, next, isPrivate);
142
+ seenByKey.set(key, next);
143
+ if (seenByKey.size > SEEN_BY_KEY_MAX) {
144
+ const oldest = seenByKey.keys().next().value;
145
+ if (oldest !== undefined) seenByKey.delete(oldest);
146
+ }
147
+ if (change.added.length === 0 && change.removed.length === 0) return;
148
+ emitDiff(
149
+ { accountId: client.account.id, chatId, messageId: update.msgId, isPrivate },
150
+ change,
151
+ );
152
+ }
153
+
154
+ export function subscribeReactions(client: UserClient): void {
155
+ const accountId = client.account.id;
156
+ client.tg.onRawUpdate.add((info) => {
157
+ const { update } = info;
158
+ if (update._ !== 'updateMessageReactions') return;
159
+ try {
160
+ handleUpdate(client, update);
161
+ } catch (e) {
162
+ process.stderr.write(
163
+ `telegram[${accountId}] reaction normalize failed: ${errMsg(e)}\n`,
164
+ );
165
+ }
166
+ });
167
+ }
@@ -0,0 +1,24 @@
1
+ import type { GroupOp, Station, Verb } from '@metro-labs/core/stations/types';
2
+
3
+ export const telegramStation: Station = {
4
+ name: 'telegram',
5
+ hasAccounts: true,
6
+ hasTrain: true,
7
+ messageVerbs: new Set<Verb>([
8
+ 'send',
9
+ 'reply',
10
+ 'react',
11
+ 'unreact',
12
+ 'edit',
13
+ 'delete',
14
+ 'read',
15
+ ]),
16
+ groupOps: new Set<GroupOp>([
17
+ 'create_group',
18
+ 'add_members',
19
+ 'remove_members',
20
+ 'invite_link',
21
+ ]),
22
+ attachmentMode: 'canonical',
23
+ tools: [],
24
+ };
@@ -0,0 +1,7 @@
1
+ export interface UserAccount {
2
+ id: string;
3
+ session: string;
4
+ apiId?: number;
5
+ apiHash?: string;
6
+ owner?: string;
7
+ }
@@ -0,0 +1 @@
1
+ export { emit, mintId } from '@metro-labs/core/stations/station-runtime';
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@metro-labs/telegram-bot",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/station.ts",
8
+ "./train": "./src/index.ts",
9
+ "./verify": "./src/verify.ts"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "test": "tsc --noEmit && bun test test/"
14
+ },
15
+ "dependencies": {
16
+ "@metro-labs/core": "workspace:*"
17
+ },
18
+ "devDependencies": {
19
+ "@stage-labs/config": "0.1.0-beta.2",
20
+ "eslint": "^10.3.0",
21
+ "knip": "^6.15.0",
22
+ "madge": "^8.0.0",
23
+ "typescript": "^5"
24
+ }
25
+ }
@@ -0,0 +1,120 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import {
4
+ makeAccountStore,
5
+ resolveAccountId,
6
+ } from '@metro-labs/core/stations/account-store';
7
+ import { Line } from '@metro-labs/core/lines';
8
+
9
+ const ACCOUNTS_FILE =
10
+ process.env.TELEGRAM_BOT_ACCOUNTS_FILE ??
11
+ join(homedir(), '.metro', 'telegram-bot-accounts.json');
12
+
13
+ export interface AccountConfig {
14
+ id: string;
15
+ token: string;
16
+ owner?: string;
17
+ }
18
+
19
+ export const { loadAccounts } = makeAccountStore<AccountConfig>({
20
+ prefix: 'telegram-bot',
21
+ file: ACCOUNTS_FILE,
22
+ validate(raw, die) {
23
+ const seenId = new Set<string>();
24
+ const seenTok = new Set<string>();
25
+ for (const a of raw) {
26
+ if (!a.id) die('account missing id');
27
+ if (!a.token || typeof a.token !== 'string')
28
+ die(`account '${a.id}' missing token`);
29
+ if (seenId.has(a.id)) die(`duplicate account id '${a.id}'`);
30
+ if (seenTok.has(a.token))
31
+ die(
32
+ `account '${a.id}' reuses a token used by another account (409 on getUpdates)`,
33
+ );
34
+ seenId.add(a.id);
35
+ seenTok.add(a.token);
36
+ }
37
+ },
38
+ });
39
+
40
+ export interface Account {
41
+ cfg: AccountConfig;
42
+ api: string;
43
+ fileApi: string;
44
+ offset: number;
45
+ username?: string;
46
+ }
47
+ export const accounts = new Map<string, Account>();
48
+
49
+ export async function tg<T>(
50
+ accountId: string,
51
+ method: string,
52
+ body: unknown,
53
+ timeoutMs = 30_000,
54
+ ): Promise<T> {
55
+ const acct = accounts.get(accountId);
56
+ if (!acct) throw new Error(`unknown account '${accountId}'`);
57
+ const res = await fetch(`${acct.api}/${method}`, {
58
+ method: 'POST',
59
+ headers: { 'Content-Type': 'application/json' },
60
+ body: JSON.stringify(body),
61
+ signal: AbortSignal.timeout(timeoutMs),
62
+ });
63
+ const json = (await res.json()) as {
64
+ ok: boolean;
65
+ description?: string;
66
+ result?: T;
67
+ };
68
+ if (!json.ok)
69
+ throw new Error(`telegram-bot ${method}: ${json.description ?? 'unknown'}`);
70
+ return json.result as T;
71
+ }
72
+
73
+ export async function tgForm<T>(
74
+ accountId: string,
75
+ method: string,
76
+ form: FormData,
77
+ timeoutMs = 60_000,
78
+ ): Promise<T> {
79
+ const acct = accounts.get(accountId);
80
+ if (!acct) throw new Error(`unknown account '${accountId}'`);
81
+ const res = await fetch(`${acct.api}/${method}`, {
82
+ method: 'POST',
83
+ body: form,
84
+ signal: AbortSignal.timeout(timeoutMs),
85
+ });
86
+ const json = (await res.json()) as {
87
+ ok: boolean;
88
+ description?: string;
89
+ result?: T;
90
+ };
91
+ if (!json.ok)
92
+ throw new Error(`telegram-bot ${method}: ${json.description ?? 'unknown'}`);
93
+ return json.result as T;
94
+ }
95
+
96
+ export function accountFor(args: { account?: string; line?: string }): string {
97
+ return resolveAccountId(
98
+ accounts,
99
+ args,
100
+ (line) => Line.parseTelegram(line)?.accountId,
101
+ );
102
+ }
103
+
104
+ export function lineOf(
105
+ accountId: string,
106
+ chatId: number | string,
107
+ topicId?: number,
108
+ ): string {
109
+ const tail = topicId !== undefined ? `${chatId}/${topicId}` : `${chatId}`;
110
+ return `metro://telegram-bot/${accountId}/${tail}`;
111
+ }
112
+
113
+ export function targetOf(
114
+ line: string,
115
+ accountOverride?: string,
116
+ ): { accountId: string; chatId: number; topicId?: number } {
117
+ const parsed = Line.parseTelegram(line);
118
+ if (!parsed) throw new Error(`bad telegram-bot line: ${line}`);
119
+ return { ...parsed, accountId: accountOverride ?? parsed.accountId };
120
+ }