@stage-labs/metro 0.1.0-beta.13 → 0.1.0-beta.131

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 (329) hide show
  1. package/dist/api.js +14 -0
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +176 -0
  4. package/dist/cli.js +101 -0
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +93 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +43 -0
  11. package/dist/plugin.js +120 -0
  12. package/dist/provider-flags.js +9 -0
  13. package/dist/route.js +38 -0
  14. package/dist/runtime-install.js +97 -0
  15. package/dist/runtime.js +74 -0
  16. package/dist/serve.js +150 -0
  17. package/dist/service.js +242 -0
  18. package/dist/tail.js +119 -0
  19. package/dist/update.js +45 -0
  20. package/dist/version.js +97 -0
  21. package/package.json +14 -27
  22. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  23. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +18 -0
  24. package/runtime/marketplace/plugin/.mcp.json +1 -0
  25. package/runtime/marketplace/plugin/bin/guard.mjs +55 -0
  26. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +122 -0
  27. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  28. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  29. package/runtime/marketplace/plugin/orchestrator.md +16 -0
  30. package/runtime/marketplace/plugin/skills/refresh/SKILL.md +15 -0
  31. package/runtime/node_modules/@metro-labs/core/package.json +43 -0
  32. package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
  33. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  34. package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
  35. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  36. package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
  37. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  38. package/runtime/node_modules/@metro-labs/core/src/lines.ts +87 -0
  39. package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
  40. package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
  41. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
  42. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  44. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
  47. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +108 -0
  48. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  49. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  50. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  51. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +70 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +365 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +43 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +300 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +122 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +371 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +155 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +45 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +95 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/seed.ts +33 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +209 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +309 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +239 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +310 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +175 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +77 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +296 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +286 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +332 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +229 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +269 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +207 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +229 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +372 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +223 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +96 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  174. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  175. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  176. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  177. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  178. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  179. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  180. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  181. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  182. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  183. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  191. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  192. package/runtime/node_modules/@metro-labs/http/package.json +33 -0
  193. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  194. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
  195. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  196. package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
  197. package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
  198. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  199. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  200. package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
  201. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  202. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  203. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  204. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  205. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  206. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  207. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  208. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  209. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  210. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  211. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  212. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  213. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  214. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  215. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  216. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  217. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  218. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  219. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  220. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  221. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  222. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  223. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  224. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  225. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  226. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  227. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  228. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  229. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  230. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  231. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  232. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +100 -0
  233. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +219 -0
  234. package/runtime/node_modules/@metro-labs/threema/src/api.ts +130 -0
  235. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +132 -0
  236. package/runtime/node_modules/@metro-labs/threema/src/format.ts +123 -0
  237. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  238. package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
  239. package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
  240. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  241. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  242. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  243. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  244. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  245. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  246. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +190 -0
  247. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  248. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  249. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +347 -0
  250. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  251. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  252. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  253. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  254. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  255. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  256. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  257. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  258. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  259. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  260. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  261. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  262. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +18 -0
  263. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  264. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  265. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  266. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  267. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  268. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  269. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  270. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  271. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  272. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  273. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  274. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  275. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  276. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  277. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  278. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  279. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  280. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  281. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  282. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  283. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  284. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  285. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  286. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  287. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  288. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  289. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  290. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  291. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  292. package/runtime/runtime.json +3 -0
  293. package/runtime/server.ts +1 -0
  294. package/runtime/stations.json +39 -0
  295. package/runtime/trains/.keep +0 -0
  296. package/LICENSE +0 -21
  297. package/README.md +0 -252
  298. package/dist/broker.js +0 -202
  299. package/dist/cache.js +0 -69
  300. package/dist/cli/actions.js +0 -206
  301. package/dist/cli/config.js +0 -185
  302. package/dist/cli/index.js +0 -188
  303. package/dist/cli/skill.js +0 -62
  304. package/dist/cli/tail.js +0 -164
  305. package/dist/cli/util.js +0 -72
  306. package/dist/cli/webhook.js +0 -81
  307. package/dist/codex-rc.js +0 -245
  308. package/dist/dispatcher.js +0 -128
  309. package/dist/history.js +0 -121
  310. package/dist/ipc.js +0 -72
  311. package/dist/log.js +0 -15
  312. package/dist/monitor.js +0 -194
  313. package/dist/paths.js +0 -68
  314. package/dist/registry.js +0 -48
  315. package/dist/stations/claude.js +0 -45
  316. package/dist/stations/codex.js +0 -68
  317. package/dist/stations/discord.js +0 -216
  318. package/dist/stations/index.js +0 -129
  319. package/dist/stations/telegram-md.js +0 -34
  320. package/dist/stations/telegram-upload.js +0 -113
  321. package/dist/stations/telegram.js +0 -234
  322. package/dist/stations/webhook.js +0 -103
  323. package/dist/tunnel.js +0 -64
  324. package/dist/webhooks.js +0 -41
  325. package/docs/broker.md +0 -214
  326. package/docs/monitor.md +0 -163
  327. package/docs/uri-scheme.md +0 -124
  328. package/docs/users.md +0 -226
  329. package/skills/metro/SKILL.md +0 -256
