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

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 (328) 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/senders.ts +38 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +207 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +309 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +239 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +310 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +175 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +77 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +296 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +286 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +332 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +229 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +269 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +207 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +229 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +372 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +223 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +96 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  174. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  175. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  176. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  177. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  178. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  179. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  180. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  181. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  182. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  183. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  184. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  185. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  186. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  187. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  188. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  189. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  190. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  191. package/runtime/node_modules/@metro-labs/http/package.json +33 -0
  192. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  193. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
  194. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  195. package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
  196. package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
  197. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  198. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  199. package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
  200. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  201. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  202. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  203. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  204. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  205. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  206. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  207. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  208. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  209. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  210. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  211. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  212. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  213. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  214. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  215. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  216. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  217. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  218. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  219. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  220. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  221. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  222. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  223. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  224. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  225. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  226. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  227. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  228. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  229. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  230. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  231. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +100 -0
  232. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +219 -0
  233. package/runtime/node_modules/@metro-labs/threema/src/api.ts +130 -0
  234. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +132 -0
  235. package/runtime/node_modules/@metro-labs/threema/src/format.ts +123 -0
  236. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  237. package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
  238. package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
  239. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  240. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  241. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  242. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  243. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  244. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  245. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +190 -0
  246. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  247. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  248. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +347 -0
  249. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  250. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  251. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  252. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  253. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  254. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  255. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  256. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  257. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  258. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  259. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  260. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  261. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +18 -0
  262. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  263. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  264. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  265. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  266. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  267. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  268. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  269. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  270. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  271. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  272. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  273. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  274. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  275. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  276. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  277. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  278. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  279. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  280. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  281. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  282. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  283. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  284. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  285. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  286. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  287. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  288. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  289. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  290. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  291. package/runtime/runtime.json +3 -0
  292. package/runtime/server.ts +1 -0
  293. package/runtime/stations.json +39 -0
  294. package/runtime/trains/.keep +0 -0
  295. package/LICENSE +0 -21
  296. package/README.md +0 -252
  297. package/dist/broker.js +0 -202
  298. package/dist/cache.js +0 -69
  299. package/dist/cli/actions.js +0 -206
  300. package/dist/cli/config.js +0 -185
  301. package/dist/cli/index.js +0 -188
  302. package/dist/cli/skill.js +0 -62
  303. package/dist/cli/tail.js +0 -164
  304. package/dist/cli/util.js +0 -72
  305. package/dist/cli/webhook.js +0 -81
  306. package/dist/codex-rc.js +0 -245
  307. package/dist/dispatcher.js +0 -128
  308. package/dist/history.js +0 -121
  309. package/dist/ipc.js +0 -72
  310. package/dist/log.js +0 -15
  311. package/dist/monitor.js +0 -194
  312. package/dist/paths.js +0 -68
  313. package/dist/registry.js +0 -48
  314. package/dist/stations/claude.js +0 -45
  315. package/dist/stations/codex.js +0 -68
  316. package/dist/stations/discord.js +0 -216
  317. package/dist/stations/index.js +0 -129
  318. package/dist/stations/telegram-md.js +0 -34
  319. package/dist/stations/telegram-upload.js +0 -113
  320. package/dist/stations/telegram.js +0 -234
  321. package/dist/stations/webhook.js +0 -103
  322. package/dist/tunnel.js +0 -64
  323. package/dist/webhooks.js +0 -41
  324. package/docs/broker.md +0 -214
  325. package/docs/monitor.md +0 -163
  326. package/docs/uri-scheme.md +0 -124
  327. package/docs/users.md +0 -226
  328. package/skills/metro/SKILL.md +0 -256
