@stage-labs/metro 0.1.0-beta.15 → 0.1.0-beta.151

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 (327) hide show
  1. package/dist/api.js +9 -0
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +182 -0
  4. package/dist/cli.js +86 -0
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +77 -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/provider-flags.js +9 -0
  12. package/dist/route.js +50 -0
  13. package/dist/runtime-install.js +97 -0
  14. package/dist/runtime.js +74 -0
  15. package/dist/serve.js +155 -0
  16. package/dist/service.js +242 -0
  17. package/dist/tail.js +119 -0
  18. package/dist/update.js +45 -0
  19. package/dist/version.js +97 -0
  20. package/package.json +14 -28
  21. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  22. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +18 -0
  23. package/runtime/marketplace/plugin/.mcp.json +1 -0
  24. package/runtime/marketplace/plugin/bin/guard.mjs +55 -0
  25. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +40 -0
  26. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  27. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  28. package/runtime/marketplace/plugin/orchestrator.md +16 -0
  29. package/runtime/node_modules/@metro-labs/core/package.json +43 -0
  30. package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
  31. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  32. package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
  33. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  34. package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
  35. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  36. package/runtime/node_modules/@metro-labs/core/src/lines.ts +87 -0
  37. package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
  38. package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
  39. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
  40. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  41. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  44. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +108 -0
  46. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  47. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  48. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  49. package/runtime/node_modules/@metro-labs/daemon/package.json +47 -0
  50. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +70 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +365 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +39 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +225 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +122 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +364 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +185 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +40 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +95 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +200 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +30 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +380 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +354 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +263 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +118 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +313 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +157 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +69 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +304 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +299 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +108 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +59 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +101 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +247 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +182 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +148 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +201 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +257 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +353 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +232 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +242 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +186 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +370 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +210 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +58 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +65 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +80 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +48 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +117 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  174. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +97 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  176. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  177. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  178. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  179. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  180. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  181. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  182. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  183. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  191. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  192. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  193. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  194. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  195. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  196. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +106 -0
  197. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -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/workos-token.ts +150 -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 +223 -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 +37 -0
  295. package/runtime/trains/.keep +0 -0
  296. package/LICENSE +0 -21
  297. package/README.md +0 -139
  298. package/dist/broker/claims.js +0 -144
  299. package/dist/broker/history-stream.js +0 -149
  300. package/dist/cli/config.js +0 -179
  301. package/dist/cli/index.js +0 -175
  302. package/dist/cli/messenger-api.js +0 -214
  303. package/dist/cli/messenger-transcribe.js +0 -43
  304. package/dist/cli/messenger-uploads.js +0 -116
  305. package/dist/cli/monitor-api.js +0 -205
  306. package/dist/cli/tail.js +0 -95
  307. package/dist/cli/util.js +0 -72
  308. package/dist/cli/webhook.js +0 -181
  309. package/dist/codex-rc/client.js +0 -225
  310. package/dist/codex-rc/protocol.js +0 -38
  311. package/dist/dispatcher/server.js +0 -122
  312. package/dist/dispatcher.js +0 -97
  313. package/dist/history.js +0 -143
  314. package/dist/ipc.js +0 -90
  315. package/dist/lines.js +0 -54
  316. package/dist/local-identity.js +0 -80
  317. package/dist/log.js +0 -15
  318. package/dist/paths.js +0 -114
  319. package/dist/trains/protocol.js +0 -99
  320. package/dist/trains/supervisor.js +0 -210
  321. package/dist/tunnel.js +0 -102
  322. package/docs/broker.md +0 -166
  323. package/docs/monitor.md +0 -241
  324. package/docs/uri-scheme.md +0 -127
  325. package/examples/README.md +0 -32
  326. package/examples/telegram.ts +0 -121
  327. package/skills/metro/SKILL.md +0 -110
