@stage-labs/metro 0.1.0-beta.16 → 0.1.0-beta.160

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 (309) hide show
  1. package/dist/api.js +8 -57
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +182 -0
  4. package/dist/cli.js +56 -39
  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 +16 -0
  28. package/runtime/node_modules/@metro-labs/core/package.json +44 -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/station-runtime.ts +64 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +112 -0
  46. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  47. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  48. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  49. package/runtime/node_modules/@metro-labs/daemon/package.json +47 -0
  50. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +72 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +388 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +39 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +225 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +122 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +364 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +185 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +40 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +95 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +200 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +30 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +380 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +354 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +263 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +313 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +157 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +69 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +304 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +299 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +108 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +59 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +101 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +247 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +182 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +148 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +229 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +257 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +362 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +232 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +242 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +186 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +94 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +219 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +370 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +208 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +58 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +65 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +80 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +48 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +117 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  174. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +97 -0
  176. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  177. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  178. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  179. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  180. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +390 -0
  181. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  182. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  183. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +48 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +21 -0
  191. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  192. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  193. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  194. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  195. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  196. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  197. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  198. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +106 -0
  199. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  200. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  201. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  202. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +150 -0
  203. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  204. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  205. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +256 -0
  206. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  207. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  208. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  209. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  210. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  211. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  212. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  213. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  214. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  215. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  216. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  217. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +36 -0
  218. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  219. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +26 -0
  220. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  221. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  222. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  223. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  224. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +297 -0
  225. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  226. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  227. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  228. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  229. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  230. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +33 -0
  231. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +19 -0
  232. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  233. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  234. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  235. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  236. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +107 -0
  237. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +135 -0
  238. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  239. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  240. package/runtime/node_modules/@metro-labs/threema/src/files.ts +93 -0
  241. package/runtime/node_modules/@metro-labs/threema/src/format.ts +182 -0
  242. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +95 -0
  243. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  244. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +90 -0
  245. package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
  246. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  247. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  248. package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
  249. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  250. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  251. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  252. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  253. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  254. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  255. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +203 -0
  256. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  257. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  258. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +359 -0
  259. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  260. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  261. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  262. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  263. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  264. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  265. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  266. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  267. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  268. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  269. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  270. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  271. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +20 -0
  272. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  273. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  274. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  275. package/runtime/node_modules/@metro-labs/xmtp/package.json +35 -0
  276. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +115 -0
  277. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  278. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  279. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  280. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  281. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +380 -0
  282. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  283. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  284. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  285. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  286. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  287. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  288. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  289. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  290. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +47 -0
  291. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  292. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  293. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  294. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  295. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  296. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  297. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  298. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +16 -0
  299. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  300. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  301. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  302. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +98 -0
  303. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  304. package/runtime/runtime.json +3 -0
  305. package/runtime/server.ts +1 -0
  306. package/runtime/stations.json +39 -0
  307. package/runtime/trains/.keep +0 -0
  308. package/dist/login.js +0 -102
  309. package/dist/store.js +0 -63
