@stage-labs/metro 0.1.0-beta.2 → 0.1.0-beta.200

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 (391) hide show
  1. package/dist/api.js +11 -0
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +163 -0
  4. package/dist/cli.js +77 -567
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +43 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +44 -0
  11. package/dist/provider-flags.js +9 -0
  12. package/dist/route.js +58 -0
  13. package/dist/runtime-install.js +97 -0
  14. package/dist/runtime.js +74 -0
  15. package/dist/serve.js +158 -0
  16. package/dist/service.js +243 -0
  17. package/dist/tail.js +102 -144
  18. package/dist/update.js +45 -0
  19. package/dist/version.js +97 -0
  20. package/package.json +14 -23
  21. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  22. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
  23. package/runtime/marketplace/plugin/.mcp.json +1 -0
  24. package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
  25. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
  26. package/runtime/marketplace/plugin/bin/policy.mjs +97 -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 +24 -0
  30. package/runtime/node_modules/@metro-labs/core/package.json +47 -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 +119 -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 +24 -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 +91 -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 +227 -0
  39. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +74 -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-files.ts +41 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  44. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  47. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +61 -0
  48. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  49. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +89 -0
  50. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +123 -0
  51. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  52. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  53. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  54. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  55. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +119 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +91 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +100 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +252 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +61 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +91 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +168 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +35 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +128 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +86 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/approvals/needs.ts +2 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +149 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +221 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +12 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +370 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +90 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +78 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/channels/react-note.ts +13 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/channels/reply-id.ts +1 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +355 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +82 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +283 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +271 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +306 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +176 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +159 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +135 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +158 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +166 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +29 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +348 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +358 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +123 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +278 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +194 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +137 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +223 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +299 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +15 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +156 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +226 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +90 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +137 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  174. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
  176. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +83 -0
  177. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  178. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  179. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  180. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  181. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  182. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +202 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  187. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +225 -0
  188. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +333 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +61 -0
  190. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/api.ts +43 -0
  191. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  192. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/move.ts +165 -0
  193. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  194. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  195. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  196. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  197. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  198. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  199. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +365 -0
  200. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +208 -0
  201. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +71 -0
  202. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  203. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  204. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  205. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  206. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  207. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  208. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  209. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  210. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +128 -0
  211. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +134 -0
  212. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
  213. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +225 -0
  214. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +288 -0
  215. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  216. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  217. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
  218. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +51 -0
  219. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  220. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  221. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  222. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  223. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  224. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  225. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  226. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  227. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  228. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  229. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  230. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +48 -0
  231. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  232. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  233. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  234. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +148 -0
  235. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +130 -0
  236. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +61 -0
  237. package/runtime/node_modules/@metro-labs/discord-bot/package.json +26 -0
  238. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +117 -0
  239. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +240 -0
  240. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  241. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  242. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +174 -0
  243. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  244. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  245. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +122 -0
  246. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  247. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  248. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  249. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +22 -0
  250. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  251. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +1 -0
  252. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  253. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  254. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +138 -0
  255. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  256. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  257. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  258. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  259. package/runtime/node_modules/@metro-labs/outlook/package.json +25 -0
  260. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +107 -0
  261. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +20 -0
  262. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
  263. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
  264. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
  265. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
  266. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +141 -0
  267. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
  268. package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
  269. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
  270. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  271. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
  272. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
  273. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
  274. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
  275. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
  276. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  277. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -0
  278. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  279. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  280. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +256 -0
  281. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  282. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  283. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +128 -0
  284. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  285. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  286. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
  287. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +7 -0
  288. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  289. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  290. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  291. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  292. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  293. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  294. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +27 -0
  295. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +6 -0
  296. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  297. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  298. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +119 -0
  299. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +246 -0
  300. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  301. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +173 -0
  302. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  303. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +95 -0
  304. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  305. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +65 -0
  306. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +20 -0
  307. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
  308. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  309. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +1 -0
  310. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  311. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +106 -0
  312. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +134 -0
  313. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  314. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  315. package/runtime/node_modules/@metro-labs/threema/src/files.ts +58 -0
  316. package/runtime/node_modules/@metro-labs/threema/src/format.ts +169 -0
  317. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  318. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  319. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
  320. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  321. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  322. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  323. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  324. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  325. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  326. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  327. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  328. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  329. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  330. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +213 -0
  331. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  332. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  333. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +388 -0
  334. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  335. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +83 -0
  336. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +40 -0
  337. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +7 -0
  338. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  339. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  340. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  341. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  342. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +28 -0
  343. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  344. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  345. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  346. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  347. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +24 -0
  348. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +94 -0
  349. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +5 -0
  350. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +1 -0
  351. package/runtime/node_modules/@metro-labs/xmtp/package.json +34 -0
  352. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +114 -0
  353. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  354. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +101 -0
  355. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +118 -0
  356. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +244 -0
  357. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  358. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  359. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  360. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +316 -0
  361. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +69 -0
  362. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  363. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  364. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +47 -0
  365. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +122 -0
  366. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +139 -0
  367. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  368. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  369. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  370. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  371. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  372. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +17 -0
  373. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +79 -0
  374. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +198 -0
  375. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +88 -0
  376. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +98 -0
  377. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +51 -0
  378. package/runtime/runtime.json +3 -0
  379. package/runtime/server.ts +1 -0
  380. package/runtime/stations.json +37 -0
  381. package/runtime/trains/.keep +0 -0
  382. package/LICENSE +0 -21
  383. package/README.md +0 -78
  384. package/dist/channels/discord.js +0 -148
  385. package/dist/channels/telegram.js +0 -137
  386. package/dist/lib/address.js +0 -21
  387. package/dist/lib/codex-rc.js +0 -274
  388. package/dist/lib/dotenv.js +0 -31
  389. package/dist/log.js +0 -12
  390. package/dist/paths.js +0 -45
  391. package/skills/metro/SKILL.md +0 -122