@@ -0,0 +1,64 @@
1
+ import {
2
+ accountFromLine,
3
+ agentIdForAccount,
4
+ stationAgentIds,
5
+ } from './map.js';
6
+ import { STATIONS } from '@metro-labs/core/station-names';
7
+
8
+ const ACCOUNT_STATIONS = new Set<string>(STATIONS);
9
+
10
+ const stationHasAccounts = (station: string): boolean =>
11
+ ACCOUNT_STATIONS.has(station);
12
+
13
+ const accountInScope = (
14
+ allowed: Set<string>,
15
+ station: string,
16
+ accountId: string,
17
+ ): boolean => {
18
+ const agentId = agentIdForAccount(station, accountId);
19
+ return agentId !== undefined && allowed.has(agentId);
20
+ };
21
+
22
+ export function lineTargetDenied(
23
+ allowed: Set<string>,
24
+ args: Record<string, unknown>,
25
+ station?: string,
26
+ ): boolean {
27
+ const line = typeof args.line === 'string' ? args.line : undefined;
28
+ if (line === undefined) return false;
29
+ const acct = accountFromLine(line);
30
+ if (!acct) return true;
31
+ if (station !== undefined && acct.station !== station) return true;
32
+ if (!accountInScope(allowed, acct.station, acct.accountId)) return true;
33
+ const override = typeof args.account === 'string' ? args.account : undefined;
34
+ return override !== undefined
35
+ ? !accountInScope(allowed, acct.station, override)
36
+ : false;
37
+ }
38
+
39
+ export function stationFullyScoped(
40
+ allowed: Set<string>,
41
+ station: string,
42
+ ): boolean {
43
+ const ids = stationAgentIds(station);
44
+ return ids.length > 0 && ids.every((id) => allowed.has(id));
45
+ }
46
+
47
+ export function callTargetDenied(
48
+ allowed: Set<string>,
49
+ station: string,
50
+ args: Record<string, unknown>,
51
+ ): boolean {
52
+ if (typeof args.line === 'string')
53
+ return lineTargetDenied(allowed, args, station);
54
+ if (typeof args.account === 'string')
55
+ return !accountInScope(allowed, station, args.account);
56
+ return !stationFullyScoped(allowed, station);
57
+ }
58
+
59
+ export function eventInScope(allowed: Set<string>, line: string): boolean {
60
+ const acct = accountFromLine(line);
61
+ if (!acct) return false;
62
+ if (!stationHasAccounts(acct.station)) return true;
63
+ return accountInScope(allowed, acct.station, acct.accountId);
64
+ }
@@ -0,0 +1,33 @@
1
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { errMsg, log } from '@metro-labs/core/log';
4
+ import { agentsDir } from './files.js';
5
+ import { LOCAL_PROJECT_ID, localCreateAgent, localOwner, storedAgents } from './file-admin.js';
6
+
7
+ export const SEED_FILE = '.agent';
8
+
9
+ export type Seeded = 'created' | 'present' | 'none' | 'failed';
10
+
11
+ export async function seedAgent(dir = agentsDir()): Promise<Seeded> {
12
+ const path = join(dir, SEED_FILE);
13
+ if (!existsSync(path)) return 'none';
14
+ const name = readFileSync(path, 'utf8').trim();
15
+ const owner = localOwner(dir);
16
+ if (owner === null) {
17
+ log.warn({ name }, 'agent seed: no owner yet, leaving the seed for the next boot');
18
+ return 'failed';
19
+ }
20
+ if (storedAgents(dir).length > 0) {
21
+ rmSync(path, { force: true });
22
+ return 'present';
23
+ }
24
+ try {
25
+ const made = await localCreateAgent(owner, LOCAL_PROJECT_ID, name, dir);
26
+ rmSync(path, { force: true });
27
+ log.info({ id: made.id, name: made.name }, 'agent seed: agent created from the launch name');
28
+ return 'created';
29
+ } catch (err) {
30
+ log.warn({ name, err: errMsg(err) }, 'agent seed: could not create the agent');
31
+ return 'failed';
32
+ }
33
+ }
@@ -0,0 +1,38 @@
1
+ import { bufferedSince, type MetroEvent } from '@metro-labs/core/events';
2
+ import { accountFromLine } from './map.js';
3
+
4
+ export interface RecentSender {
5
+ id: string;
6
+ name: string;
7
+ at: string;
8
+ }
9
+
10
+ const DEFAULT_LIMIT = 20;
11
+
12
+ const SKIP = new Set(['', 'self', 'unknown']);
13
+
14
+ function sameAccount(from: string, station: string, accountId: string): boolean {
15
+ const account = accountFromLine(from);
16
+ return account?.station === station && account.accountId === accountId;
17
+ }
18
+
19
+ function senderOf(event: MetroEvent, station: string, accountId: string): { id: string; name: string } | null {
20
+ const from = String(event.from);
21
+ if (event.station !== station || !sameAccount(from, station, accountId)) return null;
22
+ const id = from.split('/').pop() ?? '';
23
+ if (SKIP.has(id)) return null;
24
+ return { id, name: event.fromDisplayName ?? event.fromName ?? '' };
25
+ }
26
+
27
+ export function recentSenders(station: string, accountId: string, limit = DEFAULT_LIMIT): RecentSender[] {
28
+ const seen = new Set<string>();
29
+ const out: RecentSender[] = [];
30
+ for (const { event } of bufferedSince(0).reverse()) {
31
+ const sender = senderOf(event, station, accountId);
32
+ if (sender === null || seen.has(sender.id.toLowerCase())) continue;
33
+ seen.add(sender.id.toLowerCase());
34
+ out.push({ ...sender, at: event.ts });
35
+ if (out.length >= limit) break;
36
+ }
37
+ return out;
38
+ }
@@ -0,0 +1,209 @@
1
+ import { join } from 'node:path';
2
+ import { type Server } from 'node:http';
3
+ import { userSelf } from '@metro-labs/core/events';
4
+ import { setTrainCallBackend } from '../stations/train-call.js';
5
+ import { errMsg, log, logFatalSync } from '@metro-labs/core/log';
6
+ import { acquireLock, STATE_DIR, trainsDir } from './paths.js';
7
+ import { installCrashGuard, markDaemonReady } from './crash-guard.js';
8
+ import { METRO_VERSION } from '@metro-labs/core/version';
9
+ import { funnelDriver, Tunnel, tunnelWanted, webhookPort } from '../net/tunnel.js';
10
+ import {
11
+ localConnectHint,
12
+ publicConnectHint,
13
+ tunnelPendingHint,
14
+ } from './connect-hint.js';
15
+ import { TrainSupervisor } from '../stations/supervisor.js';
16
+ import {
17
+ makeEmit,
18
+ startWebhookServer,
19
+ trainEventToMetroEvent,
20
+ } from '../routes/http.js';
21
+ import { localAgentKey } from '../stations/materialize.js';
22
+ import { agentsDir, fileSource } from '../agents/files.js';
23
+ import { ConnectorWatch } from '../connectors/watch.js';
24
+ import { syncPluginServers } from '../connectors/plugin-sync.js';
25
+ import { ensureMetroPlugin } from '../claude/plugin-install.js';
26
+ import { unwatchSession, watchSession } from '../claude/session.js';
27
+ import { tryClaudeSetup } from '../claude/setup.js';
28
+ import { applyLocalOwner } from './local-owner.js';
29
+ import { localOwner } from '../agents/file-admin.js';
30
+ import { seedAgent } from '../agents/seed.js';
31
+ import { ensureStationDeps } from '../stations/runtime-deps.js';
32
+ import { localSessionApis } from '../routes/local-mode.js';
33
+ import type { SessionApis } from '../routes/session-apis.js';
34
+ import {
35
+ agentLiveness,
36
+ closeAgentSession,
37
+ createMetroMcp,
38
+ } from '../mcp/index.js';
39
+ import { metroCall } from '../mcp/ctx.js';
40
+ import { gatherAccountsForAgents } from '../mcp/accounts.js';
41
+ import {
42
+ accountStationCapabilities,
43
+ stationByName,
44
+ } from '../stations/registry.js';
45
+ import { prepareAccount } from '../stations/attach.js';
46
+ import { materializeFrom, reloadFrom } from '../stations/materialize.js';
47
+ import type { StationName } from '@metro-labs/core/station-names';
48
+ import { startUploadReaper } from '../files/upload-store.js';
49
+
50
+ installCrashGuard();
51
+ acquireLock(join(STATE_DIR, '.tail-lock'));
52
+
53
+ log.info({ self: userSelf() }, 'user identity');
54
+
55
+ process.stdout.on('error', (err) => {
56
+ if ((err as NodeJS.ErrnoException).code !== 'EPIPE')
57
+ log.warn({ err: errMsg(err) }, 'stdout error');
58
+ });
59
+
60
+ const supervisor = new TrainSupervisor();
61
+ const emit = makeEmit();
62
+
63
+ supervisor.onTrainEvent((env, train) => {
64
+ const entry = trainEventToMetroEvent(env, train);
65
+ if (entry) emit(entry);
66
+ });
67
+
68
+ let webhookServer: Server | null = null;
69
+ const tunnel = tunnelWanted() ? new Tunnel(funnelDriver(webhookPort()), announcePublic) : null;
70
+
71
+ function announcePublic(url: string): void {
72
+ process.stderr.write(`\n${publicConnectHint(url, localOwner())}`);
73
+ }
74
+
75
+ setTrainCallBackend((train, action, args) =>
76
+ supervisor.call(train, action, args),
77
+ );
78
+
79
+ function announceLocalEndpoint(): void {
80
+ process.stderr.write(
81
+ `\n${tunnel === null ? localConnectHint(webhookPort(), localOwner()) : tunnelPendingHint()}`,
82
+ );
83
+ const key = localAgentKey();
84
+ if (key === null) {
85
+ log.info('no agent on this machine yet');
86
+ return;
87
+ }
88
+ const url = `http://127.0.0.1:${String(webhookPort())}/mcp?token=${key}`;
89
+ process.stderr.write(
90
+ `\nConnect an agent on this machine:\n\n claude mcp add --transport http metro "${url}"\n\n`,
91
+ );
92
+ }
93
+
94
+ async function syncStations(station: StationName): Promise<void> {
95
+ const { removed } = await reloadFrom(fileSource);
96
+ if (stationByName(station)?.hasTrain === false) return;
97
+ if (removed.includes(station)) await supervisor.stopTrain(station);
98
+ else {
99
+ ensureStationDeps(station);
100
+ supervisor.requestReload(station);
101
+ }
102
+ }
103
+
104
+ function sessionApis(): SessionApis {
105
+ return localSessionApis({
106
+ syncStations,
107
+ reloadAgents: async () => {
108
+ await reloadFrom(fileSource);
109
+ },
110
+ restart: () => {
111
+ exitCode = RESTART_EXIT;
112
+ onShutdown();
113
+ },
114
+ stop: () => {
115
+ exitCode = HOLD_EXIT;
116
+ onShutdown();
117
+ },
118
+ closeAgentSession,
119
+ gatherAccounts: gatherAccountsForAgents,
120
+ capabilities: accountStationCapabilities,
121
+ liveness: agentLiveness,
122
+ prepareAccount,
123
+ });
124
+ }
125
+
126
+ let connectors: ConnectorWatch | null = null;
127
+
128
+ function startConnectors(): void {
129
+ const watch = new ConnectorWatch(agentsDir(), () => {
130
+ syncPluginServers();
131
+ });
132
+ connectors = watch;
133
+ watch.start();
134
+ }
135
+
136
+ async function main(): Promise<void> {
137
+ applyLocalOwner();
138
+ await seedAgent();
139
+ await materializeFrom(fileSource, { allowEmpty: true });
140
+ supervisor.start();
141
+ const metroMcp = await createMetroMcp();
142
+ webhookServer = await startWebhookServer(
143
+ emit,
144
+ sessionApis(),
145
+ metroMcp.httpHandler,
146
+ metroCall,
147
+ );
148
+ metroMcp.startInbound();
149
+ startConnectors();
150
+ startUploadReaper();
151
+ announceLocalEndpoint();
152
+ tunnel?.start();
153
+ log.info(
154
+ { tunnel: !!tunnel, trainsDir: trainsDir(), mcp: '/', version: METRO_VERSION },
155
+ 'dispatcher ready',
156
+ );
157
+ markDaemonReady();
158
+ ensureMetroPlugin()
159
+ .then((outcome) => {
160
+ log.info({ outcome }, 'plugin: metro plugin for Claude Code');
161
+ if (outcome !== 'skipped') syncPluginServers();
162
+ })
163
+ .catch((err: unknown) => {
164
+ log.warn({ err: errMsg(err) }, 'plugin: could not ensure the Claude Code plugin');
165
+ });
166
+ tryClaudeSetup();
167
+ watchSession();
168
+ }
169
+
170
+ const SHUTDOWN_TIMEOUT_MS = 3_000;
171
+ const RESTART_EXIT = 75;
172
+ const HOLD_EXIT = 76;
173
+ let exitCode = 0;
174
+ let shuttingDown = false;
175
+ async function shutdown(): Promise<void> {
176
+ if (shuttingDown) return;
177
+ shuttingDown = true;
178
+ log.info('dispatcher shutting down');
179
+ unwatchSession();
180
+ connectors?.stop();
181
+ tunnel?.stop();
182
+ if (webhookServer) {
183
+ const server = webhookServer;
184
+ await Promise.race([
185
+ new Promise<void>((r) => {
186
+ server.close(() => {
187
+ r();
188
+ });
189
+ }),
190
+ new Promise<void>((r) => {
191
+ setTimeout(r, SHUTDOWN_TIMEOUT_MS).unref();
192
+ }),
193
+ ]);
194
+ }
195
+ await supervisor.stop();
196
+ process.exit(exitCode);
197
+ }
198
+ const onShutdown = (): void => {
199
+ shutdown().catch((err: unknown) => {
200
+ log.error({ err: errMsg(err) }, 'dispatcher: shutdown failed');
201
+ process.exit(1);
202
+ });
203
+ };
204
+ for (const sig of ['SIGINT', 'SIGTERM'] as const) process.on(sig, onShutdown);
205
+
206
+ await main().catch((err: unknown) => {
207
+ logFatalSync({ err: errMsg(err) }, 'dispatcher failed to start');
208
+ process.exit(1);
209
+ });
@@ -0,0 +1,39 @@
1
+ import { hostname } from 'node:os';
2
+
3
+ const uiBase = (): string => process.env.METRO_UI_URL ?? 'https://metro.box';
4
+
5
+ const connectLink = (base: string): string =>
6
+ `${uiBase()}/#/${new URL(base).host}`;
7
+
8
+ const ownerLine = (owner: string | null): string =>
9
+ owner === null
10
+ ? 'No owner is set, so no wallet can sign in. Restart with: metro serve --owner <address>'
11
+ : `Only ${owner} can sign in.`;
12
+
13
+ export function localConnectHint(port: number, owner: string | null): string {
14
+ const here = `http://127.0.0.1:${String(port)}`;
15
+ const forward = `ssh -L ${String(port)}:127.0.0.1:${String(port)} ${hostname()}`;
16
+ return [
17
+ 'Manage this machine from the web UI:',
18
+ '',
19
+ ` ${connectLink(here)}`,
20
+ '',
21
+ `From another computer, forward the port first: ${forward}`,
22
+ ownerLine(owner),
23
+ '',
24
+ ].join('\n');
25
+ }
26
+
27
+ export function publicConnectHint(url: string, owner: string | null): string {
28
+ return [
29
+ 'Manage this machine from the web UI, from anywhere:',
30
+ '',
31
+ ` ${connectLink(url)}`,
32
+ '',
33
+ ownerLine(owner),
34
+ '',
35
+ ].join('\n');
36
+ }
37
+
38
+ export const tunnelPendingHint = (): string =>
39
+ 'Publishing this daemon through Tailscale Funnel… (if Tailscale asks to enable Funnel, its link is in the log)\n';
@@ -0,0 +1,49 @@
1
+ import { errMsg, log, logFatalSync } from '@metro-labs/core/log';
2
+
3
+ export type CrashKind = 'unhandledRejection' | 'uncaughtException';
4
+
5
+ const seen: Record<CrashKind, number> = {
6
+ unhandledRejection: 0,
7
+ uncaughtException: 0,
8
+ };
9
+
10
+ let ready = false;
11
+ let installed = false;
12
+
13
+ export function markDaemonReady(): void {
14
+ ready = true;
15
+ }
16
+
17
+ export function crashCount(kind: CrashKind): number {
18
+ return seen[kind];
19
+ }
20
+
21
+ function fields(kind: CrashKind, err: unknown): Record<string, unknown> {
22
+ return {
23
+ crash: kind,
24
+ seen: seen[kind],
25
+ err: errMsg(err),
26
+ errType: err instanceof Error ? err.name : typeof err,
27
+ stack: err instanceof Error ? err.stack : undefined,
28
+ };
29
+ }
30
+
31
+ function reportCrash(kind: CrashKind, err: unknown): void {
32
+ seen[kind] += 1;
33
+ if (!ready) {
34
+ logFatalSync(fields(kind, err), 'daemon: failed before it was ready');
35
+ process.exit(1);
36
+ }
37
+ log.error(fields(kind, err), 'daemon: kept running through a crash');
38
+ }
39
+
40
+ export function installCrashGuard(): void {
41
+ if (installed) return;
42
+ installed = true;
43
+ process.on('unhandledRejection', (reason: unknown) => {
44
+ reportCrash('unhandledRejection', reason);
45
+ });
46
+ process.on('uncaughtException', (err: unknown) => {
47
+ reportCrash('uncaughtException', err);
48
+ });
49
+ }
@@ -0,0 +1,14 @@
1
+ import { localOwner, setLocalOwner } from '../agents/file-admin.js';
2
+ import { log } from '@metro-labs/core/log';
3
+
4
+ export function applyLocalOwner(): string | null {
5
+ const wanted = process.env.METRO_OWNER?.trim() ?? '';
6
+ if (wanted !== '') {
7
+ const address = setLocalOwner(wanted);
8
+ log.info({ owner: address }, 'local daemon: owner set from METRO_OWNER');
9
+ return address;
10
+ }
11
+ const stored = localOwner();
12
+ if (stored === null) log.warn('local daemon: no owner set, so no wallet can sign in; restart with metro serve --owner <address>');
13
+ return stored;
14
+ }
@@ -0,0 +1,106 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import {
3
+ closeSync,
4
+ mkdirSync,
5
+ openSync,
6
+ readFileSync,
7
+ unlinkSync,
8
+ writeSync,
9
+ } from 'node:fs';
10
+ import { homedir } from 'node:os';
11
+ import { join } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { log } from '@metro-labs/core/log';
14
+
15
+ export const STATE_DIR =
16
+ process.env.METRO_STATE_DIR ?? join(homedir(), '.cache', 'metro');
17
+ mkdirSync(STATE_DIR, { recursive: true });
18
+
19
+
20
+ export function trainsDir(): string {
21
+ const env = process.env.METRO_TRAINS_DIR?.trim();
22
+ if (env !== undefined && env !== '') return env;
23
+ return fileURLToPath(new URL('../../trains', import.meta.url));
24
+ }
25
+
26
+ function readLockPid(lockFile: string): number {
27
+ try {
28
+ return Number(readFileSync(lockFile, 'utf8').trim());
29
+ } catch {
30
+ return NaN;
31
+ }
32
+ }
33
+
34
+ function namesMetro(text: string): boolean {
35
+ return text.includes('server.ts') || text.includes('metro');
36
+ }
37
+
38
+ function holderIsMetro(pid: number): boolean {
39
+ try {
40
+ return namesMetro(readFileSync(`/proc/${pid}/cmdline`, 'utf8'));
41
+ } catch {
42
+ return holderCommand(pid);
43
+ }
44
+ }
45
+
46
+ function holderCommand(pid: number): boolean {
47
+ try {
48
+ return namesMetro(
49
+ execFileSync('ps', ['-o', 'command=', '-p', String(pid)], {
50
+ encoding: 'utf8',
51
+ stdio: ['ignore', 'pipe', 'ignore'],
52
+ }),
53
+ );
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ export function lockHeldByLiveDaemon(pid: number): boolean {
60
+ if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
61
+ try {
62
+ process.kill(pid, 0);
63
+ } catch {
64
+ return false;
65
+ }
66
+ return holderIsMetro(pid);
67
+ }
68
+
69
+ function writeOwnLock(lockFile: string): void {
70
+ const fd = openSync(lockFile, 'wx');
71
+ writeSync(fd, String(process.pid));
72
+ closeSync(fd);
73
+ process.on('exit', () => {
74
+ try {
75
+ if (readFileSync(lockFile, 'utf8').trim() === String(process.pid))
76
+ unlinkSync(lockFile);
77
+ } catch {
78
+ }
79
+ });
80
+ }
81
+
82
+ export function acquireLock(lockFile: string): void {
83
+ for (let attempt = 0; attempt < 5; attempt++) {
84
+ try {
85
+ writeOwnLock(lockFile);
86
+ return;
87
+ } catch (err) {
88
+ if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err;
89
+ const pid = readLockPid(lockFile);
90
+ if (lockHeldByLiveDaemon(pid)) {
91
+ log.info(
92
+ { pid },
93
+ 'a healthy `metro` daemon is already running; exiting (no second dispatcher)',
94
+ );
95
+ process.exit(0);
96
+ }
97
+ try {
98
+ unlinkSync(lockFile);
99
+ } catch {
100
+ }
101
+ }
102
+ }
103
+ throw new Error(
104
+ `metro: could not acquire dispatcher lock (${lockFile}) after retries`,
105
+ );
106
+ }
@@ -0,0 +1,25 @@
1
+ import { str } from '@metro-labs/core/str';
2
+
3
+ export type Addressed = 'direct' | 'mention' | 'reply';
4
+
5
+ interface AddressFacts {
6
+ isPrivate?: unknown;
7
+ mentionsSelf?: unknown;
8
+ replyToSelf?: unknown;
9
+ }
10
+
11
+ export function addressedBy(facts: AddressFacts, replyTo: string, sent: ReadonlySet<string>): Addressed | undefined {
12
+ if (facts.isPrivate === true) return 'direct';
13
+ if (facts.mentionsSelf === true) return 'mention';
14
+ if (facts.replyToSelf === true || (replyTo !== '' && sent.has(replyTo))) return 'reply';
15
+ return undefined;
16
+ }
17
+
18
+ export function replyMeta(ev: Record<string, unknown>, sent: ReadonlySet<string>): Record<string, string> {
19
+ const replyTo = str(ev.replyTo);
20
+ const addressed = addressedBy(ev, replyTo, sent);
21
+ return {
22
+ ...(replyTo ? { reply_to: replyTo } : {}),
23
+ ...(addressed === undefined ? {} : { addressed }),
24
+ };
25
+ }
@@ -0,0 +1,24 @@
1
+ import { accountFromLine, agentIdForAccount } from '../agents/map.js';
2
+
3
+ const accountStrippedLine = (line: string): string => {
4
+ const parts = line.split('/');
5
+ if (parts.length < 5) return line;
6
+ return [parts[0], parts[1], parts[2], ...parts.slice(4)].join('/');
7
+ };
8
+
9
+ export const dedupeOwner = (line: string): string => {
10
+ const acct = accountFromLine(line);
11
+ if (!acct) return 'unowned';
12
+ const agentId = agentIdForAccount(acct.station, acct.accountId);
13
+ return agentId === undefined
14
+ ? `account:${acct.accountId}`
15
+ : `agent:${agentId}`;
16
+ };
17
+
18
+ export const dedupeKey = (
19
+ station: string,
20
+ line: string,
21
+ kind: string,
22
+ messageId: string,
23
+ ): string =>
24
+ `${dedupeOwner(line)} ${station} ${accountStrippedLine(line)} ${kind} ${messageId}`;