package/docs/monitor.md DELETED
@@ -1,241 +0,0 @@
1
- # Metro monitor endpoints
2
-
3
- Read-only HTTP endpoints for an external observer (the `apps/app` mobile app, an admin
4
- dashboard, a curl one-liner) to view live daemon state without touching the JSONL files
5
- directly.
6
-
7
- These endpoints mount on the **existing** webhook HTTP server (default port `8420`).
8
- There is no separate daemon, no separate port, no extra process to launch. The
9
- implementation lives in [`src/cli/monitor-api.ts`](../src/cli/monitor-api.ts) (re-exported
10
- by `src/cli/tail.ts` for backwards compatibility) and is wired into the HTTP server in
11
- [`src/dispatcher/server.ts`](../src/dispatcher/server.ts) via `handleMonitorRequest`.
12
-
13
- ## Routes
14
-
15
- | Method | Path | Returns |
16
- |--------|---------------------------------|------------------------------------------------------------------------------------------|
17
- | GET | `/api/state` | JSON snapshot — `{ claims, lines, recent_history (last 100), bot_ids }`. |
18
- | GET | `/api/tail` | Server-Sent Events stream — `history.jsonl` entries, claim-aware filtered. |
19
- | POST | `/api/call/<train>/<action>` | Forward an action call to a train via `forward-call` IPC; returns `{result}`. |
20
- | POST | `/api/messenger/send` | In-daemon chat: emits a history entry on `metro://messenger/owner`. Accepts `{text?, attachments?[], as?}`. |
21
- | POST | `/api/messenger/register` | Store an Expo push token so agent replies push to the phone. |
22
- | POST | `/api/messenger/upload` | Raw binary upload (up to 25 MiB). Body = file bytes; headers `Content-Type`, optional `X-Filename`. Returns `{id, url, kind, mime, size, name?}`. |
23
- | GET | `/api/messenger/files/<name>` | Stream a previously uploaded attachment. Accepts `?token=…` as an alternative to the bearer header for `<img>` / `<audio>` tags. |
24
-
25
- `/api/state` and `/api/tail` are read-only. `/api/call/<train>/<action>` is the single
26
- write endpoint — it never touches the on-disk history; the train running on the daemon
27
- emits its own outbound event after delivering the message, which then flows through the
28
- normal SSE stream like any other entry.
29
-
30
- ## Authentication
31
-
32
- Bearer token in env: `METRO_MONITOR_TOKEN`.
33
-
34
- - If unset, both routes respond **503** with `{"error":"monitor endpoints not configured (METRO_MONITOR_TOKEN unset)"}`. Anonymous access is never allowed by accident.
35
- - If set, requests **must** carry `Authorization: Bearer <token>`. Missing/wrong/malformed → **401**. The comparison is constant-time (`crypto.timingSafeEqual`).
36
-
37
- Set in `~/.config/metro/.env`:
38
-
39
- ```
40
- METRO_MONITOR_TOKEN=<a long random string — `openssl rand -base64 32`>
41
- ```
42
-
43
- The daemon picks up the env var on next start.
44
-
45
- ## `GET /api/state`
46
-
47
- Returns a one-shot JSON snapshot:
48
-
49
- ```jsonc
50
- {
51
- "claims": {
52
- "metro://discord/123456789": "metro://claude/user/abc"
53
- },
54
- "lines": [
55
- "metro://discord/123456789",
56
- "metro://telegram/-100…"
57
- ],
58
- "recent_history": [/* most-recent-first, up to 100 HistoryEntry objects */],
59
- "bot_ids": { "discord": "1234567890", "telegram": "987654321" },
60
- "version": "x.y.z"
61
- }
62
- ```
63
-
64
- - `claims` — verbatim contents of `claims.json`.
65
- - `lines` — the set of conversation URIs seen across recent history and current claims (good-enough proxy for "what lines exist right now"). Subject to refinement; not authoritative.
66
- - `recent_history` — same shape as `HistoryEntry` in `src/history.ts`, ordered most-recent-first, capped at 100 entries.
67
- - `bot_ids` — verbatim contents of `bot-ids.json`.
68
- - `version` — the daemon's package version (handy for clients gating on capabilities).
69
-
70
- ### Pagination
71
-
72
- For backlog scrolling, pass `?before=<N>&limit=<M>` (both non-negative integers, `limit` clamped to 500):
73
-
74
- ```bash
75
- curl -H "Authorization: Bearer $METRO_MONITOR_TOKEN" \
76
- "https://monitor.metro.box/api/state?before=200&limit=100" | jq .recent_history
77
- ```
78
-
79
- When `before` is set, only `recent_history` is returned (the older slice). Without
80
- `before`, the full snapshot above is returned.
81
-
82
- ### Example
83
-
84
- ```bash
85
- curl -H "Authorization: Bearer $METRO_MONITOR_TOKEN" \
86
- https://monitor.metro.box/api/state | jq
87
- ```
88
-
89
- ## `GET /api/tail` (SSE)
90
-
91
- Server-Sent Events stream of new `history.jsonl` entries. Each event has:
92
-
93
- ```
94
- id: <metro-msg-id>
95
- event: history
96
- data: <one HistoryEntry as JSON>
97
-
98
- ```
99
-
100
- The stream stays open until the client disconnects. A `: keepalive` comment is emitted
101
- every 25 seconds to keep proxies happy.
102
-
103
- ### Query parameters
104
-
105
- All optional. Mirror the `metro tail` CLI flags.
106
-
107
- | Param | Default | Effect |
108
- |--------------------|-------------|-------------------------------------------------------------------------|
109
- | `as=<line>` | (none) | Self URI — enables "mine + free" claim-aware filtering. |
110
- | `mode=strict\|unclaimed\|all` | derived | Override the mode (`strict` needs `as=`). |
111
- | `chat=<line>` | (none) | Only emit events matching this exact `line`. |
112
- | `station=<name>` | (none) | Only emit events matching this station (`discord`, `telegram`, …). |
113
- | `include_webhooks=true` | `false` | Include webhook-station events in personal modes. |
114
- | `since=tail\|0\|<offset>` | `tail` | Where to start in `history.jsonl`. `tail` = EOF; `0` = full replay; or a byte offset. |
115
-
116
- ### Filter rule
117
-
118
- Same predicate as `metro tail --as=<id> [--strict|--unclaimed|--all] [--include-webhooks]`:
119
-
120
- > An event is delivered when its `line` is **claimed by `as=`** *or* **claimed by no one**,
121
- > minus webhooks (unless `include_webhooks=true`), minus anything failing `chat=`/`station=`.
122
-
123
- See [broker.md](./broker.md) for the underlying broker semantics.
124
-
125
- ### Example: live tail for a claude user
126
-
127
- ```bash
128
- curl -N \
129
- -H "Authorization: Bearer $METRO_MONITOR_TOKEN" \
130
- "https://monitor.metro.box/api/tail?as=metro://claude/user/abc&include_webhooks=true"
131
- ```
132
-
133
- The `-N` flag disables curl's output buffering so SSE frames appear as they arrive.
134
-
135
- ### Example: full backlog replay for debugging
136
-
137
- ```bash
138
- curl -N \
139
- -H "Authorization: Bearer $METRO_MONITOR_TOKEN" \
140
- "https://monitor.metro.box/api/tail?since=0"
141
- ```
142
-
143
- ## `POST /api/call/<train>/<action>`
144
-
145
- Forwards an action call to a train (same as `metro call <train> <action> <args>` on the
146
- command line) via the daemon's existing `forward-call` IPC. Use this from the mobile or
147
- web app to send a message, react, edit, etc. without needing shell access.
148
-
149
- ### Request
150
-
151
- ```http
152
- POST /api/call/discord/send HTTP/1.1
153
- Host: monitor.metro.box
154
- Authorization: Bearer <METRO_MONITOR_TOKEN>
155
- Content-Type: application/json
156
-
157
- {"args": {"line": "metro://discord/123", "text": "hello from the web"}}
158
- ```
159
-
160
- The body is one of:
161
-
162
- - `{"args": <object|array|string>}` — explicit `args` wrapper (recommended).
163
- - `<object>` — any other JSON object is forwarded as the args verbatim (useful for terse
164
- clients).
165
- - Empty body — forwarded as `{}`.
166
-
167
- `<train>` must match a train running under `~/.metro/trains/`; `<action>` is whatever
168
- that train expects (`send`, `react`, `edit`, …).
169
-
170
- ### Response
171
-
172
- | Status | Body | Meaning |
173
- |--------|-----------------------------------------------------|----------------------------------------------------------|
174
- | 200 | `{"result": <whatever the train returned>}` | Train accepted the call and returned a result. |
175
- | 400 | `{"error": "bad JSON body: …"}` | Body was not valid JSON. |
176
- | 401 | `{"error": "unauthorized"}` | Missing / wrong bearer token. |
177
- | 405 | `{"error": "method not allowed"}` | Wrong verb (only `POST` is accepted on this path). |
178
- | 500 | `{"error": "…"}` | Daemon IPC unavailable (e.g. socket missing). |
179
- | 502 | `{"error": "…"}` | Train returned an error or the IPC handshake malformed. |
180
-
181
- Request bodies larger than 256 KiB are rejected with HTTP 500.
182
-
183
- ### Example: send a message
184
-
185
- ```bash
186
- curl -X POST \
187
- -H "Authorization: Bearer $METRO_MONITOR_TOKEN" \
188
- -H "Content-Type: application/json" \
189
- -d '{"args":{"line":"metro://discord/123","text":"hi"}}' \
190
- https://monitor.metro.box/api/call/discord/send
191
- ```
192
-
193
- Because the daemon's `send` adapter writes a history entry once the message lands, an
194
- active `/api/tail` subscriber will receive the corresponding event a moment later
195
- (`from` = local user). UIs typically clear the input on HTTP 200 and let the SSE replay
196
- show the sent message.
197
-
198
- ## Exposing publicly via Cloudflare tunnel
199
-
200
- The daemon listens on `127.0.0.1:8420` only. To reach `/api/*` from a phone or a
201
- remote machine, route a public hostname through the existing `webhook.metro.box`
202
- cloudflared tunnel.
203
-
204
- Add a second hostname route to your `cloudflared` config (typically
205
- `~/.cloudflared/config.yml`):
206
-
207
- ```yaml
208
- ingress:
209
- - hostname: webhook.metro.box
210
- service: http://127.0.0.1:8420
211
- - hostname: monitor.metro.box # new — same backing service
212
- service: http://127.0.0.1:8420
213
- - service: http_status:404
214
- ```
215
-
216
- Then create the DNS record:
217
-
218
- ```bash
219
- cloudflared tunnel route dns <tunnel-name> monitor.metro.box
220
- ```
221
-
222
- Restart the tunnel; both hostnames now reach the same metro daemon. `webhook.metro.box`
223
- keeps serving inbound webhooks; `monitor.metro.box` serves the bearer-token-gated
224
- monitor routes.
225
-
226
- There's no harm in serving `/api/*` from `webhook.metro.box` too — the bearer-token
227
- gate is the same either way. The separate hostname is purely a routing convenience
228
- (and lets you put different access policies in front of each, e.g., Cloudflare Access
229
- on `monitor.metro.box` only).
230
-
231
- ## Failure modes
232
-
233
- | Condition | Response |
234
- |------------------------------------|-------------------------------------------------------------------|
235
- | `METRO_MONITOR_TOKEN` not set | 503 `{"error":"monitor endpoints not configured (...)"}` |
236
- | Missing `Authorization` header | 401 `{"error":"unauthorized"}` |
237
- | Wrong / malformed token | 401 `{"error":"unauthorized"}` |
238
- | Unknown `/api/*` path | 404 `{"error":"not found"}` |
239
- | `POST` (or any non-`GET`) to `/api/*` | 405 `{"error":"method not allowed"}` |
240
- | `history.jsonl` doesn't exist yet | 200 with empty `recent_history` / SSE stream w/ no events. |
241
- | Client disconnects mid-SSE | Handler clears its interval + closes the file watcher cleanly. |
@@ -1,127 +0,0 @@
1
- # Metro URI scheme
2
-
3
- Universal identifier for every conversational scope in metro. Lines pass around as opaque strings; only the owning station parses its own paths.
4
-
5
- ## Grammar
6
-
7
- ```
8
- line = "metro://" station "/" path
9
- station = lowercase identifier (claude | codex | discord | telegram | webhook | …)
10
- path = station-specific, "/"-separated segments
11
- ```
12
-
13
- The URI parses cleanly with the WHATWG `URL` parser: `new URL(line)` gives `protocol="metro:"`, `host=<station>`, `pathname="/<path>"`.
14
-
15
- ## Registered stations
16
-
17
- | Station | Pattern | Example |
18
- |------------|----------------------------------------------|------------------------------------------------------------------------|
19
- | `discord` | `metro://discord/<channel-id>` | `metro://discord/1234567890123456789` |
20
- | `telegram` | `metro://telegram/<chat-id>[/<topic-id>]` | `metro://telegram/-1001234567890/42` |
21
- | `claude` | `metro://claude/<user-id>/<session-id>` | `metro://claude/9bfc7af0-…/50b00d11-…` |
22
- | `codex` | `metro://codex/<user-id>/<session-id>` | `metro://codex/8119ecb1-…/01997d4b-…` |
23
- | `webhook` | `metro://webhook/<endpoint-id>` | `metro://webhook/fwaCgTKJuLAjS2K0` |
24
-
25
- Claude / Codex lines mirror the `<root>/<sub>` structure of `metro://telegram/<chat-id>/<topic-id>`: `<user-id>` (the stable account id — same across devices) plays the role of `<chat-id>`, and `<session-id>` (one conversation) plays the role of `<topic-id>`. Both segments are derived per station (see [participants](#participants) below).
26
-
27
- ## Participants
28
-
29
- Every chat station also exposes participant URIs — used as `from` on inbound/outbound events and history rows.
30
-
31
- | Kind | Pattern | Example |
32
- |--------|----------------------------------|---------------------------------------------------------------|
33
- | user | `metro://<station>/user/<id>` | `metro://discord/user/87654321` |
34
- | claude | `metro://claude/user/<orgId>` | `metro://claude/user/9bfc7af0-2117-44c5-baf2-d22ba382d065` |
35
- | codex | `metro://codex/user/<accountId>` | `metro://codex/user/8119ecb1-b05e-48db-aa80-434584439df9` |
36
- | webhook | `metro://webhook/<endpointId>` | `metro://webhook/fwaCgTKJuLAjS2K0` (line + `from` are the same — no HTTP-side user identity) |
37
-
38
- `from` and `to` on history entries are always participant URIs. Discord/Telegram inbounds set `from` to the user URI; the daemon sets `to` to the local user identity:
39
-
40
- - **Claude Code** (`$CLAUDECODE` set) — `metro://claude/user/<orgId>`. `<orgId>` is the stable Anthropic-account UUID, resolved by shelling out to `claude auth status --json`.
41
- - **Codex** (`$METRO_CODEX_RC` or `$CODEX_HOME` set) — `metro://codex/user/<accountId>`. `<accountId>` is the ChatGPT-account UUID, read from `$CODEX_HOME/auth.json` (default `~/.codex/auth.json`) at the `tokens.account_id` field. Requires `auth_mode=chatgpt`; API-key-only Codex sessions have no account id and metro will error.
42
- - **Neither** — `to` is the generic `metro://user`.
43
-
44
- Same account on any machine yields the same URI. Switching accounts via `claude auth login` / `codex login` flips the URI within ~5 s for the long-lived daemon (5 s TTL cache); one-shot CLI invocations re-resolve every run. On outbound, `from` = the same user identity; `to` = the original sender for replies/reacts (looked up from history), or the channel `line` for fresh group sends. A `fromName` field carries the display name (`@alice`, `bonustrack_`).
45
-
46
- Override with `--from=<uri>` on any write command, or set `$METRO_FROM` to pin a custom identity for the whole session.
47
-
48
- Chat lines identify a Discord channel / Telegram chat (with optional forum topic). Claude / Codex lines identify a *specific session* of a specific user (`<user-id>/<session-id>`) — posting to one re-emits the message on the daemon's stdout stream and (if configured) pushes it to the Codex app-server. They have no inherent "messages"; only events.
49
-
50
- ### Session derivation per station
51
-
52
- | Station | `<user-id>` | `<session-id>` |
53
- |----------|----------------------------|-----------------------------------------------------------------|
54
- | `claude` | `orgId` from `claude auth status --json` | `$CLAUDE_CODE_SESSION_ID` (set by Claude Code; stable across `--resume`)|
55
- | `codex` | `tokens.account_id` from `$CODEX_HOME/auth.json` | codex-rc thread id from the JSON-RPC handshake (`thread/loaded/list` → `thread/start`) |
56
-
57
- Override either segment with `METRO_USER_ID` / `METRO_USER_SESSION_ID` env vars.
58
-
59
- ### User registry
60
-
61
- The daemon persists every `(station, user-id, session)` tuple it sees to `$METRO_STATE_DIR/user-registry.json`. `metro lines` lists the recently-seen conversation URIs.
62
-
63
- ## Webhook station
64
-
65
- Receive-only HTTP endpoint for third-party services (GitHub, Intercom, Fireflies, …). Each registered endpoint is one `metro://webhook/<endpoint-id>` line.
66
-
67
- - **Register:** `metro webhook add <label> [--secret=<shared-secret>]` mints a 16-char endpoint id (96 bits of entropy, persisted to `$METRO_STATE_DIR/webhooks.json`) and prints the receiving URL. `metro webhook list` / `remove <id>` for the obvious.
68
- - **Listener:** the dispatcher binds `127.0.0.1:8420` (override with `METRO_WEBHOOK_PORT`) when ≥1 endpoint is registered. Routes `POST /wh/<endpoint-id>` to an inbound event with `payload: { headers, body }` — `body` is parsed JSON when the request `Content-Type` is JSON, raw string otherwise. `GET /wh/<endpoint-id>` returns 200 (for provider ping checks).
69
- - **Envelope:** `messageId` falls back to `X-GitHub-Delivery` / `X-Request-ID` / a generated UUID for idempotency tracking. `text` is synthesized from `X-GitHub-Event` / `X-Intercom-Topic` plus method + path for at-a-glance routing; consumers narrow on `payload.body` for full event details.
70
- - **HMAC verification:** if `--secret` was set on `metro webhook add`, requests must include a matching `X-Hub-Signature-256: sha256=<hex>` (GitHub/Intercom format) — mismatches are rejected with 401 before reaching the stream.
71
- - **Public reachability:** provided by a Cloudflare named tunnel — see [Tunneling](#tunneling) below. Without one, the listener stays loopback-only (useful for `curl` testing).
72
-
73
- ## Tunneling
74
-
75
- Webhook providers need a public URL. Metro integrates with **Cloudflare named tunnels** (free, stable, account-scoped):
76
-
77
- ```bash
78
- cloudflared tunnel login # one-time OAuth (browser)
79
- metro tunnel setup metro webhook.yourdomain.com # creates the tunnel + DNS route
80
- metro # daemon spawns `cloudflared tunnel run`
81
- ```
82
-
83
- After setup, `metro webhook list` prints `https://webhook.yourdomain.com/wh/<id>` for each endpoint. The URL is stable across restarts (bound to the tunnel UUID in `~/.cloudflared/<uuid>.json`, not the cloudflared process). Tunnel config persists at `$METRO_STATE_DIR/tunnel.json`. Without setup, endpoints fall back to `http://127.0.0.1:8420/wh/<id>` (local-only, useful for curl testing).
84
-
85
- ## Message addressing
86
-
87
- Messages on chat lines are referenced by **line + message id** (two args), not as part of the URI. So:
88
-
89
- ```bash
90
- metro call discord send '{"line":"metro://discord/123","text":"ack","replyTo":"4567"}'
91
- metro call discord edit '{"line":"metro://discord/123","messageId":"9876","text":"fixed typo"}'
92
- metro call telegram react '{"line":"metro://telegram/-100/42","messageId":"4567","emoji":"👍"}'
93
- ```
94
-
95
- ## Properties
96
-
97
- - **Stable**: a Line is valid for the lifetime of the scope.
98
- - **Self-describing**: the station name is encoded; the dispatcher routes by station prefix.
99
- - **Persistable**: safe as a JSON key on disk (used by `lines.json`).
100
- - **Branded**: TypeScript type `Line` prevents mixing with arbitrary strings.
101
-
102
- ## API
103
-
104
- ```ts
105
- import { Line } from './lines.js'; // value namespace + type
106
-
107
- const l: Line = Line.discord('1234567890'); // typed Line
108
- Line.parse(l); // { station: 'discord', path: ['1234567890'] } | null
109
- Line.station(l); // 'discord'
110
- Line.claude(orgId, sessionId); // metro://claude/<orgId>/<sessionId>
111
- Line.codex(accountId, threadId); // metro://codex/<accountId>/<threadId>
112
- Line.parseClaude(l); // { userId, sessionId } | null
113
- Line.parseCodex(l); // { userId, sessionId } | null
114
- Line.webhook(endpointId); // metro://webhook/<endpointId>
115
- Line.parseWebhook(l); // string | null (the endpoint id)
116
- Line.user(station, id); // metro://<station>/user/<id>
117
- Line.isLocal(l); // true for any metro://{claude,codex}/...
118
- ```
119
-
120
- ## Adding a new station
121
-
122
- A "station" in this URI scheme is just a namespace — anything a train emits with
123
- `metro://<name>/<path>` works. There is no required registration with core:
124
-
125
- 1. Pick a lowercase station name (`slack`, `matrix`, …).
126
- 2. In your train, emit envelopes with `line: "metro://<name>/<id>"`.
127
- 3. Optionally add a `Line.<station>(...)` formatter to `src/lines.ts` for type-safe construction.
@@ -1,32 +0,0 @@
1
- # Example train
2
-
3
- `telegram.ts` is a starting point, not runtime code. Copy to
4
- `~/.metro/trains/<name>.ts`, edit, save, restart the daemon:
5
-
6
- ```
7
- cp telegram.ts ~/.metro/trains/telegram.ts
8
- echo 'TELEGRAM_BOT_TOKEN=…' >> ~/.metro/.env
9
- metro
10
- ```
11
-
12
- For a Discord port: swap the API base + auth header (`Bot $TOKEN`), install
13
- `discord.js` for the gateway, and emit the same envelope shape — the
14
- stdin/stdout protocol below is platform-independent. Action names and payload
15
- shapes are entirely up to you.
16
-
17
- ## Protocol (JSON lines over stdio)
18
-
19
- ```
20
- metro ─── stdin (one JSON line per action call) ──> train
21
- <── stdout (one JSON line per event OR response) ── train
22
- ```
23
-
24
- - **Inbound event** (train → metro): `{ kind, station, line, from, from_name?, message_id?, line_name?, reply_to?, text?, is_private?, payload? }` — snake_case on the wire. Metro mints `id` + `display` if absent and translates to camelCase for `history.jsonl` / the broker (`HistoryEntry` in `src/history.ts`).
25
- - **Call** (metro → train): `{ "op": "call", "id": "req_abc", "action": "send", "args": {...} }`.
26
- - **Response** (train → metro): `{ "op": "response", "id": "req_abc", "result": {...} }` or `{ ..., "error": "..." }`.
27
-
28
- Anything on stdout without an `op` is treated as an inbound event.
29
-
30
- ## Lifecycle
31
-
32
- Metro scans `~/.metro/trains/*.{ts,js,mjs}` at boot — one subprocess per file. Crashed trains restart with backoff (1s → 5s → 30s, up to 5 consecutive failures). `metro trains list` shows state. Restart the daemon to pick up edits. `~/.metro/.env` is auto-loaded into each train's `process.env`.
@@ -1,121 +0,0 @@
1
- /**
2
- * Reference train — Telegram (long-polling, no npm deps). Pattern is identical for any platform.
3
- *
4
- * Setup:
5
- * cp <this-file> ~/.metro/trains/telegram.ts
6
- * echo 'TELEGRAM_BOT_TOKEN=…' >> ~/.metro/.env
7
- *
8
- * Discord-flavoured port: swap the API base + `tg()` helper for `https://discord.com/api/v10`
9
- * with `Authorization: Bot $TOKEN`, install `discord.js` for the gateway, and emit the same
10
- * envelope shape. The stdin/stdout protocol is platform-independent. Discord adds `sticker_ids`
11
- * support on `sendMessage` payloads — preserve those in `payload` so downstream tooling sees them.
12
- */
13
-
14
- const TOKEN = process.env.TELEGRAM_BOT_TOKEN;
15
- if (!TOKEN) { process.stderr.write('TELEGRAM_BOT_TOKEN unset\n'); process.exit(2); }
16
- const API = `https://api.telegram.org/bot${TOKEN}`;
17
-
18
- const emit = (e: unknown): void => void process.stdout.write(JSON.stringify(e) + '\n');
19
- const respond = (id: string, body: { result?: unknown; error?: string }): void =>
20
- void process.stdout.write(JSON.stringify({ op: 'response', id, ...body }) + '\n');
21
- const mintId = (): string => `msg_${Math.random().toString(36).slice(2, 10)}`;
22
- /** Self URI for outbound emissions. Set by the supervisor; trains echo their own sends so mobile + tail see them. */
23
- const SELF_URI = process.env.METRO_SELF_URI ?? '';
24
- const emitOutbound = (line: string, messageId: string, text: string, replyTo?: string): void => emit({
25
- kind: 'outbound', id: mintId(), ts: new Date().toISOString(),
26
- station: 'telegram', line, from: SELF_URI, to: line, message_id: messageId, text, reply_to: replyTo,
27
- });
28
-
29
- async function tg<T>(method: string, body: unknown, timeoutMs = 30_000): Promise<T> {
30
- const res = await fetch(`${API}/${method}`, {
31
- method: 'POST', headers: { 'Content-Type': 'application/json' },
32
- body: JSON.stringify(body), signal: AbortSignal.timeout(timeoutMs),
33
- });
34
- const json = (await res.json()) as { ok: boolean; description?: string; result?: T };
35
- if (!json.ok) throw new Error(`telegram ${method}: ${json.description ?? 'unknown'}`);
36
- return json.result as T;
37
- }
38
-
39
- type TgMsg = {
40
- message_id: number; date: number;
41
- chat: { id: number; type: string; title?: string; first_name?: string };
42
- from?: { id: number; username?: string; first_name?: string; is_bot?: boolean };
43
- text?: string; caption?: string;
44
- message_thread_id?: number; is_topic_message?: boolean;
45
- photo?: unknown[]; document?: { file_name?: string };
46
- };
47
-
48
- function envelope(m: TgMsg): Record<string, unknown> {
49
- const tags = [...(m.photo?.length ? ['[image]'] : []), ...(m.document ? [`[file: ${m.document.file_name ?? 'doc'}]`] : [])];
50
- const text = [m.text ?? m.caption, ...tags].filter(Boolean).join(' ');
51
- const topicId = m.is_topic_message ? m.message_thread_id : undefined;
52
- const line = topicId !== undefined ? `metro://telegram/${m.chat.id}/${topicId}` : `metro://telegram/${m.chat.id}`;
53
- return {
54
- kind: 'inbound', id: mintId(), ts: new Date(m.date * 1000).toISOString(),
55
- station: 'telegram', line,
56
- line_name: topicId === undefined ? (m.chat.title ?? m.chat.first_name ?? undefined) : undefined,
57
- from: `metro://telegram/user/${m.from?.id ?? 'unknown'}`,
58
- from_name: m.from?.username ? `@${m.from.username}` : m.from?.first_name,
59
- message_id: String(m.message_id), text, payload: m, is_private: m.chat.type === 'private',
60
- };
61
- }
62
-
63
- const targetOf = (line: string): { chatId: number; topicId?: number } => {
64
- const m = line.match(/^metro:\/\/telegram\/(-?\d+)(?:\/(\d+))?/);
65
- if (!m) throw new Error(`bad telegram line: ${line}`);
66
- return { chatId: Number(m[1]), topicId: m[2] ? Number(m[2]) : undefined };
67
- };
68
-
69
- type CallMsg = { op: 'call'; id: string; action: string; args: Record<string, unknown> };
70
- async function handleCall({ id, action, args }: CallMsg): Promise<void> {
71
- try {
72
- if (action === 'send') {
73
- const { line, text, replyTo } = args as { line: string; text: string; replyTo?: string };
74
- const { chatId, topicId } = targetOf(line);
75
- const body: Record<string, unknown> = { chat_id: chatId, text };
76
- if (topicId !== undefined) body.message_thread_id = topicId;
77
- if (replyTo) body.reply_parameters = { message_id: Number(replyTo) };
78
- const sent = await tg<{ message_id: number }>('sendMessage', body);
79
- const sentId = String(sent.message_id);
80
- emitOutbound(line, sentId, text, replyTo);
81
- respond(id, { result: { messageId: sentId } });
82
- } else if (action === 'react') {
83
- const { line, messageId, emoji } = args as { line: string; messageId: string; emoji: string };
84
- await tg('setMessageReaction', {
85
- chat_id: targetOf(line).chatId, message_id: Number(messageId),
86
- reaction: emoji ? [{ type: 'emoji', emoji }] : [],
87
- });
88
- respond(id, { result: { ok: true } });
89
- } else respond(id, { error: `unknown action '${action}' (have: send, react)` });
90
- } catch (err) { respond(id, { error: (err as Error).message }); }
91
- }
92
-
93
- let buf = '';
94
- process.stdin.setEncoding('utf8');
95
- process.stdin.on('data', chunk => {
96
- buf += chunk;
97
- let nl;
98
- while ((nl = buf.indexOf('\n')) !== -1) {
99
- const line = buf.slice(0, nl).trim();
100
- buf = buf.slice(nl + 1);
101
- if (!line) continue;
102
- try { const msg = JSON.parse(line); if (msg.op === 'call') void handleCall(msg); }
103
- catch (err) { process.stderr.write(`bad stdin line: ${(err as Error).message}\n`); }
104
- }
105
- });
106
-
107
- let offset = 0;
108
- process.stderr.write('telegram train ready\n');
109
- while (true) {
110
- try {
111
- const updates = await tg<{ update_id: number; message?: TgMsg }[]>('getUpdates',
112
- { offset, timeout: 25, allowed_updates: ['message'] }, 60_000);
113
- for (const u of updates) {
114
- offset = u.update_id + 1;
115
- if (u.message && !u.message.from?.is_bot) emit(envelope(u.message));
116
- }
117
- } catch (err) {
118
- process.stderr.write(`telegram poll error: ${(err as Error).message}\n`);
119
- await new Promise(r => setTimeout(r, 2_000));
120
- }
121
- }
@@ -1,110 +0,0 @@
1
- ---
2
- name: metro
3
- description: Run the metro train-supervisor in this session — launch `metro` in the background, watch its stdout for inbound JSON events, and act on each. Use when the user asks to start/run/launch metro, when you see JSON lines on stdout shaped `{"station":"…","line":"metro://…","from":"…","to":"…","text":"…"}`, or when handling a chat/webhook reply/edit/react/send via `metro call`.
4
- ---
5
-
6
- # Metro — event-interception wire
7
-
8
- Metro is the wire between this session and any number of platforms. Platform code lives in
9
- **trains** under `~/.metro/trains/` — single TS files that you (the agent) write, edit, or
10
- replace on demand.
11
-
12
- ## What metro does
13
-
14
- 1. Spawns each file in `~/.metro/trains/*.{ts,js,mjs}` as a long-running Bun subprocess.
15
- 2. Multiplexes their stdout (JSON lines) into one unified event stream on metro's stdout.
16
- 3. Routes `metro call <train> <action> <args>` requests back to the matching train's stdin.
17
- 4. Two builtin event sources stay in core: **webhooks** (HTTP receiver) and **messenger** (in-daemon chat).
18
-
19
- ## Starting metro
20
-
21
- **Claude Code:** `Bash(command: "metro", run_in_background: true)`, then attach `Monitor` to its stdout.
22
-
23
- **Codex:** `shell(command: "METRO_CODEX_RC=ws://127.0.0.1:8421 metro", run_in_background: true)` — metro pushes each event via JSON-RPC `turn/start`. The user must run a Codex daemon + TUI on the same WebSocket URL (`codex app-server --listen ws://127.0.0.1:8421` + `codex --remote ws://127.0.0.1:8421`, type "hi" once to seed a thread).
24
-
25
- `metro doctor` reports trains found, deps installed, dispatcher running, codex-rc, skill install.
26
-
27
- ## Envelope
28
-
29
- Every event on stdout is a single JSON line:
30
-
31
- ```json
32
- {"id":"msg_…","ts":"2026-05-17T18:00:00Z","station":"discord","line":"metro://discord/123","line_name":"infra","from":"metro://discord/user/456","from_name":"alice","to":"metro://claude/user/9bfc…","text":"hi","message_id":"789","reply_to":"…","payload":{…}}
33
- ```
34
-
35
- Wire fields are `snake_case` on the train protocol; the dispatcher translates to camelCase for `history.jsonl`. Trains supply `line`, `from`, `to`, `text`, plus `payload` (the platform's native message). Metro mints `id` + `display` if missing.
36
-
37
- **No `kind` field.** Direction is derived: `Line.isLocal(from)` → outbound (📤), else inbound (📩). Reactions and edits are train-specific — encode them in `text` (e.g. `[react 👀]`) plus whatever you need in `payload`.
38
-
39
- ## Outbound: `metro call <train> <action> <args>`
40
-
41
- ```
42
- metro call discord send '{"line":"metro://discord/123","text":"hi","replyTo":"789"}'
43
- metro call telegram react '{"line":"metro://telegram/-100/1","messageId":"42","emoji":"👀"}'
44
- metro call discord edit '{"line":"metro://discord/123","messageId":"999","text":"new"}'
45
- ```
46
-
47
- `[args]` can be JSON, `@path/to/args.json`, `-` (stdin), or a bare string. Action names are whatever the train exposes — metro core knows nothing about them.
48
-
49
- ## Writing a new train
50
-
51
- 1. Start from `node_modules/@stage-labs/metro/examples/telegram.ts`.
52
- 2. Copy → `~/.metro/trains/<name>.ts` and edit. Keep the inbound envelope shape and the `op:"call"` → `op:"response"` protocol.
53
- 3. Deps (if needed): `cd ~/.metro && bun add <pkg>`. Credentials: `echo 'FOO_TOKEN=…' >> ~/.metro/.env`.
54
- 4. Restart the metro daemon (or just `metro trains restart <name>`) to pick up the new train.
55
-
56
- Trains are throwaway — if the user asks for new functionality, rewrite the train rather than adding glue in core.
57
-
58
- ## First-run setup (once per machine)
59
-
60
- ```
61
- mkdir -p ~/.metro && cd ~/.metro && bun init -y
62
- cp node_modules/@stage-labs/metro/examples/telegram.ts ~/.metro/trains/
63
- echo 'TELEGRAM_BOT_TOKEN=…' >> ~/.metro/.env
64
- metro setup skill # optional — installs this SKILL.md into ~/.claude / ~/.codex
65
- metro
66
- ```
67
-
68
- ## Detecting "is this for me?"
69
-
70
- Trains should set `is_private: true` for DMs. For groups, narrow on `payload`:
71
-
72
- - **discord** — DM when `payload.guildId == null`; otherwise look at `payload.mentions.users`.
73
- - **telegram** — DM when `payload.chat.type === 'private'`; otherwise look at `payload.entities` mentions.
74
- - **webhook** — every POST is for you by design. Route on `payload.headers['x-github-event']` / `x-intercom-topic`.
75
- - **messenger** — every event on the messenger line is between the user and the agent. No filtering needed.
76
-
77
- ## CLI cheat sheet
78
-
79
- ```
80
- metro # start the daemon
81
- metro trains [list] # list trains + state
82
- metro trains new <name> # scaffold ~/.metro/trains/<name>.ts
83
- metro trains restart <name> # kill + respawn a train (resets backoff)
84
- metro call <train> <action> <args> # forward an action call
85
- metro tail [--as=<user-uri>] [--follow] # subscribe to the event log
86
- metro history [--limit=N] [--line=…] [--from=…] [--text=…] [--since=…]
87
- metro webhook add <label> # register an HTTP receive endpoint
88
- metro tunnel setup <name> <hostname> # configure a Cloudflare named tunnel
89
- metro doctor # health check
90
- ```
91
-
92
- ## Webhooks (builtin source)
93
-
94
- Webhooks stay in core because they're shared HTTP infra (one Cloudflare tunnel routes many endpoints). `metro webhook add <label>` issues an endpoint id; the full URL is `https://<tunnel-host>/wh/<id>` (or `http://127.0.0.1:8420/wh/<id>` locally). Events arrive on `metro://webhook/<id>`.
95
-
96
- ## Messenger (builtin source)
97
-
98
- Direct chat between the agent and the device. Five endpoints — all under the same bearer-token guard as `/api/state`:
99
-
100
- - `POST /api/messenger/send {text?, attachments?, as?}` — emit an envelope on `metro://messenger/owner`. Either text or attachments required.
101
- - `POST /api/messenger/react {messageId, emoji, as?}` — emit a slim `payload.reactTo` envelope. If the sender already has an active reaction with this emoji on this target, emits `{removed: true}` instead — reactions are toggleable.
102
- - `POST /api/messenger/upload` — raw binary upload (≤ 25 MiB, body = file bytes, `Content-Type` + optional `X-Filename` headers). Returns `{id, url, kind, mime, size, name?}`; files land in `$METRO_STATE_DIR/messenger-uploads/`.
103
- - `GET /api/messenger/files/<name>` — stream a stored upload. Accepts `Authorization: Bearer …` header *or* `?token=…` query (for `<img>` / `<audio>` tags). Content-Type derived from extension.
104
- - `POST /api/messenger/register {pushToken}` — store an Expo push token so agent replies push to the device.
105
-
106
- The mobile + web companion apps use these for chat with the agent. The envelope is the standard slim shape — no `kind` / `emoji` fields, direction derived from `Line.isLocal(from)`.
107
-
108
- ## Crashes
109
-
110
- If a train crashes, metro restarts it with backoff (1s → 5s → 30s, then gives up after 5 consecutive failures). Use `metro trains` to check state.