package/dist/api.js CHANGED
@@ -1,58 +1,9 @@
1
- import { metroUrl, readToken } from './store.js';
2
- export class NotSignedIn extends Error {
3
- }
4
- const TIMEOUT_MS = 20_000;
5
- const LOOPBACK = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
6
- export function carriesSecretsSafely(base) {
7
- let url;
8
- try {
9
- url = new URL(base);
10
- }
11
- catch {
12
- return false;
13
- }
14
- if (url.protocol === 'https:')
15
- return true;
16
- return url.protocol === 'http:' && LOOPBACK.has(url.hostname);
17
- }
18
- function tokenOrThrow() {
19
- if (!carriesSecretsSafely(metroUrl()))
20
- throw new Error(`refusing to send your session to ${metroUrl()} in the clear — use https, or a loopback address`);
21
- const token = readToken();
22
- if (token === null)
23
- throw new NotSignedIn('not signed in — run `metro login` first');
24
- return token;
25
- }
26
- async function get(path, presented) {
27
- const auth = presented ?? tokenOrThrow();
28
- let res;
29
- try {
30
- res = await fetch(`${metroUrl()}${path}`, {
31
- headers: { authorization: `Bearer ${auth}` },
32
- signal: AbortSignal.timeout(TIMEOUT_MS),
33
- });
34
- }
35
- catch {
36
- throw new Error(`could not reach ${metroUrl()}`);
37
- }
38
- if (res.status === 401)
39
- throw new NotSignedIn('that sign-in has expired — run `metro login` again');
40
- if (!res.ok)
41
- throw new Error(`metro answered ${String(res.status)}`);
42
- return res.json();
43
- }
44
- function isRecord(value) {
45
- return typeof value === 'object' && value !== null && !Array.isArray(value);
46
- }
47
- export async function mcpServers() {
48
- const body = await get('/api/connectors');
49
- if (!isRecord(body) || typeof body.json !== 'string')
50
- throw new Error('metro returned an unexpected response');
51
- return body.json;
52
- }
53
- export async function sessionEmail(presented) {
54
- const body = await get('/api/session', presented);
55
- if (!isRecord(body) || typeof body.email !== 'string')
56
- throw new Error('metro returned an unexpected response');
57
- return body.email;
1
+ import { localAgents, localDaemonUp, 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 whoisAuthorized(wanted) {
5
+ if (!(await localDaemonUp()))
6
+ throw new Error(NO_DAEMON);
7
+ const agent = pickLocalAgent(localAgents(), wanted);
8
+ return { agent: agent.name, where: localUrl() };
58
9
  }
@@ -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,182 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { markOnboardingDone, seedChannels } from './onboarding.js';
3
+ import { writeMcpConfig } from './mcp-config.js';
4
+ import { currentRoute, permissionMode, routeModelEnv, systemPrompt } from './route.js';
5
+ import { settingsConflicts, settingsFiles } from './claude-settings.js';
6
+ import { localAgents, pickLocalAgent } from './local.js';
7
+ import { PROVIDER_FLAGS } from './provider-flags.js';
8
+ import { localPort, localUrl } from './runtime.js';
9
+ const CHANNEL_FLAGS = ['--dangerously-load-development-channels', 'server:metro'];
10
+ const FRESH_PROMPT_FLAGS = ['--system-prompt-snapshot', 'off'];
11
+ const PERMISSION_MODE_FLAG = { auto: 'auto', bypass: 'bypassPermissions' };
12
+ const KEY_HEADER = 'x-metro-key';
13
+ const PROBE_MS = 3_000;
14
+ export const claudeArgs = (extra, mcpConfig, mode = 'auto', prompt = null) => [
15
+ ...CHANNEL_FLAGS,
16
+ '--permission-mode',
17
+ PERMISSION_MODE_FLAG[mode],
18
+ ...FRESH_PROMPT_FLAGS,
19
+ ...(mcpConfig === undefined ? [] : ['--mcp-config', mcpConfig]),
20
+ ...(prompt === null ? [] : ['--append-system-prompt', prompt]),
21
+ ...extra,
22
+ ];
23
+ const set = (env, name) => (env[name] ?? '').trim() !== '';
24
+ export function bypassSandboxEnv(env, mode, uid) {
25
+ if (mode !== 'bypass' || uid !== 0 || env.IS_SANDBOX !== undefined)
26
+ return env;
27
+ return { ...env, IS_SANDBOX: '1' };
28
+ }
29
+ export function pinnedBy(env) {
30
+ if (set(env, 'ANTHROPIC_BASE_URL'))
31
+ return 'ANTHROPIC_BASE_URL';
32
+ return PROVIDER_FLAGS.find((flag) => set(env, flag)) ?? null;
33
+ }
34
+ export function gatewayEnv(base, agentKey, port) {
35
+ if (agentKey === null || pinnedBy(base) !== null)
36
+ return base;
37
+ const own = (base.ANTHROPIC_CUSTOM_HEADERS ?? '').trim();
38
+ const mine = `${KEY_HEADER}: ${agentKey}`;
39
+ return {
40
+ ...base,
41
+ ANTHROPIC_BASE_URL: `http://127.0.0.1:${String(port)}/gateway`,
42
+ ANTHROPIC_CUSTOM_HEADERS: own === '' ? mine : `${own}\n${mine}`,
43
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '1',
44
+ };
45
+ }
46
+ const OWN_CREDENTIALS = ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN'];
47
+ export function credentialEnv(env, agentKey, signedIn) {
48
+ if (signedIn || OWN_CREDENTIALS.some((name) => set(env, name)))
49
+ return env;
50
+ return { ...env, ANTHROPIC_AUTH_TOKEN: agentKey };
51
+ }
52
+ export function claudeSignedIn() {
53
+ const run = spawnSync('claude', ['auth', 'status', '--json'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
54
+ if (run.error !== undefined || typeof run.stdout !== 'string')
55
+ return false;
56
+ try {
57
+ const parsed = JSON.parse(run.stdout);
58
+ return parsed.authenticated === true || parsed.loggedIn === true || typeof parsed.email === 'string' || typeof parsed.organization === 'string';
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ export function servingDaemon(body) {
65
+ if (typeof body !== 'object' || body === null)
66
+ return false;
67
+ const mode = body;
68
+ return mode.mode === 'local' && mode.stopped !== true;
69
+ }
70
+ async function daemonServing(base = localUrl()) {
71
+ try {
72
+ const res = await fetch(`${base}/api/mode`, { signal: AbortSignal.timeout(PROBE_MS) });
73
+ return res.ok && servingDaemon(await res.json());
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ export function agentKey(agents, wanted) {
80
+ if (agents.length === 0)
81
+ return { skip: 'no agent lives on this machine yet, so Claude Code talks to Anthropic directly' };
82
+ try {
83
+ return { key: pickLocalAgent(agents, wanted).key };
84
+ }
85
+ catch {
86
+ return {
87
+ skip: wanted === undefined || wanted === ''
88
+ ? 'this box has no agent yet (or several old ones), so Claude Code talks to Anthropic directly for now'
89
+ : `no agent named '${wanted}' lives here, so Claude Code talks to Anthropic directly`,
90
+ };
91
+ }
92
+ }
93
+ function localAgentList() {
94
+ try {
95
+ return localAgents();
96
+ }
97
+ catch {
98
+ return [];
99
+ }
100
+ }
101
+ async function verdict() {
102
+ const pinned = pinnedBy(process.env);
103
+ if (pinned !== null)
104
+ return { skip: `${pinned} is set, so Claude Code keeps talking to it` };
105
+ const conflicts = settingsConflicts(settingsFiles());
106
+ if (conflicts.length > 0)
107
+ return { skip: `a settings file pins the provider (${conflicts.join(', ')}), so Claude Code keeps it` };
108
+ const picked = agentKey(localAgentList(), undefined);
109
+ if ('skip' in picked)
110
+ return picked;
111
+ if (await daemonServing())
112
+ return picked;
113
+ return { skip: 'the daemon is not serving here (stopped, or not running), so Claude Code talks to Anthropic directly' };
114
+ }
115
+ export function runClaude(args, env) {
116
+ return new Promise((resolve, reject) => {
117
+ const leaveToChild = () => undefined;
118
+ process.on('SIGINT', leaveToChild);
119
+ process.on('SIGTERM', leaveToChild);
120
+ const child = spawn('claude', args, { stdio: 'inherit', env });
121
+ child.on('error', (err) => {
122
+ reject(new Error(err.code === 'ENOENT'
123
+ ? 'the `claude` command is not on PATH — install Claude Code first'
124
+ : err.message));
125
+ });
126
+ child.on('exit', (code) => {
127
+ resolve(code ?? 1);
128
+ });
129
+ });
130
+ }
131
+ async function servedKey(decision) {
132
+ if ('key' in decision)
133
+ return decision.key;
134
+ const picked = agentKey(localAgentList(), undefined);
135
+ if ('skip' in picked)
136
+ return null;
137
+ return (await daemonServing()) ? picked.key : null;
138
+ }
139
+ function mcpConfigFor(key, port) {
140
+ if (key === null)
141
+ return null;
142
+ const file = writeMcpConfig(key, port);
143
+ process.stderr.write(`metro claude: the metro MCP server is loaded for this session from the daemon at http://127.0.0.1:${String(port)}/mcp, nothing to add by hand\n`);
144
+ return file;
145
+ }
146
+ function gatewayLaunchEnv(key, port) {
147
+ 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`);
148
+ const routed = routeModelEnv(gatewayEnv(process.env, key, port), currentRoute());
149
+ const env = credentialEnv(routed, key, claudeSignedIn());
150
+ if (env !== routed)
151
+ 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");
152
+ if (seedChannels() === 'marked')
153
+ process.stderr.write('metro claude: marked Channels as available in Claude Code cached flags, since a box that sends no telemetry never fetches them\n');
154
+ if (markOnboardingDone() === 'marked')
155
+ process.stderr.write("metro claude: skipped Claude Code's first-run setup, since it already has a credential here\n");
156
+ return env;
157
+ }
158
+ export async function launchClaude(extra) {
159
+ const decision = await verdict();
160
+ const port = localPort();
161
+ const mcp = mcpConfigFor(await servedKey(decision), port);
162
+ const mode = permissionMode();
163
+ const prompt = systemPrompt();
164
+ if (prompt !== null)
165
+ process.stderr.write('metro claude: the system prompt from the Harness page is appended to this session\n');
166
+ const sandbox = (env) => {
167
+ const out = bypassSandboxEnv(env, mode, process.getuid?.());
168
+ if (out !== env)
169
+ process.stderr.write('metro claude: bypass permission mode under root, so IS_SANDBOX=1 is set for Claude Code (it refuses to skip permissions as root otherwise); this box is a dedicated single-tenant machine\n');
170
+ return out;
171
+ };
172
+ try {
173
+ if ('skip' in decision) {
174
+ process.stderr.write(`metro claude: ${decision.skip}\n`);
175
+ return await runClaude(claudeArgs(extra, mcp?.path, mode, prompt), sandbox(process.env));
176
+ }
177
+ return await runClaude(claudeArgs(extra, mcp?.path, mode, prompt), sandbox(gatewayLaunchEnv(decision.key, port)));
178
+ }
179
+ finally {
180
+ mcp?.cleanup();
181
+ }
182
+ }
package/dist/cli.js CHANGED
@@ -1,54 +1,71 @@
1
1
  #!/usr/bin/env node
2
- import { mcpServers, NotSignedIn, sessionEmail } from './api.js';
3
- import { signIn } from './login.js';
4
- import { clearToken, credentialsPath, metroUrl, writeToken } from './store.js';
2
+ import { whoisAuthorized } from './api.js';
3
+ import { stopAll } from './control.js';
4
+ import { tailEvents } from './tail.js';
5
+ import { launchClaude } from './claude.js';
5
6
  import { update } from './update.js';
7
+ import { serve } from './serve.js';
8
+ import { service, serviceStopHint } from './service.js';
6
9
  import { currentVersion } from './version.js';
7
- const USAGE = `metro — the command line for your MCP connectors
10
+ const USAGE = `metro — run your agent on this machine
8
11
 
9
- metro login sign in to metro in your browser
10
- metro logout forget this machine's sign-in
11
- metro whoami print the account this machine is signed in as
12
- metro mcp print an mcpServers block for every connector
13
- metro update update to the newest published version
12
+ metro serve [--port <n>] [--owner <address>]
13
+ run the daemon: the agent, its channels and its connectors live in
14
+ ~/.metro/agents here, and it is published through Tailscale Funnel at a
15
+ permanent https://<machine>.<tailnet>.ts.net (Tailscale installed and
16
+ signed in on this machine); the page at metro.box manages it through the
17
+ link it prints; --owner names the one wallet that may sign in, needed on
18
+ the first start only; Stop on the page parks the daemon and keeps the
19
+ address, so Start works from the page too
20
+ metro service install [--port <n>] [--owner <address>]
21
+ run metro serve as a service (systemd on Linux, launchd on macOS): it
22
+ starts at boot and after a crash, so the page can stop, start and
23
+ restart it with no shell; metro service uninstall and status as well
24
+ metro stop stop metro on this machine, metro serve included
25
+ metro tail <agent-id>
26
+ follow this machine's inbound events, one JSON line each
27
+ metro whoami print the agent this machine runs
28
+ metro claude [args...]
29
+ open Claude Code with the metro channel; every argument is passed through
30
+ the same, with inference on Amazon Bedrock through a local proxy so the
31
+ channel still works (needs AWS_BEARER_TOKEN_BEDROCK and AWS_REGION)
32
+ metro update update to the newest published version (--check only reports)
14
33
  metro version print this CLI's version
15
34
 
16
- Start Claude Code with all of them, without writing them to disk:
17
-
18
- claude --mcp-config <(metro mcp)
19
-
20
- METRO_URL the metro to talk to (default https://mcp.metro.box)
21
- METRO_TOKEN use this session instead of the stored one
35
+ METRO_WEBHOOK_PORT the daemon's port (default 8420)
36
+ METRO_AGENTS_DIR where the agents live (default ~/.metro/agents)
37
+ METRO_AGENT_KEY the key metro tail presents, instead of the agent file's
38
+ METRO_BEDROCK_MODEL
39
+ send every request to this Bedrock model id (default: derive from the
40
+ model Claude Code asks for, e.g. eu.anthropic.claude-sonnet-4-6)
41
+ METRO_RUNTIME_DIR run the daemon from this directory instead of the bundled one
22
42
  `;
23
- async function login() {
24
- const token = await signIn();
25
- const email = await sessionEmail(token).catch(() => '');
26
- writeToken(token);
27
- process.stderr.write(`Signed in${email === '' ? '' : ` as ${email}`}. Stored in ${credentialsPath()}\n`);
28
- }
29
- async function whoami() {
30
- process.stdout.write(`${await sessionEmail()} on ${metroUrl()}\n`);
43
+ async function stopDaemon() {
44
+ const stopped = await stopAll();
45
+ if (stopped.length === 0) {
46
+ process.stderr.write('no metro daemon is running on this machine\n');
47
+ return 1;
48
+ }
49
+ for (const d of stopped)
50
+ process.stderr.write(`Stopped metro (pid ${String(d.pid)}, via ${d.via})\n`);
51
+ const hint = serviceStopHint();
52
+ if (hint !== null)
53
+ process.stderr.write(`${hint}\n`);
54
+ return 0;
31
55
  }
32
56
  const HELP = new Set([undefined, 'help', '--help', '-h']);
33
57
  const COMMANDS = {
34
- login: async () => {
35
- await login();
36
- return 0;
37
- },
38
- logout: async () => {
39
- clearToken();
40
- process.stderr.write('Signed out.\n');
41
- return Promise.resolve(0);
42
- },
43
58
  whoami: async () => {
44
- await whoami();
45
- return 0;
46
- },
47
- mcp: async () => {
48
- process.stdout.write(`${await mcpServers()}\n`);
59
+ const { agent, where } = await whoisAuthorized(process.argv[3]);
60
+ process.stdout.write(`agent '${agent}' on ${where}\n`);
49
61
  return 0;
50
62
  },
51
- update,
63
+ serve: () => serve(process.argv.slice(3)),
64
+ service: () => service(process.argv.slice(3)),
65
+ stop: stopDaemon,
66
+ tail: () => tailEvents(process.argv.slice(3)),
67
+ claude: () => launchClaude(process.argv.slice(3)),
68
+ update: () => update(process.argv.slice(3)),
52
69
  version: async () => {
53
70
  process.stdout.write(`${currentVersion()}\n`);
54
71
  return Promise.resolve(0);
@@ -64,6 +81,6 @@ async function run(command) {
64
81
  const code = await run(process.argv[2]).catch((err) => {
65
82
  const message = err instanceof Error ? err.message : String(err);
66
83
  process.stderr.write(`metro: ${message}\n`);
67
- return err instanceof NotSignedIn ? 2 : 1;
84
+ return 1;
68
85
  });
69
86
  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
+ }