@stage-labs/metro 0.1.0-beta.9 → 0.1.0-beta.90

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 (287) hide show
  1. package/dist/api.js +14 -0
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +129 -0
  4. package/dist/cli.js +101 -0
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +93 -0
  8. package/dist/node-name.js +71 -0
  9. package/dist/plugin.js +80 -0
  10. package/dist/provider-flags.js +9 -0
  11. package/dist/runtime-install.js +87 -0
  12. package/dist/runtime.js +74 -0
  13. package/dist/serve.js +150 -0
  14. package/dist/service.js +242 -0
  15. package/dist/tail.js +119 -0
  16. package/dist/update.js +45 -0
  17. package/dist/version.js +97 -0
  18. package/package.json +14 -25
  19. package/runtime/node_modules/@metro-labs/core/package.json +43 -0
  20. package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
  21. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  22. package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
  23. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  24. package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
  25. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  26. package/runtime/node_modules/@metro-labs/core/src/lines.ts +82 -0
  27. package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
  28. package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
  29. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
  30. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +12 -0
  31. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  32. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  33. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  34. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
  35. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +107 -0
  36. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  37. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  38. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  39. package/runtime/node_modules/@metro-labs/daemon/package.json +47 -0
  40. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  41. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +368 -0
  42. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +43 -0
  43. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
  44. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +302 -0
  45. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +111 -0
  46. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +345 -0
  47. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +155 -0
  48. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +45 -0
  49. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +88 -0
  50. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +193 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +176 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +329 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +167 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +307 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +144 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +69 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +159 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +117 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +157 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +127 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +97 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +295 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +277 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +330 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +252 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +220 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +182 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +208 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +195 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +198 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +40 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +139 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +390 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +198 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +184 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +248 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +35 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +91 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  149. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  150. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  151. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  152. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  153. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  154. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  155. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  156. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  157. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  158. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  159. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  160. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  161. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  162. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  163. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  164. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  165. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  166. package/runtime/node_modules/@metro-labs/http/package.json +33 -0
  167. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  168. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
  169. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  170. package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
  171. package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
  172. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  173. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  174. package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
  175. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  176. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  177. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  178. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  179. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  180. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  181. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  182. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  183. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  184. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  185. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  186. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  187. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  188. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  189. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  190. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  191. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  192. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  193. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  194. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  195. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  196. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  197. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  198. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  199. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  200. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  201. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  202. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  203. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  204. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  205. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  206. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  207. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  208. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  209. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  210. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
  211. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  212. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  213. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +327 -0
  214. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  215. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  216. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  217. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  218. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  219. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  220. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  221. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  222. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  223. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  224. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  225. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
  226. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  227. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  228. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  229. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  230. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  231. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  232. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  233. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  234. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  235. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  236. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  237. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  238. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  239. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  240. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  241. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  242. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  243. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  244. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  245. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  246. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  247. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  248. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  249. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  250. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  251. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  252. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  253. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  254. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  255. package/runtime/runtime.json +3 -0
  256. package/runtime/server.ts +1 -0
  257. package/runtime/stations.json +36 -0
  258. package/runtime/trains/.keep +0 -0
  259. package/LICENSE +0 -21
  260. package/README.md +0 -249
  261. package/dist/cache.js +0 -75
  262. package/dist/cli/actions.js +0 -139
  263. package/dist/cli/config.js +0 -185
  264. package/dist/cli/index.js +0 -177
  265. package/dist/cli/skill.js +0 -62
  266. package/dist/cli/util.js +0 -72
  267. package/dist/cli/webhook.js +0 -83
  268. package/dist/codex-rc.js +0 -245
  269. package/dist/dispatcher.js +0 -122
  270. package/dist/history.js +0 -99
  271. package/dist/ipc.js +0 -72
  272. package/dist/log.js +0 -15
  273. package/dist/paths.js +0 -68
  274. package/dist/registry.js +0 -48
  275. package/dist/stations/claude.js +0 -45
  276. package/dist/stations/codex.js +0 -68
  277. package/dist/stations/discord.js +0 -200
  278. package/dist/stations/index.js +0 -133
  279. package/dist/stations/telegram-md.js +0 -34
  280. package/dist/stations/telegram-upload.js +0 -113
  281. package/dist/stations/telegram.js +0 -213
  282. package/dist/stations/webhook.js +0 -100
  283. package/dist/tunnel.js +0 -66
  284. package/dist/webhooks.js +0 -40
  285. package/docs/agents.md +0 -216
  286. package/docs/uri-scheme.md +0 -125
  287. package/skills/metro/SKILL.md +0 -249