package/dist/tail.js CHANGED
@@ -1,161 +1,119 @@
1
- // Standalone inbound stream. Polls Telegram + connects to Discord, prints
2
- // one JSON line per inbound message on stdout. Designed to be launched by
3
- // an agent and observed via Bash+Monitor (Claude Code) or unified_exec
4
- // polling (Codex).
5
- //
6
- // On every inbound: fires a 👀 reaction and starts a typing indicator that
7
- // refreshes until the agent replies (signaled by `metro reply` touching
8
- // .typing-stop/<key>) or the 60s safety cap is hit.
9
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
10
- import { join } from 'node:path';
11
- import pkg from '../package.json' with { type: 'json' };
12
- import * as discord from './channels/discord.js';
13
- import * as telegram from './channels/telegram.js';
14
- import { tg } from './channels/telegram.js';
15
- import { CodexRC } from './lib/codex-rc.js';
16
- import { configuredPlatforms, loadMetroEnv, STATE_DIR, requireConfiguredPlatform } from './paths.js';
17
- import { errMsg, log } from './log.js';
18
- loadMetroEnv();
19
- const platforms = configuredPlatforms();
20
- requireConfiguredPlatform(platforms);
21
- // Telegram allows only one getUpdates poller per bot token. If another
22
- // `metro` instance is already running, exit cleanly instead of fighting
23
- // (409 spam). Stale lockfiles (PID dead) are reclaimed.
24
- const LOCK_FILE = join(STATE_DIR, '.tail-lock');
25
- function processIsAlive(pid) {
26
- try {
27
- process.kill(pid, 0);
28
- return true;
29
- }
30
- catch {
31
- return false;
1
+ import { localAgent } from './local.js';
2
+ import { assertAgentId, localUrl } from './runtime.js';
3
+ const BACKOFF_MS = [1_000, 2_000, 5_000];
4
+ const STALL_MS = 90_000;
5
+ export const backoffAt = (attempt) => BACKOFF_MS[Math.min(attempt, BACKOFF_MS.length - 1)] ?? 5_000;
6
+ export function sseDataLines(buffer, chunk) {
7
+ const lines = (buffer + chunk).split('\n');
8
+ const rest = lines.pop() ?? '';
9
+ const events = [];
10
+ for (const raw of lines) {
11
+ const line = raw.endsWith('\r') ? raw.slice(0, -1) : raw;
12
+ if (!line.startsWith('data:'))
13
+ continue;
14
+ const value = line.slice(5);
15
+ events.push(value.startsWith(' ') ? value.slice(1) : value);
32
16
  }
17
+ return { rest, events };
33
18
  }
34
- if (existsSync(LOCK_FILE)) {
35
- const pid = Number(readFileSync(LOCK_FILE, 'utf8').trim());
36
- if (Number.isInteger(pid) && pid > 0 && processIsAlive(pid)) {
37
- log.info({ pid }, 'another `metro` instance is already polling; exiting');
38
- process.exit(0);
39
- }
19
+ function keySource(agentId) {
20
+ const fromEnv = process.env.METRO_AGENT_KEY?.trim();
21
+ if (fromEnv !== undefined && fromEnv !== '')
22
+ return { resolve: () => Promise.resolve(fromEnv), fixed: true };
23
+ return {
24
+ resolve: () => {
25
+ const agent = localAgent();
26
+ if (agent === null || (agentId !== undefined && agent.id !== agentId))
27
+ throw new Error(`no agent ${agentId === undefined ? '' : `${agentId} `}on this machine — start metro serve, or set METRO_AGENT_KEY`);
28
+ return Promise.resolve(agent.key);
29
+ },
30
+ fixed: false,
31
+ };
32
+ }
33
+ const armStall = (control) => setTimeout(() => {
34
+ control.abort();
35
+ }, STALL_MS);
36
+ async function connect(key, signal) {
40
37
  try {
41
- unlinkSync(LOCK_FILE);
38
+ return await fetch(`${localUrl()}/api/tail`, {
39
+ headers: { authorization: `Bearer ${key}` },
40
+ signal,
41
+ });
42
+ }
43
+ catch {
44
+ return null;
42
45
  }
43
- catch { }
44
46
  }
45
- writeFileSync(LOCK_FILE, String(process.pid));
46
- function releaseLock() {
47
+ async function pump(body, control, stall) {
48
+ const reader = body.getReader();
49
+ const decoder = new TextDecoder();
50
+ let buffer = '';
47
51
  try {
48
- if (existsSync(LOCK_FILE) && readFileSync(LOCK_FILE, 'utf8').trim() === String(process.pid)) {
49
- unlinkSync(LOCK_FILE);
52
+ for (;;) {
53
+ const { done, value } = await reader.read();
54
+ if (done)
55
+ return 'ended';
56
+ clearTimeout(stall.timer);
57
+ stall.timer = armStall(control);
58
+ const out = sseDataLines(buffer, decoder.decode(value, { stream: true }));
59
+ buffer = out.rest;
60
+ for (const event of out.events)
61
+ process.stdout.write(`${event}\n`);
50
62
  }
51
63
  }
52
- catch { }
64
+ catch {
65
+ return 'ended';
66
+ }
53
67
  }
54
- process.on('exit', releaseLock);
55
- const TYPING_DIR = join(STATE_DIR, '.typing-stop');
56
- const TYPING_REFRESH_MS = 4_000;
57
- const TYPING_MAX_MS = 60_000;
58
- mkdirSync(TYPING_DIR, { recursive: true });
59
- // Codex push channel. Set METRO_CODEX_RC to the codex app-server URL
60
- // (typically `ws://127.0.0.1:8421` matching `codex app-server --listen
61
- // ws://127.0.0.1:8421`) to inject each inbound into the agent's history
62
- // via JSON-RPC `turn/start`. Codex's TUI `--remote` flag only accepts
63
- // ws://, so the daemon, the TUI, and metro must all share the same URL.
64
- // Unset → metro behaves exactly as before; stdout emit always runs first
65
- // so Claude Code Monitor users are unaffected.
66
- const codexRC = process.env.METRO_CODEX_RC ? new CodexRC(process.env.METRO_CODEX_RC, pkg.version) : null;
67
- codexRC?.start();
68
- const emit = (line) => {
69
- const json = JSON.stringify(line);
70
- process.stdout.write(`${json}\n`);
71
- codexRC?.push(json);
72
- };
73
- const typingActive = new Map();
74
- const typingKey = (platform, chat) => `${platform}_${chat}`;
75
- function fireTyping(platform, chat) {
76
- if (platform === 'telegram') {
77
- void tg('sendChatAction', { chat_id: chat, action: 'typing' }).catch(err => log.warn({ err: errMsg(err) }, 'telegram typing failed'));
68
+ async function streamOnce(key) {
69
+ const control = new AbortController();
70
+ const stall = { timer: armStall(control) };
71
+ try {
72
+ const res = await connect(key, control.signal);
73
+ if (res === null)
74
+ return 'unreachable';
75
+ if (res.status === 401)
76
+ return 'unauthorized';
77
+ if (!res.ok || res.body === null)
78
+ return 'unreachable';
79
+ return await pump(res.body, control, stall);
78
80
  }
79
- else {
80
- void discord.sendTyping(chat).catch(err => log.warn({ err: errMsg(err) }, 'discord typing failed'));
81
+ finally {
82
+ clearTimeout(stall.timer);
81
83
  }
82
84
  }
83
- function startTyping(platform, chat) {
84
- const k = typingKey(platform, chat);
85
- typingActive.set(k, { platform, chat, started: Date.now() });
86
- // Clear any stale stop signal so the new typing actually fires.
87
- const stopFile = join(TYPING_DIR, k);
88
- if (existsSync(stopFile)) {
89
- try {
90
- unlinkSync(stopFile);
91
- }
92
- catch { }
93
- }
94
- fireTyping(platform, chat);
85
+ function note(state, message) {
86
+ if (state.last === message)
87
+ return;
88
+ state.last = message;
89
+ process.stderr.write(`metro: ${message}\n`);
95
90
  }
96
- setInterval(() => {
97
- const now = Date.now();
98
- for (const [k, e] of typingActive) {
99
- const stopFile = join(TYPING_DIR, k);
100
- if (existsSync(stopFile)) {
101
- try {
102
- unlinkSync(stopFile);
103
- }
104
- catch { }
105
- typingActive.delete(k);
106
- continue;
107
- }
108
- if (now - e.started > TYPING_MAX_MS) {
109
- typingActive.delete(k);
110
- continue;
111
- }
112
- fireTyping(e.platform, e.chat);
91
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
92
+ async function settle(end, source, key, state) {
93
+ if (end === 'unauthorized') {
94
+ if (source.fixed)
95
+ throw new Error('the key in METRO_AGENT_KEY was refused by the local daemon');
96
+ note(state, 'key refused — fetching the current one');
97
+ return source.resolve();
113
98
  }
114
- }, TYPING_REFRESH_MS);
115
- if (platforms.telegram) {
116
- const me = await telegram.getMe();
117
- log.info({ bot: `@${me.username}` }, 'telegram ready');
118
- telegram.onInbound(m => {
119
- void tg('setMessageReaction', {
120
- chat_id: m.chat_id,
121
- message_id: m.message_id,
122
- reaction: [{ type: 'emoji', emoji: '👀' }],
123
- }).catch(err => log.warn({ err: errMsg(err) }, 'telegram auto-react failed'));
124
- const chat = String(m.chat_id);
125
- startTyping('telegram', chat);
126
- emit({ platform: 'telegram', to: `telegram:${chat}/${m.message_id}`, text: m.text });
127
- });
128
- void telegram.startPolling();
99
+ if (end === 'unreachable')
100
+ note(state, `no metro daemon on ${localUrl()} — run 'metro serve'`);
101
+ else
102
+ note(state, 'stream ended — reconnecting');
103
+ return key;
129
104
  }
130
- if (platforms.discord) {
131
- await discord.startGateway();
132
- const me = await discord.getMe();
133
- log.info({ bot: me.username }, 'discord ready');
134
- discord.onInbound(m => {
135
- void discord
136
- .setReaction(m.channel_id, m.message_id, '👀')
137
- .catch(err => log.warn({ err: errMsg(err) }, 'discord auto-react failed'));
138
- startTyping('discord', m.channel_id);
139
- emit({ platform: 'discord', to: `discord:${m.channel_id}/${m.message_id}`, text: m.text });
140
- });
141
- }
142
- // `process.on('exit', releaseLock)` above runs whenever process.exit is
143
- // called. We also `await discord.shutdownGateway()` here so the bot flips
144
- // offline immediately on SIGTERM / SIGINT instead of waiting ~45s for the
145
- // gateway's missed-heartbeat timeout. SIGKILL bypasses this (nothing we can
146
- // do); the lockfile auto-reclaims on the next start either way.
147
- let shuttingDown = false;
148
- async function shutdown() {
149
- if (shuttingDown)
150
- return;
151
- shuttingDown = true;
152
- codexRC?.stop();
153
- if (platforms.discord) {
154
- await discord.shutdownGateway().catch(err => log.warn({ err: errMsg(err) }, 'discord shutdown failed'));
105
+ export async function tailEvents(argv) {
106
+ const agentId = argv[0] === undefined ? undefined : assertAgentId(argv[0]);
107
+ const source = keySource(agentId);
108
+ let key = await source.resolve();
109
+ const state = { last: '' };
110
+ let attempt = 0;
111
+ for (;;) {
112
+ const end = await streamOnce(key);
113
+ if (end === 'ended')
114
+ attempt = 0;
115
+ key = await settle(end, source, key, state);
116
+ await delay(backoffAt(attempt));
117
+ attempt += 1;
155
118
  }
156
- process.exit(0);
157
119
  }
158
- process.stdin.on('end', shutdown);
159
- process.stdin.on('close', shutdown);
160
- process.on('SIGINT', shutdown);
161
- process.on('SIGTERM', shutdown);
package/dist/update.js ADDED
@@ -0,0 +1,45 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { currentVersion, isNewer, PACKAGE_NAME, publishedVersion, } from './version.js';
3
+ export function managerFor(location) {
4
+ return location.includes('/.bun/') ? 'bun' : 'npm';
5
+ }
6
+ export function installArgs(manager, version) {
7
+ const spec = `${PACKAGE_NAME}@${version}`;
8
+ return manager === 'bun'
9
+ ? ['add', '--global', spec]
10
+ : ['install', '--global', spec];
11
+ }
12
+ export const checkReport = (current, latest) => ({
13
+ current,
14
+ latest,
15
+ newer: latest !== '' && isNewer(latest, current),
16
+ });
17
+ export async function updateCheck() {
18
+ return checkReport(currentVersion(), await publishedVersion());
19
+ }
20
+ export async function update(argv = []) {
21
+ if (argv.includes('--check')) {
22
+ process.stdout.write(`${JSON.stringify(await updateCheck())}\n`);
23
+ return 0;
24
+ }
25
+ const current = currentVersion();
26
+ const newest = await publishedVersion();
27
+ if (newest === '') {
28
+ process.stderr.write('npm published no version this CLI understands\n');
29
+ return 1;
30
+ }
31
+ if (!isNewer(newest, current)) {
32
+ process.stderr.write(`metro ${current} is the newest published version\n`);
33
+ return 0;
34
+ }
35
+ const manager = managerFor(import.meta.url);
36
+ const args = installArgs(manager, newest);
37
+ process.stderr.write(`Updating metro ${current} to ${newest} with ${manager}\n`);
38
+ const run = spawnSync(manager, args, { stdio: 'inherit' });
39
+ if (run.error !== undefined || run.status !== 0) {
40
+ process.stderr.write(`That did not work. Run it yourself:\n ${manager} ${args.join(' ')}\n`);
41
+ return 1;
42
+ }
43
+ process.stderr.write(`metro is now ${newest}\n`);
44
+ return 0;
45
+ }
@@ -0,0 +1,97 @@
1
+ import { readFileSync } from 'node:fs';
2
+ export const PACKAGE_NAME = '@stage-labs/metro';
3
+ const REGISTRY = 'https://registry.npmjs.org';
4
+ const TIMEOUT_MS = 15_000;
5
+ export function parseVersion(raw) {
6
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(raw.trim());
7
+ if (match === null)
8
+ return null;
9
+ const core = [match[1], match[2], match[3]].map((n) => Number(n));
10
+ const pre = match[4] === undefined
11
+ ? []
12
+ : match[4]
13
+ .split('.')
14
+ .map((part) => (/^\d+$/.test(part) ? Number(part) : part));
15
+ return { core, pre };
16
+ }
17
+ function comparePart(left, right) {
18
+ if (left === undefined)
19
+ return -1;
20
+ if (right === undefined)
21
+ return 1;
22
+ if (left === right)
23
+ return 0;
24
+ if (typeof left === 'number' && typeof right === 'number')
25
+ return left < right ? -1 : 1;
26
+ if (typeof left === 'number')
27
+ return -1;
28
+ if (typeof right === 'number')
29
+ return 1;
30
+ return left < right ? -1 : 1;
31
+ }
32
+ function comparePre(a, b) {
33
+ if (a.length === 0 && b.length === 0)
34
+ return 0;
35
+ if (a.length === 0)
36
+ return 1;
37
+ if (b.length === 0)
38
+ return -1;
39
+ for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
40
+ const cmp = comparePart(a[i], b[i]);
41
+ if (cmp !== 0)
42
+ return cmp;
43
+ }
44
+ return 0;
45
+ }
46
+ export function compareVersions(a, b) {
47
+ const left = parseVersion(a);
48
+ const right = parseVersion(b);
49
+ if (left === null || right === null)
50
+ return 0;
51
+ for (let i = 0; i < 3; i += 1) {
52
+ const l = left.core[i] ?? 0;
53
+ const r = right.core[i] ?? 0;
54
+ if (l !== r)
55
+ return l < r ? -1 : 1;
56
+ }
57
+ return comparePre(left.pre, right.pre);
58
+ }
59
+ export function isNewer(candidate, current) {
60
+ return compareVersions(candidate, current) > 0;
61
+ }
62
+ export function currentVersion() {
63
+ const path = new URL('../package.json', import.meta.url);
64
+ const raw = JSON.parse(readFileSync(path, 'utf8'));
65
+ return typeof raw.version === 'string' ? raw.version : '0.0.0';
66
+ }
67
+ export function newestOf(tags) {
68
+ let best = '';
69
+ for (const value of Object.values(tags)) {
70
+ if (typeof value !== 'string' || parseVersion(value) === null)
71
+ continue;
72
+ if (best === '' || isNewer(value, best))
73
+ best = value;
74
+ }
75
+ return best;
76
+ }
77
+ export async function publishedVersion() {
78
+ let res;
79
+ try {
80
+ res = await fetch(`${REGISTRY}/${PACKAGE_NAME.replace('/', '%2F')}`, {
81
+ headers: { accept: 'application/json' },
82
+ signal: AbortSignal.timeout(TIMEOUT_MS),
83
+ });
84
+ }
85
+ catch {
86
+ throw new Error('could not reach the npm registry');
87
+ }
88
+ if (!res.ok)
89
+ throw new Error(`npm answered ${String(res.status)}`);
90
+ const body = await res.json();
91
+ if (typeof body !== 'object' || body === null)
92
+ throw new Error('npm returned an unexpected response');
93
+ const tags = body['dist-tags'];
94
+ if (typeof tags !== 'object' || tags === null)
95
+ throw new Error('npm returned no dist-tags');
96
+ return newestOf(tags);
97
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stage-labs/metro",
3
- "version": "0.1.0-beta.2",
4
- "description": "Chat with your Claude Code or Codex agent over Telegram and Discord. Ultra-lightweight: ~1.2K lines of TypeScript, pure CLI, no hosted infra.",
3
+ "version": "0.1.0-beta.200",
4
+ "description": "The metro command line. Sign in once per machine, then hand your MCP connector list to Claude Code without the credentials touching disk, argv or shell history.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -14,35 +14,26 @@
14
14
  "node": ">=22"
15
15
  },
16
16
  "bin": {
17
- "metro": "./dist/cli.js"
17
+ "metro": "dist/cli.js"
18
18
  },
19
19
  "files": [
20
20
  "dist",
21
- "skills",
22
- "README.md",
23
- "LICENSE"
21
+ "runtime"
24
22
  ],
25
23
  "publishConfig": {
26
- "access": "public"
24
+ "access": "public",
25
+ "tag": "beta"
27
26
  },
28
27
  "scripts": {
29
- "build": "tsc",
30
- "prepublishOnly": "tsc",
31
- "lint": "eslint src/",
32
- "lint:fix": "eslint src/ --fix",
33
- "typecheck": "tsc --noEmit"
34
- },
35
- "dependencies": {
36
- "discord.js": "^14.14.0",
37
- "pino": "^9.5.0",
38
- "ws": "^8.20.0"
28
+ "build": "tsc && node scripts/stage-runtime.mjs",
29
+ "prepublishOnly": "tsc && node scripts/stage-runtime.mjs",
30
+ "test": "tsc --noEmit && node scripts/stage-runtime.mjs && bun test test/"
39
31
  },
40
32
  "devDependencies": {
41
- "@types/node": "^22.10.0",
42
- "@types/ws": "^8.18.1",
33
+ "@stage-labs/config": "0.1.0-beta.2",
43
34
  "eslint": "^10.3.0",
44
- "typescript": "^5",
45
- "typescript-eslint": "^8.59.2"
46
- },
47
- "packageManager": "bun@1.3.9"
35
+ "knip": "^6.15.0",
36
+ "madge": "^8.0.0",
37
+ "typescript": "^5"
38
+ }
48
39
  }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "metro",
3
+ "description": "Metro — connector relay and chat bridge for agents",
4
+ "owner": { "name": "Bonustrack", "url": "https://metro.box" },
5
+ "plugins": [
6
+ {
7
+ "name": "metro",
8
+ "source": "./plugin",
9
+ "description": "Every connector on this box as its own MCP server through metro's local relay, the orchestrator guard, and the standing rules loaded at session start."
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "metro",
3
+ "version": "0.1.0-beta.200",
4
+ "description": "Metro for Claude Code, installed by the metro daemon on this machine: every connector the agent holds as its own MCP server relayed through the daemon, the orchestrator-only guard on the main thread, and the standing rules loaded at each session start. No vendor credential ever sits in a config file.",
5
+ "author": {
6
+ "name": "Bonustrack",
7
+ "url": "https://metro.box"
8
+ },
9
+ "repository": "https://github.com/bonustrack/metro",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "metro",
13
+ "mcp",
14
+ "connectors",
15
+ "relay"
16
+ ]
17
+ }
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import { policyVerdict } from './policy.mjs';
4
+
5
+ const BLOCKING = new Set(['AskUserQuestion', 'ExitPlanMode', 'EnterPlanMode']);
6
+ const ORCHESTRATION = new Set([
7
+ 'Agent',
8
+ 'Workflow',
9
+ 'ToolSearch',
10
+ 'Skill',
11
+ 'ScheduleWakeup',
12
+ 'Monitor',
13
+ 'CronCreate',
14
+ 'CronList',
15
+ 'CronDelete',
16
+ 'TaskCreate',
17
+ 'TaskGet',
18
+ 'TaskList',
19
+ 'TaskOutput',
20
+ 'TaskStop',
21
+ 'TaskUpdate',
22
+ ]);
23
+ const IMAGE = /\.(png|jpe?g|gif|webp|bmp)$/i;
24
+ const MCP = /^mcp__/;
25
+ const METRO_MCP = /^mcp__metro__/;
26
+
27
+ function answer(decision, reason) {
28
+ process.stdout.write(
29
+ `${JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: decision, permissionDecisionReason: reason } })}\n`,
30
+ );
31
+ }
32
+
33
+ function ownerPolicy(tool, input, subagent) {
34
+ const verdict = policyVerdict(tool, input);
35
+ if (verdict === null) return null;
36
+ const { where } = verdict;
37
+ if (verdict.access === 'deny') return { decision: 'deny', reason: `Blocked by the owner's policy for ${verdict.owner} (${verdict.tool}).` };
38
+ if (subagent) return { decision: 'ask', reason: `The owner asked to approve ${where}.` };
39
+ return {
40
+ decision: 'deny',
41
+ reason: `${where} needs the owner's approval, and waiting for it here would stop the whole session. Run this exact call from a background worker (Agent, run_in_background: true): the worker waits for the owner's answer while you keep answering.`,
42
+ };
43
+ }
44
+
45
+ function verdict(payload, input) {
46
+ const tool = typeof payload.tool_name === 'string' ? payload.tool_name : '';
47
+ if (BLOCKING.has(tool))
48
+ return `${tool} blocks the session waiting on the terminal, and nobody is watching it. Decide with your best judgement and state the assumption, or ask over chat with the metro tools and keep working in the meantime.`;
49
+ if (typeof payload.agent_id === 'string' && payload.agent_id !== '') return null;
50
+ if (tool === 'Agent' && input.run_in_background === false)
51
+ return 'A foreground subagent blinds the main thread: it cannot see or acknowledge inbound metro messages until the agent finishes. Re-issue the identical Agent call with run_in_background: true, then wait for the task notification.';
52
+ if (ORCHESTRATION.has(tool)) return null;
53
+ if (METRO_MCP.test(tool)) return null;
54
+ if (MCP.test(tool))
55
+ return `A connector call is work, not orchestration: ${tool} reads somebody's files, mail or records, and its answer can be long enough to crowd out the chat you are here to follow. Only metro's own tools run on this thread. Delegate it to a subagent with the Agent tool (run_in_background: true; the worker reaches every connector) and relay what it finds.`;
56
+ if (tool === 'Read' && typeof input.file_path === 'string' && IMAGE.test(input.file_path)) return null;
57
+ return `The main thread is orchestrator-only, so ${tool === '' ? 'this tool' : tool} is not available here. Delegate the work to a subagent with the Agent tool (run_in_background: true; the worker agent has full tool access) or to a Workflow, and report the result over metro.`;
58
+ }
59
+
60
+ let payload = {};
61
+ try {
62
+ payload = JSON.parse(readFileSync(0, 'utf8'));
63
+ } catch {
64
+ payload = {};
65
+ }
66
+ const shaped = payload !== null && typeof payload === 'object' ? payload : {};
67
+ const input = shaped.tool_input !== null && typeof shaped.tool_input === 'object' ? shaped.tool_input : {};
68
+ const reason = verdict(shaped, input);
69
+ const policy = reason === null && typeof shaped.tool_name === 'string' ? ownerPolicy(shaped.tool_name, input, typeof shaped.agent_id === 'string' && shaped.agent_id !== '') : null;
70
+ if (reason !== null) answer('deny', reason);
71
+ else if (policy !== null) answer(policy.decision, policy.reason);
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ const agentsDir = () => process.env.METRO_AGENTS_DIR || join(homedir(), '.metro', 'agents');
7
+
8
+ function readAgent() {
9
+ const path = join(agentsDir(), 'agent.json');
10
+ let parsed;
11
+ try {
12
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
13
+ } catch {
14
+ throw new Error(`no agent on this machine yet (${path})`);
15
+ }
16
+ if (typeof parsed?.key !== 'string') throw new Error(`no agent key in ${path}`);
17
+ return parsed;
18
+ }
19
+
20
+ if (process.argv[2] !== 'headers') {
21
+ process.stderr.write('usage: metro-plugin.mjs headers\n');
22
+ process.exit(1);
23
+ }
24
+
25
+ try {
26
+ process.stdout.write(`${JSON.stringify({ Authorization: `Bearer ${readAgent().key}` })}\n`);
27
+ } catch (err) {
28
+ process.stderr.write(`metro: ${err.message}\n`);
29
+ process.stdout.write('{}\n');
30
+ }