@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
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "@stage-labs/metro",
3
- "version": "0.1.0-beta.13",
4
- "private": false,
5
- "description": "Live JSON stream of Telegram + Discord messages for your local Claude Code / Codex session. The user launches metro; metro emits inbounds on stdout and accepts replies via CLI subcommands.",
3
+ "version": "0.1.0-beta.131",
4
+ "description": "The metro command line. Sign in once per machine, then hand your MCP connector list to Claude Code without the credentials touching disk, argv or shell history.",
6
5
  "license": "MIT",
7
6
  "repository": {
8
7
  "type": "git",
@@ -15,38 +14,26 @@
15
14
  "node": ">=22"
16
15
  },
17
16
  "bin": {
18
- "metro": "./dist/cli/index.js"
17
+ "metro": "dist/cli.js"
19
18
  },
20
19
  "files": [
21
20
  "dist",
22
- "docs",
23
- "skills",
24
- "README.md",
25
- "LICENSE"
21
+ "runtime"
26
22
  ],
27
23
  "publishConfig": {
28
- "access": "public"
24
+ "access": "public",
25
+ "tag": "beta"
29
26
  },
30
27
  "scripts": {
31
- "build": "tsc",
32
- "prepublishOnly": "tsc",
33
- "lint": "eslint src/",
34
- "lint:fix": "eslint src/ --fix",
35
- "typecheck": "tsc --noEmit",
36
- "test": "METRO_STATE_DIR=\"$(mktemp -d /tmp/metro-test.XXXXXX)\" bun test test/"
37
- },
38
- "dependencies": {
39
- "discord.js": "^14.14.0",
40
- "pino": "^9.5.0",
41
- "pino-pretty": "^13.1.3",
42
- "ws": "^8.20.0"
28
+ "build": "tsc && node scripts/stage-runtime.mjs",
29
+ "prepublishOnly": "tsc && node scripts/stage-runtime.mjs",
30
+ "test": "tsc --noEmit && node scripts/stage-runtime.mjs && bun test test/"
43
31
  },
44
32
  "devDependencies": {
45
- "@types/node": "^22.10.0",
46
- "@types/ws": "^8.18.1",
33
+ "@stage-labs/config": "0.1.0-beta.2",
47
34
  "eslint": "^10.3.0",
48
- "typescript": "^5",
49
- "typescript-eslint": "^8.59.2"
50
- },
51
- "packageManager": "bun@1.3.9"
35
+ "knip": "^6.15.0",
36
+ "madge": "^8.0.0",
37
+ "typescript": "^5"
38
+ }
52
39
  }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "metro",