package/dist/api.js ADDED
@@ -0,0 +1,14 @@
1
+ import { localAgents, localDaemonUp, localMcpServers, pickLocalAgent } from './local.js';
2
+ import { localUrl } from './runtime.js';
3
+ const NO_DAEMON = `no metro daemon on ${localUrl()} — start one with: metro serve`;
4
+ export async function mcpServers(wanted) {
5
+ if (!(await localDaemonUp()))
6
+ throw new Error(NO_DAEMON);
7
+ return localMcpServers(pickLocalAgent(localAgents(), wanted));
8
+ }
9
+ export async function whoisAuthorized(wanted) {
10
+ if (!(await localDaemonUp()))
11
+ throw new Error(NO_DAEMON);
12
+ const agent = pickLocalAgent(localAgents(), wanted);
13
+ return { agent: agent.name, where: localUrl() };
14
+ }
@@ -0,0 +1,40 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { PROVIDER_FLAGS } from './provider-flags.js';
5
+ const CONFLICTING = [...PROVIDER_FLAGS, 'ANTHROPIC_BASE_URL', 'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN'];
6
+ export function settingsFiles(cwd = process.cwd(), env = process.env) {
7
+ const explicit = env.CLAUDE_CONFIG_DIR?.trim() ?? '';
8
+ const configDir = explicit === '' ? join(homedir(), '.claude') : explicit;
9
+ return [
10
+ ...new Set([
11
+ join(configDir, 'settings.json'),
12
+ join(cwd, '.claude', 'settings.json'),
13
+ join(cwd, '.claude', 'settings.local.json'),
14
+ ]),
15
+ ];
16
+ }
17
+ function envBlock(file) {
18
+ if (!existsSync(file))
19
+ return {};
20
+ try {
21
+ const parsed = JSON.parse(readFileSync(file, 'utf8'));
22
+ const block = parsed.env;
23
+ return typeof block === 'object' && block !== null ? block : {};
24
+ }
25
+ catch {
26
+ return {};
27
+ }
28
+ }
29
+ export function settingsConflicts(files) {
30
+ const out = [];
31
+ for (const file of files) {
32
+ const block = envBlock(file);
33
+ for (const key of CONFLICTING) {
34
+ const value = block[key];
35
+ if (typeof value === 'string' && value.trim() !== '')
36
+ out.push(`${file}: ${key}`);
37
+ }
38
+ }
39
+ return out;
40
+ }
package/dist/claude.js ADDED
@@ -0,0 +1,129 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { settingsConflicts, settingsFiles } from './claude-settings.js';
3
+ import { localAgents, pickLocalAgent } from './local.js';
4
+ import { PROVIDER_FLAGS } from './provider-flags.js';
5
+ import { localPort, localUrl } from './runtime.js';
6
+ const CHANNEL_FLAGS = ['--dangerously-load-development-channels', 'server:metro'];
7
+ const KEY_HEADER = 'x-metro-key';
8
+ const PROBE_MS = 3_000;
9
+ export const claudeArgs = (extra) => [
10
+ ...CHANNEL_FLAGS,
11
+ ...extra,
12
+ ];
13
+ const set = (env, name) => (env[name] ?? '').trim() !== '';
14
+ export function pinnedBy(env) {
15
+ if (set(env, 'ANTHROPIC_BASE_URL'))
16
+ return 'ANTHROPIC_BASE_URL';
17
+ return PROVIDER_FLAGS.find((flag) => set(env, flag)) ?? null;
18
+ }
19
+ export function gatewayEnv(base, agentKey, port) {
20
+ if (agentKey === null || pinnedBy(base) !== null)
21
+ return base;
22
+ const own = (base.ANTHROPIC_CUSTOM_HEADERS ?? '').trim();
23
+ const mine = `${KEY_HEADER}: ${agentKey}`;
24
+ return {
25
+ ...base,
26
+ ANTHROPIC_BASE_URL: `http://127.0.0.1:${String(port)}/gateway`,
27
+ ANTHROPIC_CUSTOM_HEADERS: own === '' ? mine : `${own}\n${mine}`,
28
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '1',
29
+ };
30
+ }
31
+ const OWN_CREDENTIALS = ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN'];
32
+ export function credentialEnv(env, agentKey, signedIn) {
33
+ if (signedIn || OWN_CREDENTIALS.some((name) => set(env, name)))
34
+ return env;
35
+ return { ...env, ANTHROPIC_AUTH_TOKEN: agentKey };
36
+ }
37
+ export function claudeSignedIn() {
38
+ const run = spawnSync('claude', ['auth', 'status', '--json'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
39
+ if (run.error !== undefined || typeof run.stdout !== 'string')
40
+ return false;
41
+ try {
42
+ const parsed = JSON.parse(run.stdout);
43
+ return parsed.authenticated === true || parsed.loggedIn === true || typeof parsed.email === 'string' || typeof parsed.organization === 'string';
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ export function servingDaemon(body) {
50
+ if (typeof body !== 'object' || body === null)
51
+ return false;
52
+ const mode = body;
53
+ return mode.mode === 'local' && mode.stopped !== true;
54
+ }
55
+ async function daemonServing(base = localUrl()) {
56
+ try {
57
+ const res = await fetch(`${base}/api/mode`, { signal: AbortSignal.timeout(PROBE_MS) });
58
+ return res.ok && servingDaemon(await res.json());
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ export function agentKey(agents, wanted) {
65
+ if (agents.length === 0)
66
+ return { skip: 'no agent lives on this machine yet, so Claude Code talks to Anthropic directly' };
67
+ try {
68
+ return { key: pickLocalAgent(agents, wanted).key };
69
+ }
70
+ catch {
71
+ return {
72
+ skip: wanted === undefined || wanted === ''
73
+ ? 'several agents live here; set METRO_AGENT=<name> to route through the daemon, Claude Code talks to Anthropic directly for now'
74
+ : `no agent named '${wanted}' lives here, so Claude Code talks to Anthropic directly`,
75
+ };
76
+ }
77
+ }
78
+ function localAgentList() {
79
+ try {
80
+ return localAgents();
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ }
86
+ async function verdict() {
87
+ const pinned = pinnedBy(process.env);
88
+ if (pinned !== null)
89
+ return { skip: `${pinned} is set, so Claude Code keeps talking to it` };
90
+ const conflicts = settingsConflicts(settingsFiles());
91
+ if (conflicts.length > 0)
92
+ return { skip: `a settings file pins the provider (${conflicts.join(', ')}), so Claude Code keeps it` };
93
+ const picked = agentKey(localAgentList(), process.env.METRO_AGENT);
94
+ if ('skip' in picked)
95
+ return picked;
96
+ if (await daemonServing())
97
+ return picked;
98
+ return { skip: 'the daemon is not serving here (stopped, or not running), so Claude Code talks to Anthropic directly' };
99
+ }
100
+ export function runClaude(args, env) {
101
+ return new Promise((resolve, reject) => {
102
+ const leaveToChild = () => undefined;
103
+ process.on('SIGINT', leaveToChild);
104
+ process.on('SIGTERM', leaveToChild);
105
+ const child = spawn('claude', args, { stdio: 'inherit', env });
106
+ child.on('error', (err) => {
107
+ reject(new Error(err.code === 'ENOENT'
108
+ ? 'the `claude` command is not on PATH — install Claude Code first'
109
+ : err.message));
110
+ });
111
+ child.on('exit', (code) => {
112
+ resolve(code ?? 1);
113
+ });
114
+ });
115
+ }
116
+ export async function launchClaude(extra) {
117
+ const decision = await verdict();
118
+ if ('skip' in decision) {
119
+ process.stderr.write(`metro claude: ${decision.skip}\n`);
120
+ return runClaude(claudeArgs(extra), process.env);
121
+ }
122
+ const port = localPort();
123
+ process.stderr.write(`metro claude: inference goes through the daemon at http://127.0.0.1:${String(port)}/gateway (the Model page decides where)\n`);
124
+ const routed = gatewayEnv(process.env, decision.key, port);
125
+ const env = credentialEnv(routed, decision.key, claudeSignedIn());
126
+ if (env !== routed)
127
+ process.stderr.write("metro claude: Claude Code has no login of its own here, so metro's key stands in as its credential; the Model page must route to Bedrock, OpenRouter or Codex\n");
128
+ return runClaude(claudeArgs(extra), env);
129
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { mcpServers, whoisAuthorized } from './api.js';
3
+ import { stopAll } from './control.js';
4
+ import { tailEvents } from './tail.js';
5
+ import { launchClaude } from './claude.js';
6
+ import { installPlugin } from './plugin.js';
7
+ import { update } from './update.js';
8
+ import { serve } from './serve.js';
9
+ import { service, serviceStopHint } from './service.js';
10
+ import { currentVersion } from './version.js';
11
+ const USAGE = `metro — run your agent on this machine
12
+
13
+ metro serve [--port <n>] [--owner <address>]
14
+ run the daemon: the agent, its channels and its connectors live in
15
+ ~/.metro/agents here, and it is published through Tailscale Funnel at a
16
+ permanent https://<machine>.<tailnet>.ts.net (Tailscale installed and
17
+ signed in on this machine); the page at metro.box manages it through the
18
+ link it prints; --owner names the one wallet that may sign in, needed on
19
+ the first start only; Stop on the page parks the daemon and keeps the
20
+ address, so Start works from the page too
21
+ metro service install [--port <n>] [--owner <address>]
22
+ run metro serve as a service (systemd on Linux, launchd on macOS): it
23
+ starts at boot and after a crash, so the page can stop, start and
24
+ restart it with no shell; metro service uninstall and status as well
25
+ metro stop stop metro on this machine, metro serve included
26
+ metro tail <agent-id>
27
+ follow this machine's inbound events, one JSON line each
28
+ metro whoami [agent]
29
+ print the agent this machine runs
30
+ metro mcp [agent]
31
+ print the mcpServers block of the agent's connectors, served through the
32
+ daemon's own relay (name the agent if several live here)
33
+ metro plugin set up the Claude Code plugin (connector servers + /metro:refresh)
34
+ metro claude [args...]
35
+ open Claude Code with the metro channel; every argument is passed through
36
+ the same, with inference on Amazon Bedrock through a local proxy so the
37
+ channel still works (needs AWS_BEARER_TOKEN_BEDROCK and AWS_REGION)
38
+ metro update update to the newest published version (--check only reports)
39
+ metro version print this CLI's version
40
+
41
+ Start Claude Code with every connector, writing nothing to disk:
42
+
43
+ claude --mcp-config <(metro mcp)
44
+
45
+ METRO_WEBHOOK_PORT the daemon's port (default 8420)
46
+ METRO_AGENTS_DIR where the agents live (default ~/.metro/agents)
47
+ METRO_AGENT_KEY the key metro tail presents, instead of the agent file's
48
+ METRO_BEDROCK_MODEL
49
+ send every request to this Bedrock model id (default: derive from the
50
+ model Claude Code asks for, e.g. eu.anthropic.claude-sonnet-4-6)
51
+ METRO_RUNTIME_DIR run the daemon from this directory instead of the bundled one
52
+ `;
53
+ async function stopDaemon() {
54
+ const stopped = await stopAll();
55
+ if (stopped.length === 0) {
56
+ process.stderr.write('no metro daemon is running on this machine\n');
57
+ return 1;
58
+ }
59
+ for (const d of stopped)
60
+ process.stderr.write(`Stopped metro (pid ${String(d.pid)}, via ${d.via})\n`);
61
+ const hint = serviceStopHint();
62
+ if (hint !== null)
63
+ process.stderr.write(`${hint}\n`);
64
+ return 0;
65
+ }
66
+ const HELP = new Set([undefined, 'help', '--help', '-h']);
67
+ const COMMANDS = {
68
+ whoami: async () => {
69
+ const { agent, where } = await whoisAuthorized(process.argv[3]);
70
+ process.stdout.write(`agent '${agent}' on ${where}\n`);
71
+ return 0;
72
+ },
73
+ serve: () => serve(process.argv.slice(3)),
74
+ service: () => service(process.argv.slice(3)),
75
+ stop: stopDaemon,
76
+ tail: () => tailEvents(process.argv.slice(3)),
77
+ mcp: async () => {
78
+ process.stdout.write(`${await mcpServers(process.argv[3])}\n`);
79
+ return 0;
80
+ },
81
+ plugin: installPlugin,
82
+ claude: () => launchClaude(process.argv.slice(3)),
83
+ update: () => update(process.argv.slice(3)),
84
+ version: async () => {
85
+ process.stdout.write(`${currentVersion()}\n`);
86
+ return Promise.resolve(0);
87
+ },
88
+ };
89
+ async function run(command) {
90
+ const handler = command === undefined ? undefined : COMMANDS[command];
91
+ if (handler !== undefined)
92
+ return handler();
93
+ process.stderr.write(USAGE);
94
+ return Promise.resolve(HELP.has(command) ? 0 : 1);
95
+ }
96
+ const code = await run(process.argv[2]).catch((err) => {
97
+ const message = err instanceof Error ? err.message : String(err);
98
+ process.stderr.write(`metro: ${message}\n`);
99
+ return 1;
100
+ });
101
+ process.exit(code);
@@ -0,0 +1,74 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { readFileSync, rmSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ export function serveStateDir() {
6
+ const xdg = process.env.XDG_CACHE_HOME?.trim();
7
+ const base = xdg === undefined || xdg === '' ? join(homedir(), '.cache') : xdg;
8
+ return join(base, 'metro', 'serve');
9
+ }
10
+ const serveLockPath = () => join(serveStateDir(), '.tail-lock');
11
+ function isAlive(pid) {
12
+ try {
13
+ process.kill(pid, 0);
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ async function stopPid(pid, waitMs) {
21
+ process.kill(pid, 'SIGTERM');
22
+ const until = Date.now() + waitMs;
23
+ while (Date.now() < until) {
24
+ if (!isAlive(pid))
25
+ return;
26
+ await new Promise((r) => setTimeout(r, 100));
27
+ }
28
+ process.kill(pid, 'SIGKILL');
29
+ }
30
+ const lockPath = () => process.env.METRO_STATE_DIR?.trim()
31
+ ? join(process.env.METRO_STATE_DIR.trim(), '.tail-lock')
32
+ : join(homedir(), '.cache', 'metro', '.tail-lock');
33
+ function commandOf(pid) {
34
+ try {
35
+ return execFileSync('ps', ['-o', 'command=', '-p', String(pid)], {
36
+ encoding: 'utf8',
37
+ stdio: ['ignore', 'pipe', 'ignore'],
38
+ });
39
+ }
40
+ catch {
41
+ return '';
42
+ }
43
+ }
44
+ const looksLikeMetro = (command) => command.includes('server.ts') || command.includes('metro');
45
+ function holderOf(lockFile) {
46
+ let pid;
47
+ try {
48
+ pid = Number(readFileSync(lockFile, 'utf8').trim());
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ if (!Number.isInteger(pid) || pid <= 0 || !isAlive(pid))
54
+ return null;
55
+ return looksLikeMetro(commandOf(pid)) ? pid : null;
56
+ }
57
+ export const lockedBy = () => holderOf(lockPath());
58
+ export const serveLockedBy = () => holderOf(serveLockPath());
59
+ export async function stopAll(waitMs = 10_000) {
60
+ const stopped = [];
61
+ const holder = lockedBy();
62
+ if (holder !== null) {
63
+ await stopPid(holder, waitMs);
64
+ rmSync(lockPath(), { force: true });
65
+ stopped.push({ pid: holder, via: 'the machine lock' });
66
+ }
67
+ const served = serveLockedBy();
68
+ if (served !== null && served !== holder) {
69
+ await stopPid(served, waitMs);
70
+ rmSync(serveLockPath(), { force: true });
71
+ stopped.push({ pid: served, via: 'metro serve' });
72
+ }
73
+ return stopped;
74
+ }
package/dist/hold.js ADDED
@@ -0,0 +1,211 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { createServer } from 'node:http';
4
+ const FUNNEL_RETRY_MS = 2_000;
5
+ const FUNNEL_EXIT_WAIT_MS = 5_000;
6
+ const LISTEN_ATTEMPTS = 10;
7
+ const LISTEN_RETRY_MS = 500;
8
+ const STOPPED = 503;
9
+ export const STOPPED_MESSAGE = 'metro is stopped on this machine. Start it from the Server page on metro.box, or run metro serve.';
10
+ const wait = (ms) => new Promise((r) => setTimeout(r, ms));
11
+ function corsHeaders(req) {
12
+ return {
13
+ 'access-control-allow-origin': req.headers.origin ?? '*',
14
+ 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS',
15
+ 'access-control-allow-headers': 'Authorization, Content-Type',
16
+ 'access-control-allow-private-network': 'true',
17
+ 'access-control-max-age': '86400',
18
+ vary: 'Origin',
19
+ };
20
+ }
21
+ function sendJson(req, res, status, body) {
22
+ res.writeHead(status, {
23
+ 'content-type': 'application/json',
24
+ 'cache-control': 'no-store',
25
+ ...corsHeaders(req),
26
+ });
27
+ res.end(JSON.stringify(body));
28
+ }
29
+ export function holdRequest(req, res, info, onStart) {
30
+ const path = (req.url ?? '').split('?')[0] ?? '';
31
+ if (req.method === 'OPTIONS') {
32
+ res.writeHead(204, corsHeaders(req)).end();
33
+ return;
34
+ }
35
+ if (path === '/api/mode' && req.method === 'GET') {
36
+ sendJson(req, res, 200, {
37
+ mode: 'local',
38
+ owner: info.owner,
39
+ project: 'localdaemon',
40
+ version: info.version,
41
+ stopped: true,
42
+ });
43
+ return;
44
+ }
45
+ if (path === '/api/start') {
46
+ if (req.method !== 'POST') {
47
+ sendJson(req, res, 405, { error: 'method not allowed' });
48
+ return;
49
+ }
50
+ sendJson(req, res, 200, { starting: true });
51
+ onStart();
52
+ return;
53
+ }
54
+ if (path === '/health' || path === '/healthz') {
55
+ sendJson(req, res, STOPPED, { status: 'stopped', version: info.version });
56
+ return;
57
+ }
58
+ sendJson(req, res, STOPPED, { error: STOPPED_MESSAGE, stopped: true });
59
+ }
60
+ export const holdServer = (info, onStart) => createServer((req, res) => {
61
+ holdRequest(req, res, info, onStart);
62
+ });
63
+ const lastLine = (chunk) => String(chunk).trim().split('\n').at(-1) ?? '';
64
+ class HeldFunnel {
65
+ bin;
66
+ port;
67
+ log;
68
+ child = null;
69
+ closed = false;
70
+ timer = null;
71
+ exits = 0;
72
+ constructor(bin, port, log) {
73
+ this.bin = bin;
74
+ this.port = port;
75
+ this.log = log;
76
+ }
77
+ start() {
78
+ if (this.closed)
79
+ return;
80
+ const child = spawn(this.bin, ['funnel', String(this.port)], { stdio: ['ignore', 'pipe', 'pipe'] });
81
+ this.child = child;
82
+ let last = '';
83
+ const read = (chunk) => {
84
+ const line = lastLine(chunk);
85
+ if (line !== '')
86
+ last = line;
87
+ };
88
+ child.stdout?.on('data', read);
89
+ child.stderr?.on('data', read);
90
+ child.on('error', (err) => {
91
+ this.child = null;
92
+ this.closed = true;
93
+ this.log(`funnel could not start (${err.message}); the loopback address is still held`);
94
+ });
95
+ child.on('exit', () => {
96
+ this.child = null;
97
+ if (this.closed)
98
+ return;
99
+ this.exits += 1;
100
+ if (this.exits > 1)
101
+ this.log(`funnel exited (${last}); retrying in ${String(FUNNEL_RETRY_MS / 1000)}s`);
102
+ this.timer = setTimeout(() => {
103
+ this.start();
104
+ }, FUNNEL_RETRY_MS);
105
+ });
106
+ }
107
+ async stop() {
108
+ this.closed = true;
109
+ if (this.timer !== null)
110
+ clearTimeout(this.timer);
111
+ const child = this.child;
112
+ this.child = null;
113
+ if (child === null)
114
+ return;
115
+ if (child.exitCode !== null || child.signalCode !== null)
116
+ return;
117
+ await new Promise((resolve) => {
118
+ const kill = setTimeout(() => {
119
+ child.kill('SIGKILL');
120
+ }, FUNNEL_EXIT_WAIT_MS);
121
+ child.once('exit', () => {
122
+ clearTimeout(kill);
123
+ resolve();
124
+ });
125
+ child.kill('SIGINT');
126
+ });
127
+ }
128
+ }
129
+ async function listen(server, info) {
130
+ for (let attempt = 1;; attempt += 1) {
131
+ try {
132
+ await new Promise((resolve, reject) => {
133
+ server.once('error', reject);
134
+ server.listen(info.port, info.host, () => {
135
+ server.off('error', reject);
136
+ resolve();
137
+ });
138
+ });
139
+ return;
140
+ }
141
+ catch (err) {
142
+ if (attempt >= LISTEN_ATTEMPTS)
143
+ throw err;
144
+ await wait(LISTEN_RETRY_MS);
145
+ }
146
+ }
147
+ }
148
+ const CLOSE_WAIT_MS = 2_000;
149
+ const close = (server) => new Promise((resolve) => {
150
+ const timer = setTimeout(resolve, CLOSE_WAIT_MS);
151
+ server.close(() => {
152
+ clearTimeout(timer);
153
+ resolve();
154
+ });
155
+ server.closeAllConnections();
156
+ });
157
+ function releaseLock(lockFile) {
158
+ try {
159
+ if (readFileSync(lockFile, 'utf8').trim() === String(process.pid))
160
+ rmSync(lockFile, { force: true });
161
+ }
162
+ catch {
163
+ return;
164
+ }
165
+ }
166
+ function armFunnel(info, log, ending) {
167
+ if (info.funnel === null || ending !== null)
168
+ return null;
169
+ const funnel = new HeldFunnel(info.funnel, info.port, log);
170
+ funnel.start();
171
+ return funnel;
172
+ }
173
+ export const holdBanner = (info) => `metro is stopped. Holding http://${info.host}:${String(info.port)}${info.funnel === null ? '' : ' and the Funnel address'} ` +
174
+ 'until Start on the Server page; Ctrl-C or metro stop ends metro serve';
175
+ export async function holdUntilStart(info, deps = {}) {
176
+ const signals = deps.signals ?? process;
177
+ const log = deps.log ??
178
+ ((line) => {
179
+ process.stderr.write(`${line}\n`);
180
+ });
181
+ let finish = () => undefined;
182
+ let ending = null;
183
+ const ended = new Promise((resolve) => {
184
+ finish = (end) => {
185
+ ending ??= end;
186
+ resolve(end);
187
+ };
188
+ });
189
+ const server = holdServer(info, () => {
190
+ finish('start');
191
+ });
192
+ const onSignal = () => {
193
+ finish('exit');
194
+ };
195
+ signals.on('SIGINT', onSignal);
196
+ signals.on('SIGTERM', onSignal);
197
+ if (info.lockFile !== null)
198
+ writeFileSync(info.lockFile, String(process.pid));
199
+ await listen(server, info);
200
+ const funnel = armFunnel(info, log, ending);
201
+ if (ending === null)
202
+ log(holdBanner(info));
203
+ const end = await ended;
204
+ signals.off('SIGINT', onSignal);
205
+ signals.off('SIGTERM', onSignal);
206
+ await funnel?.stop();
207
+ await close(server);
208
+ if (info.lockFile !== null)
209
+ releaseLock(info.lockFile);
210
+ return end;
211
+ }
package/dist/local.js ADDED
@@ -0,0 +1,93 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { localUrl } from './runtime.js';
5
+ const PROBE_MS = 3_000;
6
+ const READ_MS = 20_000;
7
+ export function agentsDir() {
8
+ const explicit = process.env.METRO_AGENTS_DIR?.trim();
9
+ return explicit !== undefined && explicit !== '' ? explicit : join(homedir(), '.metro', 'agents');
10
+ }
11
+ export function localAgents(dir = agentsDir()) {
12
+ if (!existsSync(dir))
13
+ return [];
14
+ const out = [];
15
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
16
+ if (!entry.isDirectory())
17
+ continue;
18
+ const path = join(dir, entry.name, 'agent.json');
19
+ if (!existsSync(path))
20
+ continue;
21
+ try {
22
+ const file = JSON.parse(readFileSync(path, 'utf8'));
23
+ if (typeof file.id === 'string' && typeof file.name === 'string' && typeof file.key === 'string')
24
+ out.push({ id: file.id, name: file.name, key: file.key });
25
+ }
26
+ catch {
27
+ continue;
28
+ }
29
+ }
30
+ return out.sort((a, b) => a.name.localeCompare(b.name));
31
+ }
32
+ export function localStations(dir = agentsDir()) {
33
+ if (!existsSync(dir))
34
+ return [];
35
+ const out = new Set();
36
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
37
+ if (!entry.isDirectory())
38
+ continue;
39
+ const path = join(dir, entry.name, 'agent.json');
40
+ if (!existsSync(path))
41
+ continue;
42
+ try {
43
+ const file = JSON.parse(readFileSync(path, 'utf8'));
44
+ if (!Array.isArray(file.stations))
45
+ continue;
46
+ for (const s of file.stations) {
47
+ const station = s.station;
48
+ if (typeof station === 'string')
49
+ out.add(station);
50
+ }
51
+ }
52
+ catch {
53
+ continue;
54
+ }
55
+ }
56
+ return [...out].sort();
57
+ }
58
+ export function pickLocalAgent(agents, wanted) {
59
+ if (wanted !== undefined && wanted !== '') {
60
+ const found = agents.find((a) => a.name === wanted || a.id === wanted);
61
+ if (found === undefined)
62
+ throw new Error(`no local agent named '${wanted}'`);
63
+ return found;
64
+ }
65
+ const only = agents[0];
66
+ if (only !== undefined && agents.length === 1)
67
+ return only;
68
+ throw new Error(agents.length === 0
69
+ ? 'no agent on this machine yet'
70
+ : `several agents on this machine — name one: ${agents.map((a) => a.name).join(', ')}`);
71
+ }
72
+ export async function localDaemonUp(base = localUrl()) {
73
+ try {
74
+ const res = await fetch(`${base}/api/mode`, { signal: AbortSignal.timeout(PROBE_MS) });
75
+ const body = (await res.json());
76
+ return res.ok && body.mode === 'local';
77
+ }
78
+ catch {
79
+ return false;
80
+ }
81
+ }
82
+ export async function localMcpServers(agent, base = localUrl()) {
83
+ const res = await fetch(`${base}/api/cli/mcp`, {
84
+ headers: { authorization: `Bearer ${agent.key}` },
85
+ signal: AbortSignal.timeout(READ_MS),
86
+ });
87
+ if (!res.ok)
88
+ throw new Error(`the local daemon answered ${String(res.status)} for the connectors`);
89
+ const body = (await res.json());
90
+ if (typeof body.json !== 'string')
91
+ throw new Error('the local daemon returned an unexpected response');
92
+ return body.json;
93
+ }