package/dist/broker.js DELETED
@@ -1,202 +0,0 @@
1
- /** Broker primitives: claims map + per-user byte-offset cursors over history.jsonl. */
2
- import { closeSync, existsSync, openSync, readFileSync, readSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
3
- import { mkdirSync } from 'node:fs';
4
- import { join } from 'node:path';
5
- import { errMsg, log } from './log.js';
6
- import { STATE_DIR } from './paths.js';
7
- import { Line } from './stations/index.js';
8
- export const CLAIMS_FILE = join(STATE_DIR, 'claims.json');
9
- const CLAIMS_LOCK = join(STATE_DIR, 'claims.json.lock');
10
- const CURSORS_DIR = join(STATE_DIR, 'cursors');
11
- export const HISTORY_FILE = join(STATE_DIR, 'history.jsonl');
12
- /** Read claims.json. Returns empty map if missing or malformed (retries once on race). */
13
- export function readClaims() {
14
- if (!existsSync(CLAIMS_FILE))
15
- return {};
16
- for (let attempt = 0; attempt < 2; attempt++) {
17
- try {
18
- return JSON.parse(readFileSync(CLAIMS_FILE, 'utf8'));
19
- }
20
- catch { /* race with writer — retry once */ }
21
- }
22
- log.warn({ path: CLAIMS_FILE }, 'claims: malformed, treating as empty');
23
- return {};
24
- }
25
- /** Mutate claims under an O_EXCL lockfile. Throws if another writer holds the lock past timeout. */
26
- function withClaimsLock(fn) {
27
- const deadline = Date.now() + 2_000;
28
- while (true) {
29
- try {
30
- closeSync(openSync(CLAIMS_LOCK, 'wx'));
31
- break;
32
- }
33
- catch (err) {
34
- if (err.code !== 'EEXIST')
35
- throw err;
36
- if (Date.now() > deadline)
37
- throw new Error('claims.json: lock contention (held >2s)');
38
- }
39
- }
40
- try {
41
- const next = readClaims();
42
- const result = fn(next);
43
- /** atomic publish: tmpfile + rename so readers never see a half-written file */
44
- const tmp = `${CLAIMS_FILE}.tmp.${process.pid}`;
45
- writeFileSync(tmp, JSON.stringify(next, null, 2) + '\n');
46
- renameSync(tmp, CLAIMS_FILE);
47
- return result;
48
- }
49
- finally {
50
- try {
51
- unlinkSync(CLAIMS_LOCK);
52
- }
53
- catch { /* ignore */ }
54
- }
55
- }
56
- export function claimLine(line, owner) {
57
- return withClaimsLock(m => { m[line] = owner; return m; });
58
- }
59
- export function releaseLine(line) {
60
- return withClaimsLock(m => {
61
- const released = line in m;
62
- delete m[line];
63
- return { released, claims: m };
64
- });
65
- }
66
- /** Per-line decision: should auto-claim run on a successful outbound? */
67
- function shouldAutoClaim(line, kind) {
68
- const station = Line.station(line);
69
- /** Webhook lines are a broadcast stream — claiming one is a footgun. */
70
- if (station === 'webhook')
71
- return { ok: false, reason: 'webhook' };
72
- /** Claude/Codex cross-user lines are 1:1 by construction — always safe. */
73
- if (station === 'claude' || station === 'codex')
74
- return { ok: true };
75
- if (kind === 'group')
76
- return { ok: false, reason: 'group' };
77
- return { ok: true };
78
- }
79
- export function tryAutoClaim(line, owner, opts = {}) {
80
- if (!opts.force) {
81
- const decision = shouldAutoClaim(line, opts.lineKind ?? 'unknown');
82
- if (!decision.ok)
83
- return { status: decision.reason, line };
84
- }
85
- try {
86
- return withClaimsLock(m => {
87
- const existing = m[line];
88
- if (existing && existing !== owner)
89
- return { status: 'skipped', existing };
90
- if (existing === owner)
91
- return { status: 'kept', owner };
92
- m[line] = owner;
93
- return { status: 'claimed', owner };
94
- });
95
- }
96
- catch (err) {
97
- return { status: 'error', error: err.message };
98
- }
99
- }
100
- /** Filename-safe slug for a participant URI. `metro://claude/user/9bfc…` → `claude-user-9bfc…`. */
101
- export function userSlug(uri) {
102
- return uri.replace(/^metro:\/+/, '').replace(/[^A-Za-z0-9_.-]/g, '-');
103
- }
104
- /** Cursor key for a tail invocation. Derived from the *effective mode*, NOT from `userSelf()`. */
105
- /** Keeps `--all` / `--unclaimed` from trampling a `--as=<id>` cursor in a CLAUDECODE shell. */
106
- /** Keys: `--as=<id>`→slug(id); `+--strict`→slug+`--strict`; `--unclaimed`→`_unclaimed`; `--all`→`_all`. */
107
- /** The `_` prefix can't collide with a userSlug (always has a station prefix like `claude-user-…`). */
108
- /** `--include-webhooks` adds `--with-webhooks` so toggling mid-stream doesn't re-emit/skip events. */
109
- export function cursorKey(mode, self, opts = {}) {
110
- if (mode === 'all')
111
- return '_all';
112
- if (mode === 'unclaimed')
113
- return '_unclaimed';
114
- if (!self)
115
- return null;
116
- const base = userSlug(self);
117
- const suffix = mode === 'mine-only' ? '--strict' : '';
118
- const webhooks = opts.includeWebhooks ? '--with-webhooks' : '';
119
- return `${base}${suffix}${webhooks}`;
120
- }
121
- const cursorPath = (key) => join(CURSORS_DIR, key);
122
- export function readCursor(key) {
123
- const p = cursorPath(key);
124
- if (!existsSync(p))
125
- return 0;
126
- const n = Number(readFileSync(p, 'utf8').trim());
127
- return Number.isFinite(n) && n >= 0 ? n : 0;
128
- }
129
- export function writeCursor(key, offset) {
130
- mkdirSync(CURSORS_DIR, { recursive: true });
131
- const p = cursorPath(key);
132
- const tmp = `${p}.tmp.${process.pid}`;
133
- writeFileSync(tmp, String(offset));
134
- renameSync(tmp, p);
135
- }
136
- /** Byte size of history.jsonl right now (for `--since=tail`). */
137
- export function historySize() {
138
- if (!existsSync(HISTORY_FILE))
139
- return 0;
140
- try {
141
- return readFileSync(HISTORY_FILE).length;
142
- }
143
- catch {
144
- return 0;
145
- }
146
- }
147
- /** Yield each complete JSONL line from `offset` to EOF; the returned offset is the position right after the `\n`. */
148
- export function* readEntriesFrom(offset) {
149
- if (!existsSync(HISTORY_FILE))
150
- return;
151
- const fd = openSync(HISTORY_FILE, 'r');
152
- try {
153
- const chunk = Buffer.alloc(64 * 1024);
154
- let pending = Buffer.alloc(0);
155
- let pos = offset;
156
- while (true) {
157
- const n = readSync(fd, chunk, 0, chunk.length, pos);
158
- if (n === 0)
159
- break;
160
- pending = Buffer.concat([pending, chunk.subarray(0, n)]);
161
- pos += n;
162
- let nl;
163
- while ((nl = pending.indexOf(0x0a)) !== -1) {
164
- const raw = pending.subarray(0, nl).toString('utf8').trim();
165
- pending = pending.subarray(nl + 1);
166
- if (!raw)
167
- continue;
168
- try {
169
- const entry = JSON.parse(raw);
170
- /** offsetAfter = read-cursor in file - bytes still in pending buffer */
171
- yield { entry, offset: pos - pending.length };
172
- }
173
- catch (err) {
174
- log.warn({ err: errMsg(err) }, 'broker: skipped malformed history line');
175
- }
176
- }
177
- }
178
- }
179
- finally {
180
- closeSync(fd);
181
- }
182
- }
183
- /**
184
- * Claim-aware filter. Webhooks excluded from personal modes unless `includeWebhooks`.
185
- */
186
- export function passesMode(event, mode, self, claims, opts = {}) {
187
- if (self && event.to === self)
188
- return true;
189
- if (mode === 'all')
190
- return true;
191
- const isWebhook = event.station === 'webhook';
192
- if (mode === 'unclaimed')
193
- return !claims[event.line];
194
- /** webhooks are filtered out of personal modes unless opted in */
195
- if (isWebhook && !opts.includeWebhooks)
196
- return false;
197
- const owner = claims[event.line];
198
- if (mode === 'mine-only')
199
- return owner === self;
200
- /** mode === 'mine-or-unclaimed' */
201
- return !owner || owner === self;
202
- }
package/dist/cache.js DELETED
@@ -1,69 +0,0 @@
1
- /** Per-machine caches: seen lines (lines.json) + bot ids (bot-ids.json). */
2
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { errMsg, log } from './log.js';
5
- import { STATE_DIR } from './paths.js';
6
- const cacheFile = join(STATE_DIR, 'lines.json');
7
- const FLUSH_DELAY_MS = 5_000;
8
- let cache = null;
9
- let dirty = false;
10
- let flushTimer = null;
11
- function read() {
12
- if (cache)
13
- return cache;
14
- if (!existsSync(cacheFile))
15
- return cache = {};
16
- try {
17
- cache = JSON.parse(readFileSync(cacheFile, 'utf8'));
18
- }
19
- catch (err) {
20
- log.warn({ err: errMsg(err), path: cacheFile }, 'lines cache read failed; treating as empty');
21
- cache = {};
22
- }
23
- return cache;
24
- }
25
- function flush() {
26
- if (!dirty || !cache)
27
- return;
28
- try {
29
- writeFileSync(cacheFile, JSON.stringify(cache, null, 2));
30
- dirty = false;
31
- }
32
- catch (err) {
33
- log.warn({ err: errMsg(err), path: cacheFile }, 'lines cache write failed');
34
- }
35
- }
36
- process.on('exit', flush);
37
- export function noteSeen(line, name) {
38
- const c = read();
39
- const entry = c[line] ??= { createdAt: new Date().toISOString() };
40
- entry.lastSeenAt = new Date().toISOString();
41
- if (name && entry.name !== name)
42
- entry.name = name;
43
- dirty = true;
44
- if (!flushTimer)
45
- flushTimer = setTimeout(() => { flushTimer = null; flush(); }, FLUSH_DELAY_MS);
46
- }
47
- export const listLines = () => Object.entries(read()).map(([line, entry]) => ({ line: line, entry }));
48
- /** Bot identity cache: `{discord: "<userId>", telegram: "<userId>"}`. Daemon writes after getMe(). */
49
- const botIdsFile = join(STATE_DIR, 'bot-ids.json');
50
- export const readBotIds = () => {
51
- try {
52
- return existsSync(botIdsFile) ? JSON.parse(readFileSync(botIdsFile, 'utf8')) : {};
53
- }
54
- catch {
55
- return {};
56
- }
57
- };
58
- export function saveBotId(station, id) {
59
- const cur = readBotIds();
60
- if (cur[station] === id)
61
- return;
62
- cur[station] = id;
63
- try {
64
- writeFileSync(botIdsFile, JSON.stringify(cur, null, 2));
65
- }
66
- catch (err) {
67
- log.warn({ err: errMsg(err) }, 'bot-ids cache write failed');
68
- }
69
- }
@@ -1,206 +0,0 @@
1
- /** CLI action handlers: send/reply/edit/react/download/fetch + helpers. */
2
- import { mkdirSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { errMsg } from '../log.js';
6
- import { DiscordStation } from '../stations/discord.js';
7
- import { TelegramStation } from '../stations/telegram.js';
8
- import { ipcCall } from '../ipc.js';
9
- import { userSelf, appendHistory, lookupEntry, mintId, readHistory, resolvePlatformId, } from '../history.js';
10
- import { asLine, Line } from '../stations/index.js';
11
- import { loadMetroEnv } from '../paths.js';
12
- import { tryAutoClaim } from '../broker.js';
13
- import { emit, flagList, flagOne, isJson, need, resolveText, writeJson, } from './util.js';
14
- export function chatStationOf(line) {
15
- const s = Line.station(line);
16
- if (s === 'discord')
17
- return new DiscordStation();
18
- if (s === 'telegram')
19
- return new TelegramStation();
20
- throw new Error(`no chat station for line "${line}" (try metro://{discord|telegram}/...)`);
21
- }
22
- function parseButtons(f) {
23
- const raw = flagOne(f, 'buttons');
24
- if (raw === undefined)
25
- return undefined;
26
- try {
27
- return JSON.parse(raw);
28
- }
29
- catch (err) {
30
- throw new Error(`--buttons must be JSON like '[[{"text":"…","url":"…"}]]': ${errMsg(err)}`);
31
- }
32
- }
33
- function richOpts(f) {
34
- const opts = {};
35
- const images = flagList(f, 'image');
36
- if (images.length)
37
- opts.images = images;
38
- const documents = flagList(f, 'document');
39
- if (documents.length)
40
- opts.documents = documents;
41
- const voice = flagOne(f, 'voice');
42
- if (voice)
43
- opts.voice = voice;
44
- const buttons = parseButtons(f);
45
- if (buttons)
46
- opts.buttons = buttons;
47
- return opts;
48
- }
49
- /** Mirror the original entry's destination: group → `line`; DM → the other-party user URI. */
50
- function destinationFor(orig, line) {
51
- if (!orig || !orig.to || orig.to === orig.line)
52
- return line;
53
- return orig.from;
54
- }
55
- /** Classify a chat line as DM/group/unknown for the auto-claim group-skip rule. */
56
- /** Telegram: chat-id sign is authoritative (id < 0 ⇒ group). Discord: peek payload.guildId on */
57
- /** the most-recent inbound (null ⇒ DM, set ⇒ group, none seen ⇒ unknown). Claude/Codex ⇒ dm. */
58
- export function classifyLine(line) {
59
- const station = Line.station(line);
60
- if (station === 'telegram') {
61
- const parsed = Line.parseTelegram(line);
62
- if (!parsed)
63
- return 'unknown';
64
- return parsed.chatId < 0 ? 'group' : 'dm';
65
- }
66
- if (station === 'claude' || station === 'codex')
67
- return 'dm';
68
- if (station === 'webhook')
69
- return 'group';
70
- if (station === 'discord') {
71
- /** Look at the most recent inbound on this line; the daemon stored the raw message in `payload`. */
72
- const recent = readHistory({ line, kind: 'inbound', limit: 1 })[0];
73
- if (!recent)
74
- return 'unknown';
75
- const payload = recent.payload;
76
- if (!payload || !('guildId' in payload)) {
77
- /** Older entries may not have a guildId — fall back to the `to` field: DMs route to a user URI. */
78
- if (recent.to && recent.to !== recent.line)
79
- return 'dm';
80
- return 'unknown';
81
- }
82
- return payload.guildId == null ? 'dm' : 'group';
83
- }
84
- return 'unknown';
85
- }
86
- /** Append an outbound action to history.jsonl; `to` mirrors the destination per `destinationFor`. */
87
- function logOutbound(f, e) {
88
- const id = mintId();
89
- const fromOverride = flagOne(f, 'from');
90
- const from = fromOverride ? asLine(fromOverride) : userSelf();
91
- appendHistory({
92
- id, ts: new Date().toISOString(), station: Line.station(e.line) ?? '?',
93
- from, to: e.to ?? e.line, ...e,
94
- });
95
- maybeAutoClaim(f, e.line, from);
96
- return id;
97
- }
98
- /** Auto-claim on outbound — skips when `--no-claim` / `METRO_NO_AUTO_CLAIM=1`, when the line is */
99
- /** a group/webhook, or when already owned by someone else. `--claim` forces a group-line claim. */
100
- function maybeAutoClaim(f, line, owner) {
101
- if (f['no-claim'] === true)
102
- return;
103
- if (process.env.METRO_NO_AUTO_CLAIM === '1')
104
- return;
105
- const force = f['claim'] === true;
106
- const lineKind = classifyLine(line);
107
- const result = tryAutoClaim(line, owner, { lineKind, force });
108
- if (result.status === 'skipped') {
109
- process.stderr.write(`auto-claim skipped: line owned by ${result.existing}\n`);
110
- }
111
- else if (result.status === 'group') {
112
- process.stderr.write(`auto-claim skipped: ${line} is a group/public line; pass --claim to take it explicitly\n`);
113
- }
114
- else if (result.status === 'webhook') {
115
- process.stderr.write(`auto-claim skipped: ${line} is a webhook line (broadcast stream)\n`);
116
- }
117
- else if (result.status === 'error') {
118
- process.stderr.write(`auto-claim failed: ${result.error}\n`);
119
- }
120
- }
121
- export async function cmdSend(p, f) {
122
- need(p, 1, 'metro send <line> <text> [--image=<path>]… [--document=<path>]… [--voice=<path>] [--buttons=<json>]');
123
- loadMetroEnv();
124
- const text = await resolveText(p, 1), line = asLine(p[0]);
125
- if (Line.isLocal(line)) {
126
- const fromFlag = flagOne(f, 'from');
127
- const resp = await ipcCall({ op: 'notify', line, from: fromFlag, text });
128
- if (!resp.ok)
129
- throw new Error(resp.error);
130
- /** cross-user notify still counts as the sender taking the line: auto-claim if unclaimed */
131
- maybeAutoClaim(f, line, fromFlag ? asLine(fromFlag) : userSelf());
132
- return emit(f, `notified ${line}`, { ok: true, line, id: null, messageId: null });
133
- }
134
- const messageId = await chatStationOf(line).send(line, text, richOpts(f));
135
- /** Inherit destination from the most recent inbound on this line so DM sends address the user. */
136
- const to = destinationFor(readHistory({ line, kind: 'inbound', limit: 1 })[0], line);
137
- const id = logOutbound(f, { kind: 'outbound', line, text, messageId, to });
138
- emit(f, `sent ${id} (${messageId}) to ${line}`, { ok: true, line, id, messageId });
139
- }
140
- export async function cmdReply(p, f) {
141
- need(p, 2, 'metro reply <line> <message_id> <text> [--image=… --document=… --voice=… --buttons=…]');
142
- loadMetroEnv();
143
- const [to, replyToArg] = p, text = await resolveText(p, 2), line = asLine(to);
144
- const replyTo = resolvePlatformId(replyToArg);
145
- const messageId = await chatStationOf(line).send(line, text, { ...richOpts(f), replyTo });
146
- const id = logOutbound(f, { kind: 'outbound', line, text, messageId, replyTo: replyToArg, to: destinationFor(lookupEntry(replyToArg), line) });
147
- emit(f, `replied ${id} (${messageId}) to ${line}#${replyTo}`, { ok: true, line, id, replyTo: replyToArg, messageId });
148
- }
149
- export async function cmdEdit(p, f) {
150
- need(p, 2, 'metro edit <line> <message_id> <text> [--buttons=<json>]');
151
- loadMetroEnv();
152
- const [to, msgArg] = p, text = await resolveText(p, 2), line = asLine(to);
153
- const platformId = resolvePlatformId(msgArg);
154
- const buttons = parseButtons(f);
155
- await chatStationOf(line).edit(line, platformId, text, buttons ? { buttons } : undefined);
156
- /** Carry forward the original recipient if we have a row for this message. */
157
- const id = logOutbound(f, { kind: 'edit', line, text, messageId: platformId, replyTo: msgArg, to: lookupEntry(msgArg)?.to });
158
- emit(f, `edited ${line}#${platformId} (${id})`, { ok: true, line, id, messageId: platformId });
159
- }
160
- export async function cmdReact(p, f) {
161
- need(p, 2, 'metro react <line> <message_id> <emoji> (empty emoji clears)');
162
- loadMetroEnv();
163
- const [to, msgArg, emoji = ''] = p, line = asLine(to);
164
- const platformId = resolvePlatformId(msgArg);
165
- await chatStationOf(line).react(line, platformId, emoji);
166
- const id = logOutbound(f, { kind: 'react', line, messageId: platformId, emoji, to: destinationFor(lookupEntry(msgArg), line) });
167
- const human = emoji ? `reacted ${emoji} on ${line}#${platformId}` : `cleared reaction on ${line}#${platformId}`;
168
- emit(f, human, { ok: true, line, id, messageId: platformId, emoji });
169
- }
170
- export async function cmdDownload(p, f) {
171
- need(p, 2, 'metro download <line> <message_id> [--out=<dir>]');
172
- loadMetroEnv();
173
- const [to, msgArg] = p, line = asLine(to);
174
- const messageId = resolvePlatformId(msgArg);
175
- const outDir = typeof f.out === 'string' ? f.out : join(tmpdir(), 'metro-downloads');
176
- mkdirSync(outDir, { recursive: true });
177
- /** Telegram has no get-message-by-id REST endpoint — daemon holds the in-memory snapshot. */
178
- let files;
179
- if (Line.station(line) === 'telegram') {
180
- const resp = await ipcCall({ op: 'download', line, messageId, outDir });
181
- if (!resp.ok)
182
- throw new Error(resp.error);
183
- files = 'files' in resp ? resp.files : [];
184
- }
185
- else {
186
- files = await chatStationOf(line).download(line, messageId, outDir);
187
- }
188
- if (isJson(f))
189
- return writeJson({ ok: true, line, files });
190
- if (!files.length)
191
- process.stdout.write(`(no image attachments on ${line}#${messageId})\n`);
192
- for (const file of files)
193
- process.stdout.write(file.path + '\n');
194
- }
195
- export async function cmdFetch(p, f) {
196
- need(p, 1, 'metro fetch <line> [--limit=N]');
197
- loadMetroEnv();
198
- const line = asLine(p[0]);
199
- const messages = await chatStationOf(line).fetch(line, Number(flagOne(f, 'limit')) || 20);
200
- if (isJson(f))
201
- return writeJson({ ok: true, line, messages });
202
- if (!messages.length)
203
- process.stdout.write(`(no messages on ${line})\n`);
204
- for (const m of messages)
205
- process.stdout.write(`${m.timestamp} ${m.author}: ${m.text}\n`);
206
- }
@@ -1,185 +0,0 @@
1
- /** Setup / doctor / update — config-side commands consumed by cli.ts. */
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { spawn } from 'node:child_process';
4
- import { join } from 'node:path';
5
- import pkg from '../../package.json' with { type: 'json' };
6
- import { DiscordStation } from '../stations/discord.js';
7
- import { TelegramStation } from '../stations/telegram.js';
8
- import { errMsg } from '../log.js';
9
- import { CONFIG_ENV_FILE, configuredPlatforms, loadMetroEnv, readDotenv, STATE_DIR, writeDotenv, } from '../paths.js';
10
- import { emit, exitErr, isJson, writeJson } from './util.js';
11
- import { cmdSetupSkill, skillStatus } from './skill.js';
12
- const TOKEN_KEYS = { telegram: 'TELEGRAM_BOT_TOKEN', discord: 'DISCORD_BOT_TOKEN' };
13
- const stationFor = (p) => p === 'telegram' ? new TelegramStation() : new DiscordStation();
14
- const maskToken = (t) => !t ? '' : t.length <= 8 ? '••••' : `${t.slice(0, 6)}…${t.slice(-2)}`;
15
- /** Apply token across CONFIG_ENV_FILE (always set/cleared) AND cwd/.env (only if it exists). */
16
- function applyToken(key, value) {
17
- const out = [];
18
- for (const path of [CONFIG_ENV_FILE, join(process.cwd(), '.env')]) {
19
- if (path !== CONFIG_ENV_FILE && !existsSync(path))
20
- continue;
21
- const env = readDotenv(path);
22
- if (value === null) {
23
- if (!(key in env))
24
- continue;
25
- delete env[key];
26
- }
27
- else
28
- env[key] = value;
29
- writeDotenv(path, env);
30
- out.push(path);
31
- }
32
- return out;
33
- }
34
- async function cmdSetupStatus(f) {
35
- loadMetroEnv();
36
- const tg = process.env.TELEGRAM_BOT_TOKEN ?? '', dc = process.env.DISCORD_BOT_TOKEN ?? '';
37
- if (isJson(f))
38
- return writeJson({
39
- version: pkg.version, config_env_file: CONFIG_ENV_FILE,
40
- tokens: { telegram: { set: !!tg, masked: maskToken(tg) }, discord: { set: !!dc, masked: maskToken(dc) } },
41
- });
42
- const cfgState = existsSync(CONFIG_ENV_FILE) ? '' : ' (not yet written)';
43
- const getStarted = !tg && !dc
44
- ? 'Get started:\n 1. metro setup telegram <token> # https://t.me/BotFather'
45
- + '\n metro setup discord <token> # https://discord.com/developers/applications'
46
- + '\n 2. metro doctor\n 3. metro\n'
47
- : 'Run `metro` to start the dispatcher, or `metro doctor` to verify.\n';
48
- process.stdout.write(`metro ${pkg.version}\n\nconfig: ${CONFIG_ENV_FILE}${cfgState}\n\n`
49
- + ` TELEGRAM_BOT_TOKEN ${tg ? `set (${maskToken(tg)})` : 'not set'}\n`
50
- + ` DISCORD_BOT_TOKEN ${dc ? `set (${maskToken(dc)})` : 'not set'}\n\n${getStarted}`);
51
- }
52
- export async function cmdSetup(p, f) {
53
- const [sub, value] = p;
54
- if (!sub)
55
- return cmdSetupStatus(f);
56
- if (sub === 'skill')
57
- return cmdSetupSkill(p.slice(1), f);
58
- if (sub === 'telegram' || sub === 'discord') {
59
- if (!value)
60
- throw new Error(`metro setup ${sub} <token> — token is required`);
61
- const trimmed = value.trim();
62
- let identity;
63
- if (!f['no-validate']) {
64
- process.env[TOKEN_KEYS[sub]] = trimmed;
65
- try {
66
- const me = await stationFor(sub).getMe();
67
- identity = sub === 'telegram' ? `@${me.username}` : me.username;
68
- }
69
- catch (err) {
70
- delete process.env[TOKEN_KEYS[sub]];
71
- throw exitErr(`token rejected by ${sub}: ${errMsg(err)} (use --no-validate to save anyway)`, 3);
72
- }
73
- }
74
- const paths = applyToken(TOKEN_KEYS[sub], trimmed);
75
- const verified = identity ? ` (verified as ${identity})` : '';
76
- emit(f, `saved ${TOKEN_KEYS[sub]}${verified} to ${paths.join(', ')}\nrestart metro for the new token to take effect.`, { ok: true, saved: TOKEN_KEYS[sub], paths, verified_as: identity ?? null });
77
- return;
78
- }
79
- if (sub === 'clear') {
80
- const target = value ?? 'all';
81
- if (target !== 'all' && target !== 'telegram' && target !== 'discord')
82
- throw new Error(`metro setup clear <telegram|discord|all> — got '${target}'`);
83
- const keys = target === 'all' ? Object.values(TOKEN_KEYS) : [TOKEN_KEYS[target]];
84
- const paths = new Set();
85
- for (const k of keys)
86
- for (const path of applyToken(k, null))
87
- paths.add(path);
88
- const label = target === 'all' ? 'all metro tokens' : TOKEN_KEYS[target];
89
- emit(f, `cleared ${label} from ${[...paths].join(', ') || '(no files had it)'}\nrestart metro for changes to take effect.`, { ok: true, cleared: target, paths: [...paths] });
90
- return;
91
- }
92
- throw new Error(`unknown setup subcommand '${sub}' (try: telegram, discord, clear, skill)`);
93
- }
94
- function tokenSource(key) {
95
- const val = process.env[key];
96
- if (!val)
97
- return '';
98
- for (const path of [join(process.cwd(), '.env'), CONFIG_ENV_FILE]) {
99
- if (existsSync(path) && readDotenv(path)[key] === val)
100
- return path;
101
- }
102
- return 'process env';
103
- }
104
- export async function cmdDoctor(_, f) {
105
- loadMetroEnv();
106
- const cfg = configuredPlatforms();
107
- const checks = [];
108
- const sources = [];
109
- for (const p of ['telegram', 'discord']) {
110
- if (!cfg[p]) {
111
- checks.push({ name: p, ok: null, detail: 'not configured' });
112
- continue;
113
- }
114
- sources.push(`${p}←${tokenSource(TOKEN_KEYS[p])}`);
115
- try {
116
- const me = await stationFor(p).getMe();
117
- checks.push({ name: p, ok: true, detail: `getMe → ${p === 'telegram' ? '@' : ''}${me.username}` });
118
- }
119
- catch (err) {
120
- checks.push({ name: p, ok: false, detail: errMsg(err) });
121
- }
122
- }
123
- checks.unshift({
124
- name: 'tokens', ok: cfg.telegram || cfg.discord,
125
- detail: sources.length ? sources.join(', ') : 'no platform configured — run `metro setup telegram|discord <token>`',
126
- });
127
- const lockFile = join(STATE_DIR, '.tail-lock');
128
- if (!existsSync(lockFile))
129
- checks.push({ name: 'dispatcher', ok: null, detail: 'not running' });
130
- else
131
- try {
132
- const pid = Number(readFileSync(lockFile, 'utf8').trim());
133
- if (!Number.isInteger(pid) || pid <= 0)
134
- throw new Error('invalid pid');
135
- process.kill(pid, 0);
136
- checks.push({ name: 'dispatcher', ok: true, detail: `running (pid ${pid})` });
137
- }
138
- catch {
139
- checks.push({ name: 'dispatcher', ok: null, detail: 'stale lockfile (auto-reclaims)' });
140
- }
141
- checks.push({
142
- name: 'codex-rc', ok: null,
143
- detail: process.env.METRO_CODEX_RC
144
- ? `push enabled → ${process.env.METRO_CODEX_RC}`
145
- : 'not configured (set METRO_CODEX_RC=ws://… to enable Codex push)',
146
- });
147
- const sk = skillStatus();
148
- const installed = Object.entries(sk).filter(([, ok]) => ok).map(([r]) => r);
149
- checks.push({
150
- name: 'skill', ok: installed.length ? true : null,
151
- detail: installed.length ? `installed for ${installed.join(', ')}` : 'not installed (run `metro setup skill`)',
152
- });
153
- if (isJson(f))
154
- return writeJson({ checks });
155
- process.stdout.write('metro doctor\n\n');
156
- for (const c of checks) {
157
- const mark = c.ok === true ? '✓' : c.ok === false ? '✗' : '–';
158
- process.stdout.write(` ${mark} ${c.name.padEnd(15)} ${c.detail}\n`);
159
- }
160
- process.stdout.write('\n');
161
- if (checks.some(c => c.ok === false))
162
- throw exitErr('one or more checks failed', 3);
163
- }
164
- export async function cmdUpdate(_, f) {
165
- const tag = pkg.version.includes('-') ? 'beta' : 'latest';
166
- const res = await fetch('https://registry.npmjs.org/@stage-labs/metro', { signal: AbortSignal.timeout(15_000) });
167
- if (!res.ok)
168
- throw new Error(`npm registry: ${res.status}`);
169
- const latest = (await res.json())['dist-tags']?.[tag];
170
- if (!latest)
171
- throw new Error(`no '${tag}' dist-tag for @stage-labs/metro`);
172
- if (latest === pkg.version) {
173
- return emit(f, `already on ${pkg.version} (latest ${tag})`, { ok: true, current: pkg.version, latest, upgraded: false });
174
- }
175
- const argv1 = process.argv[1] ?? '', spec = `@stage-labs/metro@${tag}`;
176
- const argv = argv1.includes('/.bun/') || argv1.includes('\\bun\\') ? ['bun', 'add', '-g', spec]
177
- : argv1.includes('/pnpm/') || argv1.includes('\\pnpm\\') ? ['pnpm', 'add', '-g', spec]
178
- : ['npm', 'install', '-g', spec];
179
- emit(f, `metro ${pkg.version} → ${latest}\n$ ${argv.join(' ')}`, { ok: true, current: pkg.version, latest, command: argv.join(' ') });
180
- await new Promise((resolve, reject) => {
181
- const child = spawn(argv[0], argv.slice(1), { stdio: isJson(f) ? 'ignore' : 'inherit' });
182
- child.on('exit', code => code === 0 ? resolve() : reject(new Error(`${argv[0]} exited ${code}`)));
183
- child.on('error', reject);
184
- });
185
- }