3
+ "description": "Metro โ€” connector relay and chat bridge for agents",
4
+ "owner": { "name": "Bonustrack", "url": "https://metro.box" },
5
+ "plugins": [
6
+ {
7
+ "name": "metro",
8
+ "source": "./plugin",
9
+ "description": "Sign in as one metro agent and use the connectors it holds as MCP servers through metro's relay, credentials injected server-side."
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "metro",
3
+ "version": "0.1.0-beta.131",
4
+ "description": "Metro for Claude Code, installed by the metro daemon on this machine: every connector the agent holds as its own MCP server relayed through the daemon, the orchestrator-only guard on the main thread, the standing rules loaded at each session start, and a refresh skill for when the daemon could not keep the server list current. No vendor credential ever sits in a config file.",
5
+ "author": {
6
+ "name": "Bonustrack",
7
+ "url": "https://metro.box"
8
+ },
9
+ "repository": "https://github.com/bonustrack/metro",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "metro",
13
+ "mcp",
14
+ "connectors",
15
+ "relay"
16
+ ],
17
+ "skills": "./skills/"
18
+ }
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+
4
+ const BLOCKING = new Set(['AskUserQuestion', 'ExitPlanMode', 'EnterPlanMode']);
5
+ const ORCHESTRATION = new Set([
6
+ 'Agent',
7
+ 'Workflow',
8
+ 'ToolSearch',
9
+ 'Skill',
10
+ 'ScheduleWakeup',
11
+ 'Monitor',
12
+ 'CronCreate',
13
+ 'CronList',
14
+ 'CronDelete',
15
+ 'TaskCreate',
16
+ 'TaskGet',
17
+ 'TaskList',
18
+ 'TaskOutput',
19
+ 'TaskStop',
20
+ 'TaskUpdate',
21
+ ]);
22
+ const IMAGE = /\.(png|jpe?g|gif|webp|bmp)$/i;
23
+ const MCP = /^mcp__/;
24
+ const METRO_MCP = /^mcp__metro__/;
25
+
26
+ function deny(reason) {
27
+ process.stdout.write(
28
+ `${JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason } })}\n`,
29
+ );
30
+ }
31
+
32
+ function verdict(payload) {
33
+ const tool = typeof payload.tool_name === 'string' ? payload.tool_name : '';
34
+ const input = payload.tool_input !== null && typeof payload.tool_input === 'object' ? payload.tool_input : {};
35
+ if (BLOCKING.has(tool))
36
+ return `${tool} blocks the session waiting on the terminal, and nobody is watching it. Decide with your best judgement and state the assumption, or ask over chat with the metro tools and keep working in the meantime.`;
37
+ if (typeof payload.agent_id === 'string' && payload.agent_id !== '') return null;
38
+ if (tool === 'Agent' && input.run_in_background === false)
39
+ return 'A foreground subagent blinds the main thread: it cannot see or acknowledge inbound metro messages until the agent finishes. Re-issue the identical Agent call with run_in_background: true, then wait for the task notification.';
40
+ if (ORCHESTRATION.has(tool)) return null;
41
+ if (METRO_MCP.test(tool)) return null;
42
+ if (MCP.test(tool))
43
+ return `A connector call is work, not orchestration: ${tool} reads somebody's files, mail or records, and its answer can be long enough to crowd out the chat you are here to follow. Only metro's own tools run on this thread. Delegate it to a subagent with the Agent tool (run_in_background: true; the worker reaches every connector) and relay what it finds.`;
44
+ if (tool === 'Read' && typeof input.file_path === 'string' && IMAGE.test(input.file_path)) return null;
45
+ return `The main thread is orchestrator-only, so ${tool === '' ? 'this tool' : tool} is not available here. Delegate the work to a subagent with the Agent tool (run_in_background: true; the worker agent has full tool access) or to a Workflow, and report the result over metro.`;
46
+ }
47
+
48
+ let payload = {};
49
+ try {
50
+ payload = JSON.parse(readFileSync(0, 'utf8'));
51
+ } catch {
52
+ payload = {};
53
+ }
54
+ const reason = verdict(payload !== null && typeof payload === 'object' ? payload : {});
55
+ if (reason !== null) deny(reason);
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const pluginRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
+ const mcpPath = join(pluginRoot, '.mcp.json');
9
+ const NAME_PREFIX = 'metro.box ';
10
+
11
+ const localUrl = () => `http://127.0.0.1:${Number(process.env.METRO_WEBHOOK_PORT) || 8420}`;
12
+
13
+ function agentsDir() {
14
+ const explicit = process.env.METRO_AGENTS_DIR?.trim();
15
+ return explicit ? explicit : join(homedir(), '.metro', 'agents');
16
+ }
17
+
18
+ function localAgents() {
19
+ const dir = agentsDir();
20
+ if (!existsSync(dir)) return [];
21
+ const out = [];
22
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
23
+ if (!entry.isDirectory()) continue;
24
+ const path = join(dir, entry.name, 'agent.json');
25
+ if (!existsSync(path)) continue;
26
+ try {
27
+ const file = JSON.parse(readFileSync(path, 'utf8'));
28
+ if (typeof file.id === 'string' && typeof file.name === 'string' && typeof file.key === 'string')
29
+ out.push({ id: file.id, name: file.name, key: file.key });
30
+ } catch {
31
+ continue;
32
+ }
33
+ }
34
+ return out.sort((a, b) => a.name.localeCompare(b.name));
35
+ }
36
+
37
+ function pickAgent() {
38
+ const agents = localAgents();
39
+ const wanted = process.env.METRO_AGENT?.trim();
40
+ if (wanted) {
41
+ const found = agents.find((a) => a.name === wanted || a.id === wanted);
42
+ if (!found) throw new Error(`no local agent named '${wanted}' in ${agentsDir()}`);
43
+ return found;
44
+ }
45
+ if (agents.length === 1) return agents[0];
46
+ throw new Error(
47
+ agents.length === 0
48
+ ? 'no agent on this machine yet โ€” start metro serve and create or restore one in the web UI'
49
+ : `several agents on this machine โ€” set METRO_AGENT to one of: ${agents.map((a) => a.name).join(', ')}`,
50
+ );
51
+ }
52
+
53
+ async function api(path, key) {
54
+ let res;
55
+ try {
56
+ res = await fetch(`${localUrl()}${path}`, { headers: { authorization: `Bearer ${key}` } });
57
+ } catch {
58
+ throw new Error(`no metro daemon on ${localUrl()} โ€” start one with: metro serve`);
59
+ }
60
+ const body = await res.json().catch(() => null);
61
+ if (!res.ok) {
62
+ const detail = typeof body?.error === 'string' ? body.error : `the daemon answered ${res.status}`;
63
+ const err = new Error(detail);
64
+ err.status = res.status;
65
+ throw err;
66
+ }
67
+ return body;
68
+ }
69
+
70
+ function serverEntries(rawJson) {
71
+ const servers = JSON.parse(rawJson)?.mcpServers;
72
+ if (typeof servers !== 'object' || servers === null)
73
+ throw new Error('the daemon returned an unexpected mcpServers shape');
74
+ const out = {};
75
+ for (const [key, value] of Object.entries(servers)) {
76
+ const name = key.startsWith(NAME_PREFIX) ? key.slice(NAME_PREFIX.length) : key;
77
+ out[name] = {
78
+ type: value.type ?? 'http',
79
+ url: value.url,
80
+ headersHelper: 'node "${CLAUDE_PLUGIN_ROOT}/bin/metro-plugin.mjs" headers',
81
+ };
82
+ }
83
+ return out;
84
+ }
85
+
86
+ async function refresh() {
87
+ const agent = pickAgent();
88
+ const body = await api('/api/cli/mcp', agent.key);
89
+ const servers = serverEntries(body.json);
90
+ writeFileSync(mcpPath, `${JSON.stringify(servers, null, 2)}\n`);
91
+ const names = Object.keys(servers);
92
+ process.stdout.write(
93
+ `agent '${agent.name}': ${names.length} server(s) written to the plugin\n` +
94
+ (names.length ? ` ${names.join('\n ')}\n` : '') +
95
+ 'Run /reload-plugins to apply in this session.\n',
96
+ );
97
+ }
98
+
99
+ function headers() {
100
+ let agent;
101
+ try {
102
+ agent = pickAgent();
103
+ } catch (err) {
104
+ process.stderr.write(`metro: ${err.message}\n`);
105
+ process.stdout.write('{}\n');
106
+ return;
107
+ }
108
+ process.stdout.write(`${JSON.stringify({ Authorization: `Bearer ${agent.key}` })}\n`);
109
+ }
110
+
111
+ const command = process.argv[2];
112
+ const run = { refresh, headers: () => Promise.resolve(headers()) }[command];
113
+
114
+ if (run === undefined) {
115
+ process.stderr.write('usage: metro-plugin.mjs <refresh|headers>\n');
116
+ process.exit(1);
117
+ }
118
+
119
+ run().catch((err) => {
120
+ process.stderr.write(`metro: ${err.message}\n`);
121
+ process.exit(err.status === 401 ? 2 : 1);
122
+ });
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const given = (value) => (typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined);
8
+ const claudeDir = given(process.env.CLAUDE_CONFIG_DIR) ?? join(given(process.env.HOME) ?? homedir(), '.claude');
9
+ const SKILL = join(claudeDir, 'skills', 'metro-orchestrator', 'SKILL.md');
10
+ const FALLBACK = join(dirname(dirname(fileURLToPath(import.meta.url))), 'orchestrator.md');
11
+
12
+ const body = (text) => text.replace(/^---[\s\S]*?\n---\n/, '').trim();
13
+
14
+ function guidance() {
15
+ for (const path of [SKILL, FALLBACK]) {
16
+ if (!existsSync(path)) continue;
17
+ try {
18
+ const text = body(readFileSync(path, 'utf8'));
19
+ if (text !== '') return text;
20
+ } catch {
21
+ continue;
22
+ }
23
+ }
24
+ return '';
25
+ }
26
+
27
+ const text = guidance();
28
+ if (text !== '')
29
+ process.stdout.write(
30
+ `${JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: `This machine runs as a metro agent. Standing rules, kept in the metro-orchestrator skill:\n\n${text}` } })}\n`,
31
+ );
@@ -0,0 +1,25 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/bin/guard.mjs\"",
9
+ "statusMessage": "Checking the orchestrator boundary"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "SessionStart": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/bin/session-start.mjs\""
20
+ }
21
+ ]
22
+ }
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: metro-orchestrator
3
+ description: How this box's agent works on metro. Acknowledge, delegate, relay, answer over chat, never block on the terminal. Loaded at every session start by the metro plugin; edit it here to change the rules on this box.
4
+ ---
5
+
6
+ You are the main thread of a Claude Code session that is connected to chat through metro, on a machine nobody watches. The main thread is an orchestrator: it may only delegate and talk. A guard in the metro plugin denies every other tool on this thread, so this is the shape of the work, not a preference.
7
+
8
+ **Inbound.** A message arrives as a `<channel source="metro" โ€ฆ>` block. Read `addressed` first: `direct`, `mention` or `reply` means it is for you; without it the message merely happened in a room you watch, so treat it as context and stay silent unless the person plainly wants you. For a message that is for you, the first tool call is `react` on it (๐Ÿ‘€, then ๐Ÿ‘ or โœ… once done) so the person knows you saw it, and any reply goes on the same `line`, passed verbatim.
9
+
10
+ **Delegate everything that is work.** Reading files, running commands, searching, fetching pages, editing, and every connector tool: hand it to a subagent with the `Agent` tool, `subagent_type: "worker"`, always `run_in_background: true`, then keep answering chat while it runs and relay the result when the task notification arrives. A foreground subagent blinds you to chat and the guard refuses it. You may read image files yourself, so a screenshot someone sends is yours to look at.
11
+
12
+ **Never wait on the terminal.** `AskUserQuestion`, plan mode and anything that blocks for a person at the keyboard are denied everywhere, main thread and subagents alike. When something is ambiguous, do what does not depend on it, choose the most reasonable reading, say so in your reply, and if a real decision is needed ask over chat with `send` and carry on. Tool-approval prompts reach the chat too; the person answers `yes <id>` or `no <id>` there.
13
+
14
+ **Report like a relay.** A worker's report is the only evidence you have, so pass on what it verified and say plainly what it did not. Short messages on chat: lead with the outcome, one idea per sentence, no tool names, no file paths unless the person needs them. Send a file out with `create_upload` (the curl line it hands back runs in a worker) and then `send` with the upload id.
15
+
16
+ **Keep going.** Long tasks run as background agents and loops; check in over chat when something lands rather than going quiet. The session is restarted by the daemon if it exits, so an interrupted task is resumed from chat, not from memory.
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: refresh
3
+ description: Rewrite the plugin's MCP servers from the metro daemon's connector list by hand, for when the daemon could not keep the list current itself.
4
+ ---
5
+
6
+ The metro daemon writes this plugin's MCP server list itself whenever a connector is added, renamed or removed, so this is the fallback for when it could not: the daemon was not running at the time, or a plugin update reset the file.
7
+
8
+ 1. Run with Bash:
9
+
10
+ ```
11
+ node "${CLAUDE_PLUGIN_ROOT}/bin/metro-plugin.mjs" refresh
12
+ ```
13
+
14
+ 2. On success, relay the script's output verbatim and tell the user to run `/reload-plugins --force`, which re-reads the plugin from disk; a plain `/reload-plugins` reuses a cached manifest and keeps the old list. You cannot run that command for them.
15
+ 3. If the script says there is no daemon, tell the user to start one with `metro serve` and stop. If it says there is no agent yet, tell them to create or restore one in the web UI the daemon links to.
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@metro-labs/core",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "What every metro process shares: the logger, the event bus and its types, ids, secure file writes, the train protocol, station types and attachment handling. No HTTP, no policy.",
6
+ "type": "module",
7
+ "exports": {
8
+ "./log": "./src/log.ts",
9
+ "./events": "./src/events.ts",
10
+ "./identity": "./src/identity.ts",
11
+ "./is-record": "./src/is-record.ts",
12
+ "./secure-fs": "./src/secure-fs.ts",
13
+ "./version": "./src/version.ts",
14
+ "./train-error": "./src/train-error.ts",
15
+ "./trains/protocol": "./src/protocol.ts",
16
+ "./ids": "./src/ids.ts",
17
+ "./address": "./src/address.ts",
18
+ "./station-names": "./src/station-names.ts",
19
+ "./str": "./src/str.ts",
20
+ "./lines": "./src/lines.ts",
21
+ "./endpoints": "./src/endpoints.ts",
22
+ "./stations/types": "./src/stations/types.ts",
23
+ "./stations/station-runtime": "./src/stations/station-runtime.ts",
24
+ "./stations/account-store": "./src/stations/account-store.ts",
25
+ "./stations/attachments": "./src/stations/attachments.ts",
26
+ "./stations/messaging-normalize": "./src/stations/messaging-normalize.ts"
27
+ },
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "test": "tsc --noEmit && METRO_STATE_DIR=\"$(mktemp -d /tmp/metro-test.XXXXXX)\" bun test test/"
31
+ },
32
+ "dependencies": {
33
+ "pino": "^9.5.0",
34
+ "pino-pretty": "^13.1.3"
35
+ },
36
+ "devDependencies": {
37
+ "@stage-labs/config": "0.1.0-beta.2",
38
+ "eslint": "^10.3.0",
39
+ "knip": "^6.15.0",
40
+ "madge": "^8.0.0",
41
+ "typescript": "^5"
42
+ }
43
+ }
@@ -0,0 +1,6 @@
1
+ const ADDRESS_RE = /^0x[0-9a-f]{40}$/;
2
+
3
+ export function normalizeAddress(raw: string): string | null {
4
+ const address = raw.trim().toLowerCase();
5
+ return ADDRESS_RE.test(address) ? address : null;
6
+ }
@@ -0,0 +1,7 @@
1
+ export interface Endpoint {
2
+ id: string;
3
+ webhookId?: string;
4
+ label: string;
5
+ secret?: string;
6
+ createdAt: string;
7
+ }
@@ -0,0 +1,120 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { Line } from './lines.js';
3
+
4
+ export { userSelf, daemonSelf } from './identity.js';
5
+
6
+ export type StructuredEvent =
7
+ | { type: 'msg' }
8
+ | { type: 'react'; emoji?: string; targetId?: string }
9
+ | { type: 'edit'; targetId?: string }
10
+ | { type: 'delete'; targetId?: string }
11
+ | { type: 'reply'; replyTo?: string }
12
+ | { type: 'system'; source?: string; eventName?: string };
13
+
14
+ export interface MetroEvent {
15
+ id: string;
16
+ ts: string;
17
+ station: string;
18
+ line: Line;
19
+ lineName?: string;
20
+ from: Line;
21
+ fromName?: string;
22
+ fromDisplayName?: string;
23
+ to: Line;
24
+ text?: string;
25
+ messageId?: string;
26
+ replyTo?: string;
27
+ isPrivate?: boolean;
28
+ mentionsSelf?: boolean;
29
+ replyToSelf?: boolean;
30
+ payload?: unknown;
31
+ display?: string;
32
+ event?: StructuredEvent;
33
+ agent?: string;
34
+ seq?: number;
35
+ }
36
+
37
+ function isExternalWebhook(e: MetroEvent): boolean {
38
+ return e.station === 'webhook' && !Line.isLocal(e.from);
39
+ }
40
+
41
+ function webhookEventName(e: MetroEvent): string | undefined {
42
+ const headers = (e.payload as { headers?: Record<string, string> } | undefined)
43
+ ?.headers;
44
+ return headers?.['x-github-event'] ?? headers?.['x-intercom-topic'];
45
+ }
46
+
47
+ export function classifyEvent(e: MetroEvent): StructuredEvent {
48
+ if (isExternalWebhook(e)) {
49
+ return {
50
+ type: 'system',
51
+ source: 'webhook',
52
+ eventName: webhookEventName(e),
53
+ };
54
+ }
55
+ const emoji =
56
+ (e.payload as { emoji?: string } | undefined)?.emoji ??
57
+ e.text?.match(/^\[react (.+)\]$/)?.[1];
58
+ if (emoji) return { type: 'react', emoji, targetId: e.replyTo };
59
+ if (e.replyTo) return { type: 'reply', replyTo: e.replyTo };
60
+ return { type: 'msg' };
61
+ }
62
+
63
+ export function formatDisplay(e: MetroEvent): string {
64
+ const headerFor = (icon: string, parts: (string | undefined)[]): string =>
65
+ `**${icon} ${parts.filter(Boolean).join(' ยท ')}**`;
66
+ const body = e.text ?? '';
67
+ if (isExternalWebhook(e)) {
68
+ const ev = webhookEventName(e);
69
+ return `${headerFor('๐Ÿช', ['webhook', e.lineName, ev])}\n> ${body}`;
70
+ }
71
+ if (Line.isLocal(e.from)) {
72
+ return `${headerFor('๐Ÿ“ค', [e.station, 'โ†’', e.fromName ?? e.to])}\n> ${body}`;
73
+ }
74
+ return `${headerFor('๐Ÿ“ฉ', [e.station, e.fromName ?? e.from, e.lineName])}\n> ${body}`;
75
+ }
76
+
77
+ export const mintId = (): string =>
78
+ `msg_${randomBytes(6).toString('base64url')}`;
79
+
80
+ export type BusListener = (event: MetroEvent, busSeq: number) => void;
81
+
82
+ export interface BufferedEvent {
83
+ busSeq: number;
84
+ event: MetroEvent;
85
+ }
86
+
87
+ const BUS_BUFFER_MAX = 500;
88
+
89
+ const listeners = new Set<BusListener>();
90
+ const buffer: BufferedEvent[] = [];
91
+ let busSeq = 0;
92
+
93
+ export function publishEvent(event: MetroEvent): void {
94
+ busSeq += 1;
95
+ const seq = busSeq;
96
+ buffer.push({ busSeq: seq, event });
97
+ if (buffer.length > BUS_BUFFER_MAX) buffer.shift();
98
+ for (const fn of [...listeners]) {
99
+ try {
100
+ fn(event, seq);
101
+ } catch (err) {
102
+ console.error('[metro-bus] listener threw for busSeq', seq, err);
103
+ }
104
+ }
105
+ }
106
+
107
+ export function currentBusSeq(): number {
108
+ return busSeq;
109
+ }
110
+
111
+ export function bufferedSince(afterBusSeq: number): BufferedEvent[] {
112
+ return buffer.filter((b) => b.busSeq > afterBusSeq);
113
+ }
114
+
115
+ export function subscribeEvents(fn: BusListener): () => void {
116
+ listeners.add(fn);
117
+ return () => {
118
+ listeners.delete(fn);
119
+ };
120
+ }
@@ -0,0 +1,7 @@
1
+ import { Line } from './lines.js';
2
+
3
+ export const userSelf = (): Line =>
4
+ (process.env.METRO_FROM ?? 'metro://user') as Line;
5
+
6
+ export const daemonSelf = (): Line =>
7
+ (process.env.METRO_FROM ?? process.env.METRO_SELF_URI ?? 'metro://user') as Line;
@@ -0,0 +1,21 @@
1
+ import { randomBytes } from 'node:crypto';
2
+
3
+ const ID_BYTES = 8;
4
+ const REROLLS = 16;
5
+
6
+ export const ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{10}$/;
7
+
8
+ export function newId(): string {
9
+ for (let attempt = 0; attempt < REROLLS; attempt += 1) {
10
+ const id = randomBytes(ID_BYTES).toString('base64url');
11
+ if (ID_RE.test(id)) return id;
12
+ }
13
+ throw new Error('could not generate an id');
14
+ }
15
+
16
+ export function parseId(raw: unknown): string | null {
17
+ if (typeof raw !== 'string') return null;
18
+ return ID_RE.test(raw) ? raw : null;
19
+ }
20
+
21
+ export const AGENT_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{1,31}$/;
@@ -0,0 +1,2 @@
1
+ export const isRecord = (v: unknown): v is Record<string, unknown> =>
2
+ typeof v === 'object' && v !== null && !Array.isArray(v);