@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,151 @@
1
+ import { forwardTrainCall } from '../stations/train-call.js';
2
+ import {
3
+ accountStationNames,
4
+ stationByName,
5
+ } from '../stations/registry.js';
6
+ import { listEndpoints } from '../net/tunnel.js';
7
+ import { hookUrl, threemaCallbackUrl } from '../stations/attach.js';
8
+ import { accountEnabled, agentIdForAccount, allowlistForAccount, knownAccounts, type KnownAccount } from '../agents/map.js';
9
+
10
+ const accountId = (acc: unknown): string | undefined => {
11
+ const id = (acc as { id?: unknown }).id;
12
+ return typeof id === 'string' ? id : undefined;
13
+ };
14
+
15
+ const asRecord = (acc: unknown): Record<string, unknown> | undefined =>
16
+ typeof acc === 'object' && acc !== null && !Array.isArray(acc)
17
+ ? (acc as Record<string, unknown>)
18
+ : undefined;
19
+
20
+ function withAgentId(station: string, acc: unknown): unknown {
21
+ const rec = asRecord(acc);
22
+ if (rec === undefined) return acc;
23
+ const id = typeof rec.id === 'string' ? rec.id : undefined;
24
+ if (id === undefined) return acc;
25
+ const agentId = agentIdForAccount(station, id);
26
+ if (agentId === undefined) return acc;
27
+ const allowlist = allowlistForAccount(station, id);
28
+ const tagged = allowlist === undefined ? { ...rec, agentId } : { ...rec, agentId, allowlist };
29
+ return accountEnabled(station, id) ? tagged : { ...tagged, enabled: false };
30
+ }
31
+
32
+ export function attachAgentIds(
33
+ byStation: Record<string, unknown[]>,
34
+ ): Record<string, unknown[]> {
35
+ const out: Record<string, unknown[]> = {};
36
+ for (const [station, list] of Object.entries(byStation))
37
+ out[station] = list.map((acc) => withAgentId(station, acc));
38
+ return out;
39
+ }
40
+
41
+ export function scopeAccountsByAgent(
42
+ byStation: Record<string, unknown[]>,
43
+ allowed: Set<string>,
44
+ ): Record<string, unknown[]> {
45
+ const out: Record<string, unknown[]> = {};
46
+ for (const [station, list] of Object.entries(byStation)) {
47
+ out[station] = list.filter((acc) => {
48
+ const id = accountId(acc);
49
+ if (id === undefined) return false;
50
+ const agentId = agentIdForAccount(station, id);
51
+ return agentId !== undefined && allowed.has(agentId);
52
+ });
53
+ }
54
+ return out;
55
+ }
56
+
57
+ const hasTrain = (station: string): boolean =>
58
+ stationByName(station)?.hasTrain !== false;
59
+
60
+ function inCoreAccounts(station: string): unknown[] {
61
+ if (station !== 'webhook') return [];
62
+ return listEndpoints().flatMap((e) =>
63
+ e.secret === undefined || e.webhookId === undefined
64
+ ? []
65
+ : [
66
+ {
67
+ id: e.id,
68
+ handle: `/api/webhooks/${e.webhookId}`,
69
+ endpoint: hookUrl(e.webhookId, e.secret),
70
+ },
71
+ ],
72
+ );
73
+ }
74
+
75
+ function withCallbackUrl(acc: unknown): unknown {
76
+ const rec = asRecord(acc);
77
+ if (rec === undefined) return acc;
78
+ const { callbackId, callbackToken, ...rest } = rec;
79
+ if (typeof callbackId !== 'string' || typeof callbackToken !== 'string') return rest;
80
+ return { ...rest, callback: threemaCallbackUrl(callbackId, callbackToken) };
81
+ }
82
+
83
+ const decorate = (station: string, rows: unknown[]): unknown[] =>
84
+ station === 'threema' ? rows.map(withCallbackUrl) : rows;
85
+
86
+ export interface ScopedAccounts {
87
+ accounts: Record<string, unknown[]>;
88
+ unavailable: string[];
89
+ }
90
+
91
+ async function liveAccounts(
92
+ station: string,
93
+ ): Promise<{ rows: unknown[]; reachable: boolean }> {
94
+ if (!hasTrain(station))
95
+ return { rows: inCoreAccounts(station), reachable: true };
96
+ try {
97
+ const resp = await forwardTrainCall(station, 'accounts', {});
98
+ const list = (resp.result as { accounts?: unknown[] } | undefined)?.accounts;
99
+ return { rows: decorate(station, Array.isArray(list) ? list : []), reachable: true };
100
+ } catch {
101
+ return { rows: [], reachable: false };
102
+ }
103
+ }
104
+
105
+ function mergeKnown(
106
+ station: string,
107
+ live: unknown[],
108
+ known: KnownAccount[],
109
+ ): unknown[] {
110
+ const seen = new Set(
111
+ live.flatMap((row) => {
112
+ const id = accountId(row);
113
+ return id === undefined ? [] : [id];
114
+ }),
115
+ );
116
+ const missing = known
117
+ .filter((r) => r.station === station && !seen.has(r.id))
118
+ .map((r) => ({ id: r.id }));
119
+ return [...live, ...missing];
120
+ }
121
+
122
+ async function loadStations(): Promise<ScopedAccounts> {
123
+ const unavailable: string[] = [];
124
+ const accounts: Record<string, unknown[]> = {};
125
+ const known = knownAccounts();
126
+ await Promise.all(
127
+ accountStationNames().map(async (station) => {
128
+ const { rows, reachable } = await liveAccounts(station);
129
+ if (!reachable) unavailable.push(station);
130
+ accounts[station] = hasTrain(station) ? mergeKnown(station, rows, known) : rows;
131
+ }),
132
+ );
133
+ return { accounts, unavailable };
134
+ }
135
+
136
+ export async function gatherAccounts(
137
+ allowedAgents?: Set<string>,
138
+ ): Promise<Record<string, unknown[]>> {
139
+ const { accounts } = await loadStations();
140
+ return allowedAgents ? scopeAccountsByAgent(accounts, allowedAgents) : accounts;
141
+ }
142
+
143
+ export async function gatherAccountsForAgents(
144
+ allowed: Set<string>,
145
+ ): Promise<ScopedAccounts> {
146
+ const { accounts, unavailable } = await loadStations();
147
+ return {
148
+ accounts: attachAgentIds(scopeAccountsByAgent(accounts, allowed)),
149
+ unavailable,
150
+ };
151
+ }
@@ -0,0 +1,216 @@
1
+ import { stationForLine } from '../stations/registry.js';
2
+ import { kindOf, toCanonical } from '@metro-labs/core/stations/attachments';
3
+ import {
4
+ cleanupAttachments,
5
+ resolveAttachments,
6
+ type ResolvedAttachment,
7
+ } from '../stations/attach-resolve.js';
8
+ import type { CanonicalAttachment, ToolResult } from '@metro-labs/core/stations/types';
9
+ import { errResult, makeCtx, ok, okJson, toErr } from './ctx.js';
10
+ import { allowedAgents, currentIdentity } from './request-identity.js';
11
+ import { str } from '@metro-labs/core/str';
12
+
13
+ type Station = NonNullable<ReturnType<typeof stationForLine>>;
14
+
15
+ interface MessageArgs {
16
+ line: string;
17
+ a: Record<string, unknown>;
18
+ ctx: ReturnType<typeof makeCtx>;
19
+ onSent?: (messageId: string) => void;
20
+ station: Station;
21
+ }
22
+
23
+ const messageIdOf = (response: { result: unknown }): string | undefined => {
24
+ const id = (response.result as { messageId?: unknown } | null)?.messageId;
25
+ return typeof id === 'string' && id !== '' ? id : undefined;
26
+ };
27
+
28
+ const withId = (text: string, messageId: string | undefined): string =>
29
+ messageId === undefined ? text : `${text} — message_id: ${messageId}`;
30
+
31
+ interface Sent {
32
+ labels: string[];
33
+ messageId?: string;
34
+ }
35
+
36
+ const deliveredLabels = (response: { result: unknown }): string[] => {
37
+ const list = (response.result as { attachments?: unknown } | null)
38
+ ?.attachments;
39
+ return Array.isArray(list)
40
+ ? list.filter((x): x is string => typeof x === 'string')
41
+ : [];
42
+ };
43
+
44
+ function assertDelivered(
45
+ station: Station,
46
+ delivered: string[],
47
+ atts: ResolvedAttachment[],
48
+ ): void {
49
+ if (delivered.length < atts.length)
50
+ throw new Error(
51
+ `${station.name} delivered ${delivered.length} of ${atts.length} attachment(s); ` +
52
+ 'the message text may still have been sent',
53
+ );
54
+ }
55
+
56
+ async function sendNative(
57
+ m: MessageArgs,
58
+ text: string | undefined,
59
+ replyTo: string | undefined,
60
+ atts: ResolvedAttachment[],
61
+ ): Promise<Sent> {
62
+ const { line, ctx, station } = m;
63
+ const labels: string[] = [];
64
+ let messageId: string | undefined;
65
+ if (text) {
66
+ const response = await ctx.call(
67
+ 'send',
68
+ replyTo ? { line, text, replyTo } : { line, text },
69
+ );
70
+ messageId = messageIdOf(response);
71
+ labels.push('text');
72
+ }
73
+ if (!atts.length) return { labels, messageId };
74
+ const delivered = station.sendAttachments
75
+ ? await station.sendAttachments(line, atts, ctx)
76
+ : [];
77
+ assertDelivered(station, delivered, atts);
78
+ labels.push(...delivered);
79
+ return { labels, messageId };
80
+ }
81
+
82
+ async function sendForwarded(
83
+ m: MessageArgs,
84
+ text: string | undefined,
85
+ replyTo: string | undefined,
86
+ atts: ResolvedAttachment[],
87
+ ): Promise<Sent> {
88
+ const { line, ctx, station } = m;
89
+ const args: Record<string, unknown> = { line };
90
+ if (text) args.text = text;
91
+ if (replyTo) args.replyTo = replyTo;
92
+ if (atts.length) args.attachments = atts.map(toCanonical);
93
+ const response = await ctx.call('send', args);
94
+ const messageId = messageIdOf(response);
95
+ const labels: string[] = [];
96
+ if (text) labels.push('text');
97
+ if (!atts.length) return { labels, messageId };
98
+ const delivered = deliveredLabels(response);
99
+ assertDelivered(station, delivered, atts);
100
+ labels.push(...delivered);
101
+ return { labels, messageId };
102
+ }
103
+
104
+ const unsupported = (station: Station, atts: CanonicalAttachment[]): string =>
105
+ `${station.name} cannot send attachments (${atts
106
+ .map((a) => kindOf(a.mime ?? '', a.path ?? a.url ?? a.name ?? ''))
107
+ .join(', ')}); send a link in \`text\` instead`;
108
+
109
+ function noteSent(m: MessageArgs, messageId: string | undefined): void {
110
+ if (messageId !== undefined) m.onSent?.(messageId);
111
+ }
112
+
113
+ async function handleSend(m: MessageArgs): Promise<ToolResult> {
114
+ const text = m.a.text as string | undefined;
115
+ const replyTo = m.a.reply_to as string | undefined;
116
+ const requested = (m.a.attachments as CanonicalAttachment[] | undefined) ?? [];
117
+ if (!text && !requested.length)
118
+ return errResult('send requires `text` or `attachments`');
119
+ if (requested.length && m.station.attachmentMode === 'none')
120
+ return errResult(unsupported(m.station, requested));
121
+ const atts = await resolveAttachments(requested, {
122
+ allowed: allowedAgents(currentIdentity()),
123
+ });
124
+ try {
125
+ const native =
126
+ m.station.attachmentMode === 'native' &&
127
+ typeof m.station.sendAttachments === 'function';
128
+ const sent = native
129
+ ? await sendNative(m, text, replyTo, atts)
130
+ : await sendForwarded(m, text, replyTo, atts);
131
+ if (!sent.labels.length)
132
+ return errResult('send requires `text` or `attachments`');
133
+ noteSent(m, sent.messageId);
134
+ return ok(withId(`sent: ${sent.labels.join(', ')}`, sent.messageId));
135
+ } finally {
136
+ await cleanupAttachments(atts);
137
+ }
138
+ }
139
+
140
+ async function handleRead({ line, a, ctx }: MessageArgs): Promise<ToolResult> {
141
+ const args: Record<string, unknown> = { line };
142
+ if (typeof a.limit === 'number') args.limit = a.limit;
143
+ if (a.before) args.before = str(a.before);
144
+ if (a.since) args.since = str(a.since);
145
+ return okJson(await ctx.call('read', args));
146
+ }
147
+
148
+ type MessageHandler = (m: MessageArgs) => Promise<ToolResult>;
149
+
150
+ interface VerbSpec {
151
+ args: readonly (readonly [string, string])[];
152
+ success: string;
153
+ }
154
+
155
+ const MESSAGE_VERBS: Record<string, VerbSpec> = {
156
+ reply: { args: [['message_id', 'replyTo'], ['text', 'text']], success: 'replied' },
157
+ react: { args: [['message_id', 'messageId'], ['emoji', 'emoji']], success: 'reacted' },
158
+ unreact: {
159
+ args: [['message_id', 'messageId'], ['emoji', 'emoji']],
160
+ success: 'reaction removed',
161
+ },
162
+ edit: { args: [['message_id', 'messageId'], ['text', 'text']], success: 'edited' },
163
+ delete: { args: [['message_id', 'messageId']], success: 'deleted' },
164
+ };
165
+
166
+ function makeVerbHandler(verb: string, spec: VerbSpec): MessageHandler {
167
+ return async ({ line, a, ctx, onSent }) => {
168
+ const payload: Record<string, unknown> = { line };
169
+ for (const [snake, camel] of spec.args) {
170
+ const value = str(a[snake]);
171
+ if (!value) {
172
+ const fields = spec.args.map(([snakeName]) => `\`${snakeName}\``).join(' and ');
173
+ return errResult(`${verb} requires ${fields}`);
174
+ }
175
+ payload[camel] = value;
176
+ }
177
+ const response = await ctx.call(verb, payload);
178
+ const messageId = messageIdOf(response);
179
+ if (verb === 'reply' && messageId !== undefined) onSent?.(messageId);
180
+ return ok(withId(spec.success, messageId));
181
+ };
182
+ }
183
+
184
+ const MESSAGE_HANDLERS: Record<string, MessageHandler> = {
185
+ send: handleSend,
186
+ read: handleRead,
187
+ ...Object.fromEntries(
188
+ Object.entries(MESSAGE_VERBS).map(([verb, spec]) => [verb, makeVerbHandler(verb, spec)]),
189
+ ),
190
+ };
191
+
192
+ export interface ToolHooks {
193
+ onSent?: (messageId: string) => void;
194
+ }
195
+
196
+ export async function dispatchMessageTool(
197
+ name: string,
198
+ a: Record<string, unknown>,
199
+ hooks: ToolHooks = {},
200
+ ): Promise<ToolResult> {
201
+ const line = str(a.line);
202
+ if (!line) return errResult(`${name} requires \`line\``);
203
+ const station = stationForLine(line);
204
+ if (!station || station.messageVerbs.size === 0) {
205
+ return errResult(
206
+ `${station?.name ?? 'these'} lines do not support outbound messaging (send/reply/react/unreact/edit/delete/read).`,
207
+ );
208
+ }
209
+ const handler = MESSAGE_HANDLERS[name];
210
+ if (!handler) return errResult(`unknown tool: ${name}`);
211
+ try {
212
+ return await handler({ line, a, ctx: makeCtx(station.name), station, onSent: hooks.onSent });
213
+ } catch (e) {
214
+ return toErr(name, e);
215
+ }
216
+ }
@@ -0,0 +1,29 @@
1
+ import { eventInScope } from '../agents/scope.js';
2
+ import { allowedAgents, type RequestIdentity } from './request-identity.js';
3
+ import { sessionScopeKey } from './session-route.js';
4
+
5
+ export class ChannelOwner {
6
+ private stream: RequestIdentity | undefined;
7
+
8
+ bindStream(identity: RequestIdentity): void {
9
+ this.stream = identity;
10
+ }
11
+
12
+ releaseStream(): void {
13
+ this.stream = undefined;
14
+ }
15
+
16
+ scope(): Set<string> {
17
+ return allowedAgents(this.stream);
18
+ }
19
+
20
+ streamBelongsTo(identity: RequestIdentity): boolean {
21
+ if (this.stream === undefined) return false;
22
+ return sessionScopeKey(this.stream) === sessionScopeKey(identity);
23
+ }
24
+
25
+ inScope(line: string): boolean {
26
+ if (this.stream === undefined) return false;
27
+ return eventInScope(this.scope(), line);
28
+ }
29
+ }
@@ -0,0 +1,62 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { forwardTrainCall } from '../stations/train-call.js';
3
+ import { errMsg, log } from '@metro-labs/core/log';
4
+ import { TrainError } from '@metro-labs/core/train-error';
5
+ import { type ToolContext, type ToolResult } from '@metro-labs/core/stations/types';
6
+
7
+ export async function metroCall(
8
+ train: string,
9
+ action: string,
10
+ args: Record<string, unknown>,
11
+ ): Promise<{ result: unknown }> {
12
+ const line = typeof args.line === 'string' ? args.line : undefined;
13
+ log.info({ train, action, line }, 'metro: dispatching train call');
14
+ let response;
15
+ try {
16
+ response = await forwardTrainCall(train, action, args);
17
+ } catch (err) {
18
+ log.warn(
19
+ { train, action, line, err: errMsg(err) },
20
+ 'metro: train call failed to dispatch',
21
+ );
22
+ throw new TrainError(
23
+ 'metro_call_failed',
24
+ `metro ${action} ${train}: ${errMsg(err)}`,
25
+ );
26
+ }
27
+ if (response.error) {
28
+ log.warn(
29
+ { train, action, line, err: response.error },
30
+ 'metro: train call returned error',
31
+ );
32
+ throw new TrainError(
33
+ 'metro_call_error',
34
+ `metro ${action} ${train}: ${response.error}`,
35
+ );
36
+ }
37
+ log.info({ train, action, line }, 'metro: train call ok');
38
+ return { result: response.result ?? null };
39
+ }
40
+
41
+ export const ok = (text: string): ToolResult => ({
42
+ content: [{ type: 'text', text }],
43
+ });
44
+ export const okJson = (v: unknown): ToolResult => ({
45
+ content: [{ type: 'text', text: JSON.stringify(v, null, 2) }],
46
+ });
47
+ export const errResult = (text: string): ToolResult => ({
48
+ content: [{ type: 'text', text }],
49
+ isError: true,
50
+ });
51
+ export const toErr = (name: string, e: unknown): ToolResult =>
52
+ e instanceof TrainError
53
+ ? errResult(e.message)
54
+ : errResult(`metro ${name} failed: ${String(e)}`);
55
+
56
+ export const makeCtx = (station: string): ToolContext => ({
57
+ call: (action, args) => metroCall(station, action, args),
58
+ ok,
59
+ okJson,
60
+ err: errResult,
61
+ readFile: (path) => readFile(path),
62
+ });
@@ -0,0 +1,91 @@
1
+ import type {
2
+ EventStore,
3
+ EventId,
4
+ StreamId,
5
+ } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
6
+ import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
7
+ import { frameInScope, frameLine } from './frame-scope.js';
8
+
9
+ const EVENT_STORE_MAX = 500;
10
+
11
+ interface StoredEvent {
12
+ eventId: EventId;
13
+ streamId: StreamId;
14
+ message: JSONRPCMessage;
15
+ line: string | undefined;
16
+ owner: Set<string>;
17
+ }
18
+
19
+ export interface EventStoreDeps {
20
+ scopeOf: () => Set<string>;
21
+ max?: number;
22
+ }
23
+
24
+ export interface ScopedReplay {
25
+ send: (eventId: EventId, message: JSONRPCMessage) => Promise<void>;
26
+ scope: Set<string> | undefined;
27
+ onWithheld?: (eventId: EventId, line: string | undefined) => void;
28
+ }
29
+
30
+ const SEP = '_';
31
+
32
+ const encodeEventId = (streamId: StreamId, seq: number): EventId =>
33
+ `${streamId}${SEP}${seq}`;
34
+
35
+ const decodeStreamId = (eventId: EventId): StreamId | undefined => {
36
+ const idx = eventId.lastIndexOf(SEP);
37
+ if (idx <= 0) return undefined;
38
+ return eventId.slice(0, idx);
39
+ };
40
+
41
+ export class BoundedEventStore implements EventStore {
42
+ private readonly max: number;
43
+ private readonly scopeOf: () => Set<string>;
44
+ private readonly events: StoredEvent[] = [];
45
+ private seq = 0;
46
+
47
+ constructor(deps: EventStoreDeps) {
48
+ this.scopeOf = deps.scopeOf;
49
+ this.max = deps.max ?? EVENT_STORE_MAX;
50
+ }
51
+
52
+ storeEvent(streamId: StreamId, message: JSONRPCMessage): Promise<EventId> {
53
+ const eventId = encodeEventId(streamId, ++this.seq);
54
+ this.events.push({
55
+ eventId,
56
+ streamId,
57
+ message,
58
+ line: frameLine(message),
59
+ owner: new Set(this.scopeOf()),
60
+ });
61
+ if (this.events.length > this.max) this.events.shift();
62
+ return Promise.resolve(eventId);
63
+ }
64
+
65
+ getStreamIdForEventId(eventId: EventId): Promise<StreamId | undefined> {
66
+ return Promise.resolve(decodeStreamId(eventId));
67
+ }
68
+
69
+ async replayEventsAfter(
70
+ lastEventId: EventId,
71
+ { send, scope, onWithheld }: ScopedReplay,
72
+ ): Promise<StreamId> {
73
+ const streamId = decodeStreamId(lastEventId);
74
+ if (streamId === undefined) return '';
75
+ const allowed = scope ?? new Set<string>();
76
+ let seen = false;
77
+ for (const e of this.events) {
78
+ if (e.streamId !== streamId) continue;
79
+ if (!seen) {
80
+ if (e.eventId === lastEventId) seen = true;
81
+ continue;
82
+ }
83
+ if (!frameInScope(allowed, e)) {
84
+ onWithheld?.(e.eventId, e.line);
85
+ continue;
86
+ }
87
+ await send(e.eventId, e.message);
88
+ }
89
+ return streamId;
90
+ }
91
+ }
@@ -0,0 +1,21 @@
1
+ import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
2
+ import { eventInScope } from '../agents/scope.js';
3
+ import { str } from '@metro-labs/core/str';
4
+
5
+ export interface FrameOrigin {
6
+ line: string | undefined;
7
+ owner: Set<string>;
8
+ }
9
+
10
+ export function frameLine(message: JSONRPCMessage): string | undefined {
11
+ const params: unknown = (message as { params?: unknown }).params;
12
+ if (typeof params !== 'object' || params === null) return undefined;
13
+ const meta: unknown = (params as { meta?: unknown }).meta;
14
+ if (typeof meta !== 'object' || meta === null) return undefined;
15
+ return str((meta as { line?: unknown }).line) || undefined;
16
+ }
17
+
18
+ export function frameInScope(scope: Set<string>, origin: FrameOrigin): boolean {
19
+ if (origin.line !== undefined) return eventInScope(scope, origin.line);
20
+ return origin.owner.size > 0 && [...origin.owner].every((id) => scope.has(id));
21
+ }
@@ -0,0 +1,129 @@
1
+ import { stationByName, stationForLine } from '../stations/registry.js';
2
+ import type {
3
+ GroupOp,
4
+ GroupResult,
5
+ MemberOutcome,
6
+ Station,
7
+ ToolResult,
8
+ } from '@metro-labs/core/stations/types';
9
+ import { makeCtx, okJson, toErr } from './ctx.js';
10
+ import { str } from '@metro-labs/core/str';
11
+
12
+ export interface GroupOpResult {
13
+ op: GroupOp;
14
+ line: string;
15
+ station: string;
16
+ supported: boolean;
17
+ reason?: string;
18
+ id?: string;
19
+ name?: string;
20
+ members: MemberOutcome[];
21
+ inviteLink?: string;
22
+ }
23
+
24
+ export function wrapGroupResult(
25
+ op: GroupOp,
26
+ line: string,
27
+ station: string,
28
+ r: GroupResult,
29
+ ): GroupOpResult {
30
+ const out: GroupOpResult = {
31
+ op,
32
+ line: r.line ?? line,
33
+ station,
34
+ supported: r.capability.supported,
35
+ members: Array.isArray(r.members) ? r.members : [],
36
+ };
37
+ if (r.capability.reason) out.reason = r.capability.reason;
38
+ if (r.id) out.id = r.id;
39
+ if (r.name) out.name = r.name;
40
+ if (r.inviteLink) out.inviteLink = r.inviteLink;
41
+ return out;
42
+ }
43
+
44
+ export function unsupportedGroup(
45
+ op: GroupOp,
46
+ line: string,
47
+ station: string,
48
+ reason: string,
49
+ ): GroupOpResult {
50
+ return { op, line, station, supported: false, reason, members: [] };
51
+ }
52
+
53
+ const defaultReason = (op: GroupOp, station: string): string =>
54
+ `${op} is not supported on ${station}`;
55
+
56
+ const supportsOp = (station: Station, op: GroupOp): boolean =>
57
+ station.groupOps?.has(op) ?? false;
58
+
59
+ function strList(value: unknown): string[] {
60
+ if (!Array.isArray(value)) return [];
61
+ return value.filter((v) => typeof v === 'string' && v.length > 0) as string[];
62
+ }
63
+
64
+ function forwardArgs(a: Record<string, unknown>): Record<string, unknown> {
65
+ const args: Record<string, unknown> = {};
66
+ const members = strList(a.members);
67
+ if (members.length) args.members = members;
68
+ for (const key of ['name', 'account', 'parent', 'addresses', 'inboxIds'])
69
+ if (a[key] !== undefined) args[key] = a[key];
70
+ if (a.private !== undefined) args.private = a.private;
71
+ return args;
72
+ }
73
+
74
+ async function runGroupOp(
75
+ op: GroupOp,
76
+ verb: string,
77
+ line: string,
78
+ station: Station,
79
+ args: Record<string, unknown>,
80
+ ): Promise<ToolResult> {
81
+ if (!supportsOp(station, op))
82
+ return okJson(
83
+ unsupportedGroup(op, line, station.name, defaultReason(op, station.name)),
84
+ );
85
+ try {
86
+ const { result } = await makeCtx(station.name).call(verb, args);
87
+ return okJson(wrapGroupResult(op, line, station.name, result as GroupResult));
88
+ } catch (e) {
89
+ return toErr(op, e);
90
+ }
91
+ }
92
+
93
+ export async function dispatchCreateGroup(
94
+ a: Record<string, unknown>,
95
+ ): Promise<ToolResult> {
96
+ const name = str(a.station);
97
+ if (!name)
98
+ return okJson(unsupportedGroup('create_group', '', '', 'create_group requires `station`'));
99
+ const station = stationByName(name);
100
+ if (!station)
101
+ return okJson(unsupportedGroup('create_group', '', name, `no station named ${name}`));
102
+ return runGroupOp('create_group', 'groupCreate', '', station, forwardArgs(a));
103
+ }
104
+
105
+ async function dispatchLineOp(
106
+ op: GroupOp,
107
+ verb: string,
108
+ a: Record<string, unknown>,
109
+ ): Promise<ToolResult> {
110
+ const line = str(a.line);
111
+ if (!line) return okJson(unsupportedGroup(op, '', '', `${op} requires \`line\``));
112
+ const station = stationForLine(line);
113
+ if (!station)
114
+ return okJson(unsupportedGroup(op, line, '', `no station for line ${line}`));
115
+ return runGroupOp(op, verb, line, station, { line, ...forwardArgs(a) });
116
+ }
117
+
118
+ export const dispatchAddMembers = (
119
+ a: Record<string, unknown>,
120
+ ): Promise<ToolResult> => dispatchLineOp('add_members', 'groupAddMembers', a);
121
+
122
+ export const dispatchRemoveMembers = (
123
+ a: Record<string, unknown>,
124
+ ): Promise<ToolResult> =>
125
+ dispatchLineOp('remove_members', 'groupRemoveMembers', a);
126
+
127
+ export const dispatchInviteLink = (
128
+ a: Record<string, unknown>,
129
+ ): Promise<ToolResult> => dispatchLineOp('invite_link', 'groupInviteLink', a);