@stage-labs/metro 0.1.0-beta.17 → 0.1.0-beta.170

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 (316) hide show
  1. package/dist/api.js +8 -86
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +182 -0
  4. package/dist/cli.js +56 -40
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +77 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +43 -0
  11. package/dist/provider-flags.js +9 -0
  12. package/dist/route.js +50 -0
  13. package/dist/runtime-install.js +97 -0
  14. package/dist/runtime.js +74 -0
  15. package/dist/serve.js +155 -0
  16. package/dist/service.js +242 -0
  17. package/dist/tail.js +119 -0
  18. package/dist/update.js +13 -1
  19. package/package.json +6 -5
  20. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  21. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +18 -0
  22. package/runtime/marketplace/plugin/.mcp.json +1 -0
  23. package/runtime/marketplace/plugin/bin/guard.mjs +55 -0
  24. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +40 -0
  25. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  26. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  27. package/runtime/marketplace/plugin/orchestrator.md +22 -0
  28. package/runtime/node_modules/@metro-labs/core/package.json +45 -0
  29. package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
  30. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  31. package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
  32. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  33. package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
  34. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  35. package/runtime/node_modules/@metro-labs/core/src/lines.ts +87 -0
  36. package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
  37. package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
  38. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
  39. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  40. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  41. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  44. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +113 -0
  47. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  48. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  49. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  50. package/runtime/node_modules/@metro-labs/daemon/package.json +47 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +72 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +388 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +39 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +209 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +122 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +362 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +185 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +40 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +95 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +189 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +30 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +380 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +356 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +366 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +254 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +278 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +313 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +157 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +69 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +304 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +299 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +108 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +59 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +101 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +196 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +279 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +182 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +148 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +232 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +300 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +190 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +299 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +195 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +186 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +21 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +58 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +94 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +227 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +380 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +369 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +207 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +58 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +65 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +80 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +48 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +117 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  174. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +254 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  176. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  177. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  178. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  179. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  180. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  181. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +97 -0
  182. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +391 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +224 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  191. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  192. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  193. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  194. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  195. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  196. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +22 -0
  197. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  198. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  199. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  200. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  201. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  202. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  203. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  204. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +106 -0
  205. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  206. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  207. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  208. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +150 -0
  209. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  210. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  211. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +257 -0
  212. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  213. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  214. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +157 -0
  215. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  216. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  217. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  218. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  219. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  220. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  221. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  222. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  223. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  224. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  225. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +27 -0
  226. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  227. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  228. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  229. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  230. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +298 -0
  231. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  232. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +206 -0
  233. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  234. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  235. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  236. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +65 -0
  237. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +20 -0
  238. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
  239. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  240. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  241. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  242. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +107 -0
  243. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +135 -0
  244. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  245. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  246. package/runtime/node_modules/@metro-labs/threema/src/files.ts +93 -0
  247. package/runtime/node_modules/@metro-labs/threema/src/format.ts +182 -0
  248. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +95 -0
  249. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  250. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +90 -0
  251. package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
  252. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  253. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  254. package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
  255. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  256. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  257. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  258. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  259. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  260. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  261. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +214 -0
  262. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  263. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  264. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +372 -0
  265. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  266. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  267. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  268. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  269. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  270. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  271. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  272. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  273. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  274. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  275. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  276. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  277. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +21 -0
  278. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  279. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  280. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  281. package/runtime/node_modules/@metro-labs/xmtp/package.json +35 -0
  282. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +115 -0
  283. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  284. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  285. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  286. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  287. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +382 -0
  288. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  289. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  290. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  291. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  292. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  293. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  294. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  295. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  296. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +47 -0
  297. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +127 -0
  298. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  299. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  300. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  301. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  302. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  303. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  304. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  305. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +17 -0
  306. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  307. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  308. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  309. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +98 -0
  310. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  311. package/runtime/runtime.json +3 -0
  312. package/runtime/server.ts +1 -0
  313. package/runtime/stations.json +39 -0
  314. package/runtime/trains/.keep +0 -0
  315. package/dist/prompt.js +0 -10
  316. package/dist/store.js +0 -70
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,77 @@
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
+ export function agentsDir() {
7
+ const explicit = process.env.METRO_AGENTS_DIR?.trim();
8
+ return explicit !== undefined && explicit !== '' ? explicit : join(homedir(), '.metro', 'agents');
9
+ }
10
+ function agentFiles(dir) {
11
+ const fixed = join(dir, 'agent.json');
12
+ if (existsSync(fixed))
13
+ return [fixed];
14
+ if (!existsSync(dir))
15
+ return [];
16
+ return readdirSync(dir, { withFileTypes: true })
17
+ .filter((entry) => entry.isDirectory())
18
+ .map((entry) => join(dir, entry.name, 'agent.json'))
19
+ .filter((path) => existsSync(path));
20
+ }
21
+ export function localAgents(dir = agentsDir()) {
22
+ const out = [];
23
+ for (const path of agentFiles(dir)) {
24
+ try {
25
+ const file = JSON.parse(readFileSync(path, 'utf8'));
26
+ if (typeof file.id === 'string' && typeof file.key === 'string')
27
+ out.push({ id: file.id, name: typeof file.name === 'string' ? file.name : 'agent', key: file.key });
28
+ }
29
+ catch {
30
+ continue;
31
+ }
32
+ }
33
+ return out.sort((a, b) => a.name.localeCompare(b.name));
34
+ }
35
+ export function localStations(dir = agentsDir()) {
36
+ const out = new Set();
37
+ for (const path of agentFiles(dir)) {
38
+ try {
39
+ const file = JSON.parse(readFileSync(path, 'utf8'));
40
+ if (!Array.isArray(file.stations))
41
+ continue;
42
+ for (const s of file.stations) {
43
+ const station = s.station;
44
+ if (typeof station === 'string')
45
+ out.add(station);
46
+ }
47
+ }
48
+ catch {
49
+ continue;
50
+ }
51
+ }
52
+ return [...out].sort();
53
+ }
54
+ export function pickLocalAgent(agents, wanted) {
55
+ if (wanted !== undefined && wanted !== '') {
56
+ const found = agents.find((a) => a.name === wanted || a.id === wanted);
57
+ if (found === undefined)
58
+ throw new Error(`no local agent named '${wanted}'`);
59
+ return found;
60
+ }
61
+ const only = agents[0];
62
+ if (only !== undefined && agents.length === 1)
63
+ return only;
64
+ throw new Error(agents.length === 0
65
+ ? 'no agent on this machine yet'
66
+ : `several agents on this machine — name one: ${agents.map((a) => a.name).join(', ')}`);
67
+ }
68
+ export async function localDaemonUp(base = localUrl()) {
69
+ try {
70
+ const res = await fetch(`${base}/api/mode`, { signal: AbortSignal.timeout(PROBE_MS) });
71
+ const body = (await res.json());
72
+ return res.ok && body.mode === 'local';
73
+ }
74
+ catch {
75
+ return false;
76
+ }
77
+ }
@@ -0,0 +1,25 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ const METRO_SERVER = 'metro';
5
+ export function metroMcpConfig(agentKey, port) {
6
+ const servers = {
7
+ [METRO_SERVER]: {
8
+ type: 'http',
9
+ url: `http://127.0.0.1:${String(port)}/mcp`,
10
+ headers: { Authorization: `Bearer ${agentKey}` },
11
+ },
12
+ };
13
+ return `${JSON.stringify({ mcpServers: servers }, null, 2)}\n`;
14
+ }
15
+ export function writeMcpConfig(agentKey, port, base = tmpdir()) {
16
+ const dir = mkdtempSync(join(base, 'metro-claude-'));
17
+ const path = join(dir, 'mcp.json');
18
+ writeFileSync(path, metroMcpConfig(agentKey, port), { mode: 0o600 });
19
+ return {
20
+ path,
21
+ cleanup: () => {
22
+ rmSync(dir, { recursive: true, force: true });
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,71 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { agentsDir } from './local.js';
6
+ const NODE_FILE = '.node';
7
+ const ALPHABET = 'abcdefghjkmnpqrstuvwxyz23456789';
8
+ const NAME_RE = /^metro-[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
9
+ const RENAME_WAIT_MS = 30_000;
10
+ const RENAME_POLL_MS = 500;
11
+ const renameWaitMs = () => Number(process.env.METRO_NODE_RENAME_WAIT_MS) || RENAME_WAIT_MS;
12
+ const pause = (ms) => {
13
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
14
+ };
15
+ const stderrLine = (line) => {
16
+ process.stderr.write(`${line}\n`);
17
+ };
18
+ export function newNodeName() {
19
+ const bytes = randomBytes(6);
20
+ return `metro-${[...bytes].map((b) => ALPHABET[b % ALPHABET.length] ?? 'x').join('')}`;
21
+ }
22
+ export function nodeName(dir = agentsDir()) {
23
+ const path = join(dir, NODE_FILE);
24
+ if (existsSync(path)) {
25
+ const held = readFileSync(path, 'utf8').trim();
26
+ if (NAME_RE.test(held))
27
+ return held;
28
+ }
29
+ const name = newNodeName();
30
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
31
+ writeFileSync(path, `${name}\n`, { mode: 0o600 });
32
+ return name;
33
+ }
34
+ export function currentNodeLabel(bin) {
35
+ const run = spawnSync(bin, ['status', '--json'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
36
+ if (run.error !== undefined || run.status !== 0)
37
+ return null;
38
+ try {
39
+ const parsed = JSON.parse(run.stdout);
40
+ const dns = typeof parsed.Self?.DNSName === 'string' ? parsed.Self.DNSName : '';
41
+ const label = dns.split('.')[0] ?? '';
42
+ return label === '' ? null : label.toLowerCase();
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ function awaitRename(bin, wanted, warn) {
49
+ const wait = renameWaitMs();
50
+ const until = Date.now() + wait;
51
+ while (Date.now() < until) {
52
+ if (currentNodeLabel(bin) === wanted)
53
+ return;
54
+ pause(RENAME_POLL_MS);
55
+ }
56
+ warn(`tailscale still reports the old machine name after ${String(Math.round(wait / 1000))}s; the Funnel address may come up under it until the next restart`);
57
+ }
58
+ export function ensureNodeName(bin, dir = agentsDir(), warn = stderrLine) {
59
+ const wanted = nodeName(dir);
60
+ if (currentNodeLabel(bin) === wanted)
61
+ return wanted;
62
+ const run = spawnSync(bin, ['set', '--hostname', wanted], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
63
+ if (run.error !== undefined || run.status !== 0) {
64
+ const said = run.stderr.trim();
65
+ const reason = said === '' ? (run.error?.message ?? `exit ${String(run.status)}`) : said;
66
+ throw new Error(`could not name this machine ${wanted} on your tailnet (${reason}).\n` +
67
+ `Run it once yourself, then start again: sudo tailscale set --hostname ${wanted}`);
68
+ }
69
+ awaitRename(bin, wanted, warn);
70
+ return wanted;
71
+ }
@@ -0,0 +1,43 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ const FILE = '.claude.json';
5
+ const FLAG = 'hasCompletedOnboarding';
6
+ const given = (value) => value !== undefined && value.trim() !== '' ? value.trim() : undefined;
7
+ export function claudeConfigPath(env = process.env) {
8
+ return join(given(env.CLAUDE_CONFIG_DIR) ?? given(env.HOME) ?? homedir(), FILE);
9
+ }
10
+ function readConfig(path) {
11
+ if (!existsSync(path))
12
+ return {};
13
+ try {
14
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
15
+ return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? parsed : null;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ export function markOnboardingDone(path = claudeConfigPath()) {
22
+ const config = readConfig(path);
23
+ if (config === null)
24
+ return 'unreadable';
25
+ if (config[FLAG] === true)
26
+ return 'already';
27
+ writeFileSync(path, `${JSON.stringify({ ...config, [FLAG]: true }, null, 2)}\n`, { mode: 0o600 });
28
+ return 'marked';
29
+ }
30
+ const FEATURES = 'cachedGrowthBookFeatures';
31
+ const CHANNELS_FLAG = 'tengu_harbor';
32
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
33
+ export function seedChannels(path = claudeConfigPath()) {
34
+ const config = readConfig(path);
35
+ if (config === null)
36
+ return 'unreadable';
37
+ const features = isRecord(config[FEATURES]) ? config[FEATURES] : {};
38
+ if (features[CHANNELS_FLAG] === true)
39
+ return 'already';
40
+ const next = { ...config, [FEATURES]: { ...features, [CHANNELS_FLAG]: true } };
41
+ writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
42
+ return 'marked';
43
+ }
@@ -0,0 +1,9 @@
1
+ export const PROVIDER_FLAGS = [
2
+ 'CLAUDE_CODE_USE_BEDROCK',
3
+ 'CLAUDE_CODE_USE_MANTLE',
4
+ 'CLAUDE_CODE_USE_VERTEX',
5
+ 'CLAUDE_CODE_USE_FOUNDRY',
6
+ 'CLAUDE_CODE_USE_ANTHROPIC_AWS',
7
+ 'CLAUDE_CODE_USE_ANTHROPIC_GOOGLE_CLOUD',
8
+ 'CLAUDE_CODE_USE_GATEWAY',
9
+ ];
package/dist/route.js ADDED
@@ -0,0 +1,50 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { agentsDir } from './local.js';
4
+ const PROVIDERS = ['bedrock', 'openrouter', 'codex', 'gemini'];
5
+ export function currentRoute(dir = agentsDir()) {
6
+ const path = join(dir, 'model.json');
7
+ if (!existsSync(path))
8
+ return null;
9
+ try {
10
+ const cfg = JSON.parse(readFileSync(path, 'utf8'));
11
+ const provider = PROVIDERS.find((p) => p === cfg.provider);
12
+ if (provider === undefined)
13
+ return null;
14
+ const block = cfg[provider];
15
+ const model = typeof block === 'object' && block !== null ? block.model : undefined;
16
+ return typeof model === 'string' && model !== '' ? `${provider}:${model}` : null;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ export function routeModelEnv(env, route) {
23
+ if (route === null || (env.ANTHROPIC_MODEL ?? '').trim() !== '')
24
+ return env;
25
+ return { ...env, ANTHROPIC_MODEL: route };
26
+ }
27
+ export function permissionMode(dir = agentsDir()) {
28
+ const path = join(dir, 'claude-setup.json');
29
+ if (!existsSync(path))
30
+ return 'auto';
31
+ try {
32
+ const state = JSON.parse(readFileSync(path, 'utf8'));
33
+ return state.permissionMode === 'bypass' ? 'bypass' : 'auto';
34
+ }
35
+ catch {
36
+ return 'auto';
37
+ }
38
+ }
39
+ export function systemPrompt(dir = agentsDir()) {
40
+ const path = join(dir, 'system-prompt.md');
41
+ if (!existsSync(path))
42
+ return null;
43
+ try {
44
+ const text = readFileSync(path, 'utf8').trim();
45
+ return text === '' ? null : text;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
@@ -0,0 +1,97 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { localStations } from './local.js';
6
+ import { STORE_ENTRY, daemonEntry, findBun, runtimeDir } from './runtime.js';
7
+ export const MANIFEST_FILE = 'stations.json';
8
+ export const MARKETPLACE = 'marketplace';
9
+ const METRO_SOURCES = join('node_modules', '@metro-labs');
10
+ const INSTALL_TIMEOUT_MS = 15 * 60_000;
11
+ export function runtimeStore() {
12
+ const env = process.env.METRO_RUNTIME_STORE?.trim() ?? '';
13
+ return env === '' ? join(homedir(), '.metro', 'runtime') : env;
14
+ }
15
+ const isRecord = (v) => typeof v === 'object' && v !== null;
16
+ const ranges = (raw) => isRecord(raw)
17
+ ? Object.fromEntries(Object.entries(raw).filter((e) => typeof e[1] === 'string'))
18
+ : {};
19
+ export function readManifest(path) {
20
+ const raw = JSON.parse(readFileSync(path, 'utf8'));
21
+ if (!isRecord(raw))
22
+ throw new Error(`${path} is not a runtime manifest`);
23
+ const stations = isRecord(raw.stations) ? raw.stations : {};
24
+ return {
25
+ core: ranges(raw.core),
26
+ stations: Object.fromEntries(Object.entries(stations).map(([name, deps]) => [name, ranges(deps)])),
27
+ };
28
+ }
29
+ export function dependenciesFor(manifest, stations) {
30
+ const out = { ...manifest.core };
31
+ for (const station of new Set(stations))
32
+ Object.assign(out, manifest.stations[station] ?? {});
33
+ return Object.fromEntries(Object.entries(out).sort(([a], [b]) => a.localeCompare(b)));
34
+ }
35
+ const packageText = (deps) => `${JSON.stringify({ name: 'metro-runtime', private: true, dependencies: deps }, null, 2)}\n`;
36
+ function readOrNull(path) {
37
+ try {
38
+ return readFileSync(path, 'utf8');
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
44
+ function copyMarketplace(sources, store) {
45
+ const staged = join(sources, MARKETPLACE);
46
+ if (!existsSync(staged))
47
+ return false;
48
+ cpSync(staged, join(store, MARKETPLACE), { recursive: true });
49
+ return true;
50
+ }
51
+ function syncSources(sources, store) {
52
+ const shim = join(sources, STORE_ENTRY);
53
+ if (existsSync(shim))
54
+ copyFileSync(shim, join(store, STORE_ENTRY));
55
+ const stamp = readOrNull(join(sources, 'runtime.json'));
56
+ if (stamp !== null && readOrNull(join(store, 'runtime.json')) === stamp)
57
+ return existsSync(join(store, MARKETPLACE)) ? false : copyMarketplace(sources, store);
58
+ rmSync(join(store, METRO_SOURCES), { recursive: true, force: true });
59
+ cpSync(join(sources, METRO_SOURCES), join(store, METRO_SOURCES), { recursive: true });
60
+ rmSync(join(store, MARKETPLACE), { recursive: true, force: true });
61
+ copyMarketplace(sources, store);
62
+ if (stamp !== null)
63
+ writeFileSync(join(store, 'runtime.json'), stamp);
64
+ return true;
65
+ }
66
+ export function installDependencies(store, deps, bun, log) {
67
+ const wanted = packageText(deps);
68
+ const marker = join(store, 'node_modules', '.metro-installed');
69
+ if (existsSync(marker) && readOrNull(join(store, 'package.json')) === wanted)
70
+ return false;
71
+ writeFileSync(join(store, 'package.json'), wanted);
72
+ log(`Installing the channel SDKs this machine needs into ${store}`);
73
+ const run = spawnSync(bun, ['install', '--no-summary', '--no-progress'], {
74
+ cwd: store,
75
+ stdio: ['ignore', 'inherit', 'inherit'],
76
+ timeout: INSTALL_TIMEOUT_MS,
77
+ });
78
+ if (run.error !== undefined || run.status !== 0)
79
+ throw new Error(`bun install failed in ${store}: ${run.error?.message ?? `exit ${String(run.status)}`}`);
80
+ writeFileSync(marker, `${new Date().toISOString()}\n`);
81
+ return true;
82
+ }
83
+ export function prepareRuntime(opts = {}) {
84
+ const sources = opts.sources ?? runtimeDir();
85
+ const manifestPath = join(sources, MANIFEST_FILE);
86
+ if (!existsSync(manifestPath))
87
+ return { dir: sources, entry: daemonEntry(sources), trains: join(sources, 'trains'), manifest: null };
88
+ const store = opts.store ?? runtimeStore();
89
+ const log = opts.log ??
90
+ ((line) => {
91
+ process.stderr.write(`${line}\n`);
92
+ });
93
+ mkdirSync(join(store, 'trains'), { recursive: true });
94
+ syncSources(sources, store);
95
+ installDependencies(store, dependenciesFor(readManifest(manifestPath), localStations(opts.agents)), opts.bun ?? findBun(), log);
96
+ return { dir: store, entry: daemonEntry(store), trains: join(store, 'trains'), manifest: manifestPath };
97
+ }
@@ -0,0 +1,74 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const AGENT_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{10}$/;
6
+ export const STORE_ENTRY = 'server.ts';
7
+ export const PACKAGE_ENTRY = join('node_modules', '@metro-labs', 'daemon', 'src', 'server.ts');
8
+ export const daemonEntry = (dir) => existsSync(join(dir, STORE_ENTRY))
9
+ ? join(dir, STORE_ENTRY)
10
+ : join(dir, PACKAGE_ENTRY);
11
+ export class MissingRuntime extends Error {
12
+ }
13
+ export function runtimeDir() {
14
+ const explicit = process.env.METRO_RUNTIME_DIR?.trim();
15
+ const dir = explicit !== undefined && explicit !== ''
16
+ ? explicit
17
+ : join(dirname(dirname(fileURLToPath(import.meta.url))), 'runtime');
18
+ if (!existsSync(daemonEntry(dir)))
19
+ throw new MissingRuntime(`no metro daemon at ${dir}. Reinstall with: npm i -g @stage-labs/metro@beta`);
20
+ return dir;
21
+ }
22
+ class MissingBun extends Error {
23
+ }
24
+ export function assertAgentId(id) {
25
+ if (id === undefined || !AGENT_RE.test(id))
26
+ throw new Error(`'${id ?? ''}' is not an agent id — copy it from the agent's page in the web UI`);
27
+ return id;
28
+ }
29
+ export function findBun() {
30
+ const found = spawnSync('bun', ['--version'], { stdio: 'ignore' });
31
+ if (found.error !== undefined || found.status !== 0)
32
+ throw new MissingBun('metro serve needs Bun, which is not on PATH.\n' +
33
+ 'Install it with: curl -fsSL https://bun.sh/install | bash');
34
+ return 'bun';
35
+ }
36
+ export const localPort = () => Number(process.env.METRO_WEBHOOK_PORT) || 8420;
37
+ export const localUrl = () => `http://127.0.0.1:${String(localPort())}`;
38
+ export const RESTART_CODE = 75;
39
+ export const HOLD_CODE = 76;
40
+ function spawnOnce(plan) {
41
+ const child = spawn(plan.command, plan.args, {
42
+ cwd: plan.cwd,
43
+ env: plan.env,
44
+ stdio: ['ignore', 'inherit', 'inherit'],
45
+ });
46
+ const relay = (sig) => () => {
47
+ child.kill(sig);
48
+ };
49
+ const onInt = relay('SIGINT');
50
+ const onTerm = relay('SIGTERM');
51
+ process.on('SIGINT', onInt);
52
+ process.on('SIGTERM', onTerm);
53
+ return new Promise((resolve) => {
54
+ child.on('exit', (code) => {
55
+ process.off('SIGINT', onInt);
56
+ process.off('SIGTERM', onTerm);
57
+ resolve(code ?? 0);
58
+ });
59
+ });
60
+ }
61
+ export async function spawnPlan(plan, hold) {
62
+ for (;;) {
63
+ const code = await spawnOnce(plan());
64
+ if (code === RESTART_CODE) {
65
+ process.stderr.write('metro was updated; restarting the daemon on the new version\n');
66
+ continue;
67
+ }
68
+ if (code !== HOLD_CODE || hold === undefined)
69
+ return code;
70
+ if ((await hold()) === 'exit')
71
+ return 0;
72
+ process.stderr.write('starting the daemon again\n');
73
+ }
74
+ }