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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/dist/api.js +14 -0
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +176 -0
  4. package/dist/cli.js +101 -0
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +93 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +43 -0
  11. package/dist/plugin.js +120 -0
  12. package/dist/provider-flags.js +9 -0
  13. package/dist/route.js +38 -0
  14. package/dist/runtime-install.js +97 -0
  15. package/dist/runtime.js +74 -0
  16. package/dist/serve.js +150 -0
  17. package/dist/service.js +242 -0
  18. package/dist/tail.js +119 -0
  19. package/dist/update.js +45 -0
  20. package/dist/version.js +97 -0
  21. package/package.json +14 -27
  22. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  23. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +18 -0
  24. package/runtime/marketplace/plugin/.mcp.json +1 -0
  25. package/runtime/marketplace/plugin/bin/guard.mjs +55 -0
  26. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +122 -0
  27. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  28. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  29. package/runtime/marketplace/plugin/orchestrator.md +16 -0
  30. package/runtime/marketplace/plugin/skills/refresh/SKILL.md +15 -0
  31. package/runtime/node_modules/@metro-labs/core/package.json +43 -0
  32. package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
  33. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  34. package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
  35. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  36. package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
  37. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  38. package/runtime/node_modules/@metro-labs/core/src/lines.ts +87 -0
  39. package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
  40. package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
  41. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
  42. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  44. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
  47. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +108 -0
  48. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  49. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  50. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  51. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +70 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +365 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +43 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +300 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +122 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +371 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +155 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +45 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +95 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +207 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +309 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +239 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +310 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +175 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +77 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +296 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +286 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +332 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +229 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +269 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +207 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +229 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +372 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +223 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +96 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  174. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  175. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  176. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  177. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  178. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  179. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  180. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  181. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  182. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  183. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  191. package/runtime/node_modules/@metro-labs/http/package.json +33 -0
  192. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  193. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
  194. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  195. package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
  196. package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
  197. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  198. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  199. package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
  200. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  201. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  202. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  203. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  204. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  205. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  206. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  207. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  208. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  209. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  210. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  211. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  212. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  213. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  214. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  215. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  216. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  217. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  218. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  219. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  220. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  221. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  222. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  223. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  224. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  225. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  226. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  227. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  228. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  229. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  230. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  231. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +100 -0
  232. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +219 -0
  233. package/runtime/node_modules/@metro-labs/threema/src/api.ts +130 -0
  234. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +132 -0
  235. package/runtime/node_modules/@metro-labs/threema/src/format.ts +123 -0
  236. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  237. package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
  238. package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
  239. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  240. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  241. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  242. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  243. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  244. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  245. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +190 -0
  246. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  247. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  248. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +347 -0
  249. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  250. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  251. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  252. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  253. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  254. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  255. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  256. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  257. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  258. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  259. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  260. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  261. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +18 -0
  262. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  263. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  264. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  265. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  266. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  267. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  268. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  269. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  270. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  271. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  272. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  273. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  274. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  275. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  276. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  277. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  278. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  279. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  280. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  281. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  282. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  283. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  284. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  285. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  286. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  287. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  288. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  289. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  290. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  291. package/runtime/runtime.json +3 -0
  292. package/runtime/server.ts +1 -0
  293. package/runtime/stations.json +39 -0
  294. package/runtime/trains/.keep +0 -0
  295. package/LICENSE +0 -21
  296. package/README.md +0 -252
  297. package/dist/broker.js +0 -202
  298. package/dist/cache.js +0 -69
  299. package/dist/cli/actions.js +0 -206
  300. package/dist/cli/config.js +0 -185
  301. package/dist/cli/index.js +0 -188
  302. package/dist/cli/skill.js +0 -62
  303. package/dist/cli/tail.js +0 -164
  304. package/dist/cli/util.js +0 -72
  305. package/dist/cli/webhook.js +0 -81
  306. package/dist/codex-rc.js +0 -245
  307. package/dist/dispatcher.js +0 -128
  308. package/dist/history.js +0 -121
  309. package/dist/ipc.js +0 -72
  310. package/dist/log.js +0 -15
  311. package/dist/monitor.js +0 -194
  312. package/dist/paths.js +0 -68
  313. package/dist/registry.js +0 -48
  314. package/dist/stations/claude.js +0 -45
  315. package/dist/stations/codex.js +0 -68
  316. package/dist/stations/discord.js +0 -216
  317. package/dist/stations/index.js +0 -129
  318. package/dist/stations/telegram-md.js +0 -34
  319. package/dist/stations/telegram-upload.js +0 -113
  320. package/dist/stations/telegram.js +0 -234
  321. package/dist/stations/webhook.js +0 -103
  322. package/dist/tunnel.js +0 -64
  323. package/dist/webhooks.js +0 -41
  324. package/docs/broker.md +0 -214
  325. package/docs/monitor.md +0 -163
  326. package/docs/uri-scheme.md +0 -124
  327. package/docs/users.md +0 -226
  328. package/skills/metro/SKILL.md +0 -256
@@ -0,0 +1,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);