@stage-labs/metro 0.1.0-beta.21 → 0.1.0-beta.211

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 (436) hide show
  1. package/dist/api.js +9 -101
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +152 -0
  4. package/dist/cli.js +52 -109
  5. package/dist/control.js +47 -75
  6. package/dist/hold.js +210 -0
  7. package/dist/local.js +43 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +44 -0
  11. package/dist/provider-flags.js +9 -0
  12. package/dist/route.js +58 -0
  13. package/dist/runtime-install.js +96 -0
  14. package/dist/runtime.js +29 -52
  15. package/dist/serve.js +157 -0
  16. package/dist/service.js +304 -0
  17. package/dist/tail.js +119 -0
  18. package/dist/update.js +13 -1
  19. package/package.json +2 -23
  20. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  21. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
  22. package/runtime/marketplace/plugin/.mcp.json +1 -0
  23. package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
  24. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
  25. package/runtime/marketplace/plugin/bin/policy.mjs +97 -0
  26. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  27. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  28. package/runtime/marketplace/plugin/orchestrator.md +26 -0
  29. package/runtime/node_modules/@metro-labs/core/package.json +47 -0
  30. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  31. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
  32. package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
  33. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  34. package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +24 -28
  35. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
  36. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
  37. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  38. package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
  39. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +83 -0
  40. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
  41. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  44. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +6 -11
  45. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +84 -0
  47. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +18 -1
  48. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  49. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  50. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +89 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +144 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +37 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +89 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +142 -0
  73. package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
  74. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-map.ts → daemon/src/agents/map.ts} +58 -1
  75. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  76. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +150 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +218 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
  84. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
  85. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
  86. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
  87. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
  88. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +70 -45
  89. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
  90. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
  91. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
  92. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +355 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +313 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +273 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  104. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
  105. package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  108. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
  109. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
  110. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
  111. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
  112. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +347 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  120. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
  121. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
  122. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  123. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
  124. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
  125. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +195 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +128 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +224 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +278 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +14 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
  155. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
  156. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  157. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +49 -20
  158. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
  159. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
  160. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
  161. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +24 -79
  162. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
  163. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
  164. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
  173. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +33 -42
  174. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  176. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
  177. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +68 -119
  178. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
  179. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  180. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  181. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  182. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  187. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +59 -51
  188. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +205 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +66 -0
  190. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  191. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  192. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  193. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  194. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  195. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  196. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  197. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  198. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
  199. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +9 -36
  200. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
  201. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
  202. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
  203. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
  204. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +75 -9
  205. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
  206. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +22 -6
  207. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  208. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
  209. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
  210. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  211. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
  212. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  213. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  214. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  215. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  216. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  217. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  218. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +9 -0
  219. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  220. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  221. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  222. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +147 -0
  223. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
  224. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
  225. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/package.json +3 -5
  226. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +8 -28
  227. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +19 -187
  228. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +3 -3
  229. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +31 -72
  230. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +2 -2
  231. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +2 -2
  232. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +15 -21
  233. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/members.ts +2 -2
  234. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  235. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/send-files.ts +12 -13
  236. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +23 -0
  237. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  238. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
  239. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
  240. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  241. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  242. package/runtime/node_modules/@metro-labs/{telegram-user → outlook}/package.json +2 -5
  243. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +93 -0
  244. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
  245. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
  246. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
  247. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
  248. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
  249. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +140 -0
  250. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
  251. package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
  252. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
  253. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  254. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
  255. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
  256. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
  257. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
  258. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
  259. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  260. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -0
  261. package/runtime/node_modules/@metro-labs/telegram/package.json +3 -2
  262. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +38 -96
  263. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +214 -253
  264. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +67 -108
  265. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +6 -6
  266. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +101 -147
  267. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +4 -4
  268. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
  269. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
  270. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +6 -102
  271. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
  272. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +64 -151
  273. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +61 -33
  274. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  275. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +3 -3
  276. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +11 -1
  277. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +5 -47
  278. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  279. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +86 -0
  280. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +240 -0
  281. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  282. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +170 -0
  283. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  284. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +90 -0
  285. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  286. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
  287. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +21 -0
  288. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
  289. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  290. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +95 -0
  291. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -0
  292. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  293. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  294. package/runtime/node_modules/@metro-labs/threema/src/files.ts +47 -0
  295. package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -0
  296. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  297. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  298. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
  299. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  300. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  301. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  302. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  303. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  304. package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
  305. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
  306. package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
  307. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
  308. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
  309. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
  310. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
  311. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
  312. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
  313. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
  314. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
  315. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
  316. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
  317. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
  318. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
  319. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
  320. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
  321. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  322. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
  323. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
  324. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
  325. package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
  326. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
  327. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
  328. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +15 -204
  329. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +4 -43
  330. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +41 -219
  331. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
  332. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +3 -3
  333. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
  334. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
  335. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
  336. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
  337. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
  338. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +12 -20
  339. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +1 -19
  340. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
  341. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  342. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  343. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  344. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  345. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +5 -1
  346. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +4 -51
  347. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +30 -44
  348. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
  349. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
  350. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
  351. package/runtime/runtime.json +1 -1
  352. package/runtime/server.ts +1 -0
  353. package/runtime/stations.json +37 -0
  354. package/dist/prompt.js +0 -10
  355. package/dist/store.js +0 -70
  356. package/runtime/node_modules/@metro-labs/discord/src/station.ts +0 -19
  357. package/runtime/node_modules/@metro-labs/discord/src/voice-speak.ts +0 -195
  358. package/runtime/node_modules/@metro-labs/discord/src/voice-transcribe.ts +0 -292
  359. package/runtime/node_modules/@metro-labs/discord/src/voice.ts +0 -197
  360. package/runtime/node_modules/@metro-labs/discord/src/wire.ts +0 -5
  361. package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
  362. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
  363. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
  364. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -335
  365. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
  366. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -322
  367. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
  368. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
  369. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
  370. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
  371. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
  372. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
  373. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
  374. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
  375. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
  376. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
  377. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
  378. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
  379. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
  380. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
  381. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
  382. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
  383. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
  384. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
  385. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
  386. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
  387. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
  388. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
  389. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
  390. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
  391. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +0 -364
  392. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
  393. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
  394. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
  395. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
  396. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
  397. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
  398. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
  399. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
  400. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
  401. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
  402. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
  403. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
  404. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
  405. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
  406. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
  407. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
  408. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
  409. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -3
  410. package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
  411. package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
  412. package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
  413. package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
  414. package/runtime/node_modules/@metro-labs/telegram-user/src/inbound.ts +0 -78
  415. package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
  416. package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
  417. package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
  418. package/runtime/node_modules/@metro-labs/telegram-user/src/normalize.ts +0 -22
  419. package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
  420. package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
  421. package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
  422. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  423. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
  424. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
  425. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
  426. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
  427. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
  428. /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
  429. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
  430. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
  431. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
  432. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/api-base.ts +0 -0
  433. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.ts +0 -0
  434. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
  435. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
  436. /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
@@ -0,0 +1,103 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { errMsg, log } from '@metro-labs/core/log';
3
+ import { tokenMatches } from '../stations/webhook-endpoints.js';
4
+ import {
5
+ findThreemaCallback,
6
+ type ThreemaCallback,
7
+ } from '../stations/threema-callbacks.js';
8
+ import { forwardTrainCall, type TrainCallBackend } from '../stations/train-call.js';
9
+ import { BodyTooLargeError, readBody } from './body.js';
10
+
11
+ const PREFIX = '/api/threema/';
12
+ const CALLBACK_PATH = /^\/api\/threema\/([0-9]{17,20})\/([A-Za-z0-9_-]{32,128})$/;
13
+ const BODY_MAX = 64 * 1024;
14
+ const REQUIRED = ['from', 'to', 'messageId', 'date', 'nonce', 'box', 'mac'] as const;
15
+
16
+ function callbackTarget(path: string): ThreemaCallback | null {
17
+ const m = CALLBACK_PATH.exec(path);
18
+ const callbackId = m?.[1];
19
+ const token = m?.[2];
20
+ if (callbackId === undefined || token === undefined) return null;
21
+ const row = findThreemaCallback(callbackId);
22
+ return row !== undefined && tokenMatches(row.callbackToken, token) ? row : null;
23
+ }
24
+
25
+ function parseFields(raw: Buffer): Record<string, string> | null {
26
+ const params = new URLSearchParams(raw.toString('utf8'));
27
+ const out: Record<string, string> = {};
28
+ for (const key of REQUIRED) {
29
+ const value = params.get(key)?.trim() ?? '';
30
+ if (value === '') return null;
31
+ out[key] = value;
32
+ }
33
+ const nickname = params.get('nickname')?.trim() ?? '';
34
+ if (nickname !== '') out.nickname = nickname;
35
+ return out;
36
+ }
37
+
38
+ async function deliver(
39
+ res: ServerResponse,
40
+ target: ThreemaCallback,
41
+ fields: Record<string, string>,
42
+ forward: TrainCallBackend,
43
+ ): Promise<void> {
44
+ let response;
45
+ try {
46
+ response = await forward('threema', 'callback', { account: target.id, ...fields });
47
+ } catch (err) {
48
+ log.warn(
49
+ { account: target.id, err: errMsg(err) },
50
+ 'threema: the callback could not reach the train, so Threema is asked to retry',
51
+ );
52
+ res.writeHead(503).end('threema train unavailable');
53
+ return;
54
+ }
55
+ if (response.error !== undefined) {
56
+ log.warn({ account: target.id, err: response.error }, 'threema: callback refused');
57
+ res.writeHead(400).end('refused');
58
+ return;
59
+ }
60
+ res.writeHead(200).end('ok');
61
+ }
62
+
63
+ async function readFields(
64
+ req: IncomingMessage,
65
+ res: ServerResponse,
66
+ ): Promise<Record<string, string> | null> {
67
+ let raw: Buffer;
68
+ try {
69
+ raw = await readBody(req, BODY_MAX);
70
+ } catch (err) {
71
+ if (!(err instanceof BodyTooLargeError)) throw err;
72
+ res.writeHead(413).end('payload too large');
73
+ return null;
74
+ }
75
+ const fields = parseFields(raw);
76
+ if (fields === null) res.writeHead(400).end('missing callback fields');
77
+ return fields;
78
+ }
79
+
80
+ export async function handleThreemaCallback(
81
+ req: IncomingMessage,
82
+ res: ServerResponse,
83
+ forward: TrainCallBackend = forwardTrainCall,
84
+ ): Promise<boolean> {
85
+ const path = (req.url ?? '').split('?')[0] ?? '';
86
+ if (!path.startsWith(PREFIX)) return false;
87
+ const target = callbackTarget(path);
88
+ if (target === null) {
89
+ res.writeHead(404).end();
90
+ return true;
91
+ }
92
+ if (req.method === 'GET') {
93
+ res.writeHead(200).end(`metro threema callback ${target.callbackId} ready\n`);
94
+ return true;
95
+ }
96
+ if (req.method !== 'POST') {
97
+ res.writeHead(405).end();
98
+ return true;
99
+ }
100
+ const fields = await readFields(req, res);
101
+ if (fields !== null) await deliver(res, target, fields, forward);
102
+ return true;
103
+ }
@@ -0,0 +1,39 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { ApiError } from '@metro-labs/http/api-error';
3
+ import { sessionRoute } from '@metro-labs/http/api-http';
4
+ import { log } from '@metro-labs/core/log';
5
+
6
+ const STOP = '/api/stop';
7
+ const RESTART = '/api/restart';
8
+ const EXIT_DELAY_MS = 500;
9
+
10
+ export interface ControlApiDeps {
11
+ restart: () => void;
12
+ stop: () => void;
13
+ served?: () => boolean;
14
+ }
15
+
16
+ interface Action {
17
+ run: () => void;
18
+ body: Record<string, boolean>;
19
+ }
20
+
21
+ const servedByCli = (): boolean => (process.env.METRO_CLI_BIN?.trim() ?? '') !== '';
22
+
23
+ function actionFor(path: string, deps: ControlApiDeps): Action {
24
+ return path === STOP ? { run: deps.stop, body: { stopping: true } } : { run: deps.restart, body: { restarting: true } };
25
+ }
26
+
27
+ export function handleControlRequest(req: IncomingMessage, res: ServerResponse, deps: ControlApiDeps): boolean {
28
+ return sessionRoute(req, res, { methods: { [STOP]: ['POST'], [RESTART]: ['POST'] }, admin: true, label: 'control-api' }, async (session, path) => {
29
+ const action = actionFor(path, deps);
30
+ if (!(deps.served ?? servedByCli)())
31
+ throw new ApiError(
32
+ 'this daemon was not started by metro serve, so nothing on the machine would bring it back: use the shell instead',
33
+ 400,
34
+ );
35
+ log.info({ path, subject: session.subject }, 'control-api: requested from the page');
36
+ setTimeout(action.run, EXIT_DELAY_MS).unref();
37
+ return Promise.resolve(action.body);
38
+ });
39
+ }
@@ -0,0 +1,58 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { hostname, homedir } from 'node:os';
3
+ import { statfs } from 'node:fs/promises';
4
+ import { errMsg, log } from '@metro-labs/core/log';
5
+ import { sessionRoute } from '@metro-labs/http/api-http';
6
+ import { publicBaseUrl, webhookPort } from '../files/attach-serve.js';
7
+ import { claudeDir } from '../claude/files.js';
8
+ import { METRO_VERSION } from '@metro-labs/core/version';
9
+ import { agentsDir } from '../agents/files.js';
10
+ import { localOwner } from '../agents/file-admin.js';
11
+
12
+ const PATH = '/api/server';
13
+
14
+ export interface MachineApiDeps {
15
+ startedAt?: string;
16
+ }
17
+
18
+ const bootedAt = new Date(Date.now() - process.uptime() * 1000).toISOString();
19
+
20
+ interface Disk {
21
+ path: string;
22
+ totalBytes: number;
23
+ freeBytes: number;
24
+ }
25
+
26
+ async function diskInfo(path = homedir()): Promise<Disk | null> {
27
+ try {
28
+ const s = await statfs(path);
29
+ return { path, totalBytes: s.bsize * s.blocks, freeBytes: s.bsize * s.bavail };
30
+ } catch (err) {
31
+ log.warn({ err: errMsg(err), path }, 'machine: could not read the disk');
32
+ return null;
33
+ }
34
+ }
35
+
36
+ export async function machineInfo(startedAt = bootedAt): Promise<Record<string, unknown>> {
37
+ const store = process.env.METRO_RUNTIME_STORE?.trim() ?? '';
38
+ return {
39
+ disk: await diskInfo(),
40
+ version: METRO_VERSION,
41
+ owner: localOwner(),
42
+ hostname: hostname(),
43
+ platform: process.platform,
44
+ arch: process.arch,
45
+ port: webhookPort(),
46
+ publicUrl: publicBaseUrl(),
47
+ uptimeSeconds: Math.round(process.uptime()),
48
+ startedAt,
49
+ bun: process.versions.bun ?? null,
50
+ agentsDir: agentsDir(),
51
+ claudeDir: claudeDir(),
52
+ runtimeStore: store === '' ? null : store,
53
+ };
54
+ }
55
+
56
+ export function handleMachineRequest(req: IncomingMessage, res: ServerResponse, deps: MachineApiDeps): boolean {
57
+ return sessionRoute(req, res, { methods: { [PATH]: ['GET'] }, admin: false, label: 'machine-api' }, () => machineInfo(deps.startedAt));
58
+ }
@@ -0,0 +1,26 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { ApiError } from '@metro-labs/http/api-error';
3
+ import { readJsonBody, sessionRoute, type ApiSession } from '@metro-labs/http/api-http';
4
+ import { isOrganizationId } from '@metro-labs/http/workos-token';
5
+ import { isRecord } from '@metro-labs/core/is-record';
6
+ import { log } from '@metro-labs/core/log';
7
+
8
+ const PATH = '/api/owner';
9
+
10
+ export interface OwnerApiDeps {
11
+ setOwner: (owner: string) => string;
12
+ }
13
+
14
+ async function move(req: IncomingMessage, session: ApiSession, deps: OwnerApiDeps): Promise<{ owner: string }> {
15
+ const body = await readJsonBody(req);
16
+ const owner = isRecord(body) && typeof body.owner === 'string' ? body.owner.trim() : '';
17
+ if (!isOrganizationId(owner)) throw new ApiError('owner must be an organization id', 400);
18
+ if (owner === session.subject) throw new ApiError('this machine already belongs to that organization', 409);
19
+ const saved = deps.setOwner(owner);
20
+ log.info({ from: session.subject, to: saved }, 'owner: this machine moved to another organization');
21
+ return { owner: saved };
22
+ }
23
+
24
+ export function handleOwnerRequest(req: IncomingMessage, res: ServerResponse, deps: OwnerApiDeps): boolean {
25
+ return sessionRoute(req, res, { methods: { [PATH]: ['POST'] }, admin: true, label: 'owner-api' }, (session) => move(req, session, deps));
26
+ }
@@ -0,0 +1,90 @@
1
+ import { execFile } from 'node:child_process';
2
+ import type { IncomingMessage, ServerResponse } from 'node:http';
3
+ import { log } from '@metro-labs/core/log';
4
+ import { ApiError } from '@metro-labs/http/api-error';
5
+ import { sessionRoute } from '@metro-labs/http/api-http';
6
+ import { isRecord } from '@metro-labs/core/is-record';
7
+ import { METRO_VERSION } from '@metro-labs/core/version';
8
+
9
+ const PATH = '/api/update';
10
+ const CHECK_TTL_MS = 10 * 60_000;
11
+ const CHECK_TIMEOUT_MS = 30_000;
12
+ const UPDATE_TIMEOUT_MS = 5 * 60_000;
13
+ const RESTART_DELAY_MS = 500;
14
+ const OUTPUT_TAIL = 800;
15
+
16
+ interface UpdateCheck {
17
+ current: string;
18
+ latest: string;
19
+ newer: boolean;
20
+ }
21
+
22
+ interface UpdateResult {
23
+ updated: boolean;
24
+ version: string;
25
+ restarting: boolean;
26
+ }
27
+
28
+ export interface UpdateApiDeps {
29
+ restart: () => void;
30
+ cliBin?: () => string;
31
+ }
32
+
33
+ const cliBinFromEnv = (): string => process.env.METRO_CLI_BIN?.trim() ?? '';
34
+
35
+ function runCli(bin: string, args: string[], timeout: number): Promise<{ stdout: string; stderr: string }> {
36
+ return new Promise((resolve, reject) => {
37
+ execFile(process.execPath, [bin, ...args], { timeout, maxBuffer: 4 * 1024 * 1024 }, (err, stdout, stderr) => {
38
+ if (err === null) resolve({ stdout, stderr });
39
+ else reject(new ApiError(`metro update failed: ${(stderr || err.message).trim().slice(-OUTPUT_TAIL)}`, 502));
40
+ });
41
+ });
42
+ }
43
+
44
+ let cached: { at: number; check: UpdateCheck } | null = null;
45
+
46
+ function parseCheck(raw: string): UpdateCheck {
47
+ let body: unknown;
48
+ try {
49
+ body = JSON.parse(raw.trim().split('\n').at(-1) ?? '');
50
+ } catch {
51
+ throw new ApiError('metro update --check returned no report', 502);
52
+ }
53
+ if (!isRecord(body) || typeof body.current !== 'string' || typeof body.latest !== 'string')
54
+ throw new ApiError('metro update --check returned an unexpected report', 502);
55
+ return { current: body.current, latest: body.latest, newer: body.newer === true };
56
+ }
57
+
58
+ async function check(bin: string, now = Date.now()): Promise<UpdateCheck> {
59
+ if (cached !== null && now - cached.at < CHECK_TTL_MS) return cached.check;
60
+ const out = await runCli(bin, ['update', '--check'], CHECK_TIMEOUT_MS);
61
+ cached = { at: now, check: parseCheck(out.stdout) };
62
+ return cached.check;
63
+ }
64
+
65
+ async function apply(bin: string, deps: UpdateApiDeps): Promise<UpdateResult> {
66
+ const before = await check(bin);
67
+ if (!before.newer) return { updated: false, version: before.current, restarting: false };
68
+ const out = await runCli(bin, ['update'], UPDATE_TIMEOUT_MS);
69
+ cached = null;
70
+ log.info({ from: before.current, to: before.latest, output: out.stderr.trim().slice(-OUTPUT_TAIL) }, 'update-api: metro updated, restarting');
71
+ setTimeout(() => {
72
+ deps.restart();
73
+ }, RESTART_DELAY_MS).unref();
74
+ return { updated: true, version: before.latest, restarting: true };
75
+ }
76
+
77
+ function binOrThrow(deps: UpdateApiDeps): string {
78
+ const bin = (deps.cliBin ?? cliBinFromEnv)();
79
+ if (bin === '')
80
+ throw new ApiError('this daemon was not started by metro serve, so it cannot update itself: run metro update on the machine', 400);
81
+ return bin;
82
+ }
83
+
84
+ export function handleUpdateRequest(req: IncomingMessage, res: ServerResponse, deps: UpdateApiDeps): boolean {
85
+ return sessionRoute(req, res, { methods: { [PATH]: ['GET', 'POST'] }, admin: ['POST'], label: 'update-api' }, async () => {
86
+ const bin = binOrThrow(deps);
87
+ if (req.method === 'GET') return { ...(await check(bin)), running: METRO_VERSION };
88
+ return apply(bin, deps);
89
+ });
90
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import './boot/boot.js';
@@ -0,0 +1,42 @@
1
+ export type AttachStep = 'code' | 'password' | 'scan' | 'pair' | 'device' | 'browser';
2
+
3
+ export interface AttachPrompt {
4
+ step: AttachStep;
5
+ prompt: string;
6
+ qr?: string;
7
+ pairingCode?: string;
8
+ userCode?: string;
9
+ verificationUri?: string;
10
+ authorizeUrl?: string;
11
+ }
12
+
13
+ export interface AttachOutcome {
14
+ config: Record<string, unknown>;
15
+ identity: Record<string, string>;
16
+ }
17
+
18
+ export interface DriverHooks {
19
+ prompt: (p: AttachPrompt) => void;
20
+ done: (o: AttachOutcome) => void;
21
+ fail: (message: string) => void;
22
+ }
23
+
24
+ export interface StepInput {
25
+ code?: unknown;
26
+ password?: unknown;
27
+ state?: unknown;
28
+ mode?: unknown;
29
+ error?: unknown;
30
+ errorDescription?: unknown;
31
+ }
32
+
33
+ export interface AttachDriver {
34
+ submit: (input: StepInput) => Promise<void>;
35
+ cancel: () => Promise<void>;
36
+ }
37
+
38
+ export interface StartedAttach {
39
+ driver: AttachDriver;
40
+ prompt: AttachPrompt;
41
+ expiresAt?: number;
42
+ }
@@ -1,4 +1,5 @@
1
- import { mkdtemp, rm, writeFile } from 'node:fs/promises';
1
+ import { mkdtemp, open, rm, writeFile } from 'node:fs/promises';
2
+ import { assertAttachmentSize } from '@metro-labs/core/stations/attachments';
2
3
  import { tmpdir } from 'node:os';
3
4
  import { join } from 'node:path';
4
5
 
@@ -98,3 +99,30 @@ export async function writeInlineTemp(
98
99
 
99
100
  export const removeInlineTemp = (dir: string): Promise<void> =>
100
101
  rm(dir, { recursive: true, force: true });
102
+
103
+ export async function streamToTemp(
104
+ body: ReadableStream<Uint8Array>,
105
+ name: string | undefined,
106
+ ): Promise<InlineTemp & { bytes: number }> {
107
+ const dir = await mkdtemp(join(tmpdir(), INLINE_TEMP_PREFIX));
108
+ const path = join(dir, safeFileName(name));
109
+ const handle = await open(path, 'wx', 0o600);
110
+ const reader = body.getReader();
111
+ let bytes = 0;
112
+ try {
113
+ for (;;) {
114
+ const { done, value } = await reader.read();
115
+ if (done) break;
116
+ bytes += value.length;
117
+ assertAttachmentSize(bytes);
118
+ await handle.write(value);
119
+ }
120
+ } catch (err) {
121
+ await reader.cancel().catch(() => undefined);
122
+ await handle.close();
123
+ await removeInlineTemp(dir);
124
+ throw err;
125
+ }
126
+ await handle.close();
127
+ return { dir, path, bytes };
128
+ }
@@ -1,39 +1,11 @@
1
1
  import { StationAttachError } from './attach.js';
2
+ import type { DriverHooks, StartedAttach } from './attach-driver.js';
3
+ import { startOutlook } from './attach-outlook.js';
2
4
 
3
- export const INTERACTIVE_STATIONS = ['telegram-user', 'whatsapp'] as const;
5
+ export const INTERACTIVE_STATIONS = ['telegram', 'whatsapp', 'outlook'] as const;
4
6
 
5
7
  export type InteractiveStation = (typeof INTERACTIVE_STATIONS)[number];
6
8
 
7
- export type AttachStep = 'code' | 'password' | 'scan' | 'pair';
8
-
9
- export interface AttachPrompt {
10
- step: AttachStep;
11
- prompt: string;
12
- qr?: string;
13
- pairingCode?: string;
14
- }
15
-
16
- export interface AttachOutcome {
17
- config: Record<string, unknown>;
18
- identity: Record<string, string>;
19
- }
20
-
21
- export interface DriverHooks {
22
- prompt: (p: AttachPrompt) => void;
23
- done: (o: AttachOutcome) => void;
24
- fail: (message: string) => void;
25
- }
26
-
27
- export interface AttachDriver {
28
- submit: (input: { code?: unknown; password?: unknown }) => Promise<void>;
29
- cancel: () => Promise<void>;
30
- }
31
-
32
- export interface StartedAttach {
33
- driver: AttachDriver;
34
- prompt: AttachPrompt;
35
- }
36
-
37
9
  const CODE_PROMPT =
38
10
  'Telegram sent a login code to that number. Enter it here.';
39
11
  const PASSWORD_PROMPT =
@@ -61,12 +33,12 @@ async function startTelegramUser(
61
33
  input: Record<string, unknown>,
62
34
  hooks: DriverHooks,
63
35
  ): Promise<StartedAttach> {
64
- const { TelegramUserLogin, TelegramUserLoginError, validateCredentials } =
65
- await import('@metro-labs/telegram-user/login');
66
- const isOwn = (e: unknown): boolean => e instanceof TelegramUserLoginError;
36
+ const { TelegramLogin, TelegramLoginError, validateCredentials } =
37
+ await import('@metro-labs/telegram/login');
38
+ const isOwn = (e: unknown): boolean => e instanceof TelegramLoginError;
67
39
  let login;
68
40
  try {
69
- login = new TelegramUserLogin(
41
+ login = new TelegramLogin(
70
42
  validateCredentials({
71
43
  apiId: input.apiId,
72
44
  apiHash: input.apiHash,
@@ -155,7 +127,8 @@ export async function startInteractiveAttach(
155
127
  input: Record<string, unknown>,
156
128
  hooks: DriverHooks,
157
129
  ): Promise<StartedAttach> {
158
- return station === 'telegram-user'
130
+ if (station === 'outlook') return startOutlook(input, hooks);
131
+ return station === 'telegram'
159
132
  ? startTelegramUser(input, hooks)
160
133
  : startWhatsapp(input, hooks);
161
134
  }
@@ -0,0 +1,63 @@
1
+ import type { OutlookLogin } from '@metro-labs/outlook/login';
2
+ import { StationAttachError } from './attach.js';
3
+ import type { DriverHooks, StartedAttach, StepInput } from './attach-driver.js';
4
+
5
+ const BROWSER_PROMPT =
6
+ 'Sign in with Microsoft in a new tab, with the mailbox this agent should read. This page updates on its own once you are done.';
7
+ const DEVICE_PROMPT =
8
+ 'Open the Microsoft sign-in page, type this code, and sign in with the mailbox this agent should read.';
9
+ const OTHER_ATTEMPT = 'This sign-in link belongs to another attempt. Start again from the Channels page.';
10
+ const BROWSER_TTL_MS = 15 * 60_000;
11
+
12
+ const text = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');
13
+
14
+ export async function startOutlook(input: Record<string, unknown>, hooks: DriverHooks): Promise<StartedAttach> {
15
+ const { OutlookBrowserLogin, OutlookLogin: DeviceLogin, OutlookLoginError, failureOf, parseMailbox } = await import('@metro-labs/outlook/login');
16
+ const said = (err: unknown, fallback: string): string =>
17
+ err instanceof OutlookLoginError && err.message !== '' ? err.message : fallback;
18
+ const { browser, mailbox } = (() => {
19
+ try {
20
+ const wanted = parseMailbox(input.mailbox);
21
+ return { browser: new OutlookBrowserLogin({ mailbox: wanted }), mailbox: wanted };
22
+ } catch (err) {
23
+ throw new StationAttachError(said(err, 'Microsoft refused to start the sign-in'), 400);
24
+ }
25
+ })();
26
+ let device: OutlookLogin | null = null;
27
+
28
+ const toDevice = async (): Promise<void> => {
29
+ if (device !== null) return;
30
+ const login = new DeviceLogin({ onDone: hooks.done, onFailed: hooks.fail }, { mailbox });
31
+ const code = await login.start().catch((err: unknown) => {
32
+ throw new StationAttachError(said(err, 'Microsoft refused to start the sign-in'), 400);
33
+ });
34
+ device = login;
35
+ hooks.prompt({ step: 'device', prompt: DEVICE_PROMPT, userCode: code.userCode, verificationUri: code.verificationUri });
36
+ };
37
+
38
+ const finish = async (input: StepInput): Promise<void> => {
39
+ const state = text(input.state);
40
+ if (state !== browser.state) throw new StationAttachError(OTHER_ATTEMPT, 400);
41
+ const error = text(input.error);
42
+ if (error !== '') {
43
+ hooks.fail(failureOf({ error, error_description: text(input.errorDescription) }));
44
+ return;
45
+ }
46
+ const code = text(input.code);
47
+ if (code === '') throw new StationAttachError('Microsoft did not send a sign-in code back', 400);
48
+ try {
49
+ hooks.done(await browser.finish(code, state));
50
+ } catch (err) {
51
+ hooks.fail(said(err, 'Metro could not finish the Microsoft sign-in.'));
52
+ }
53
+ };
54
+
55
+ return {
56
+ prompt: { step: 'browser', prompt: BROWSER_PROMPT, authorizeUrl: browser.authorizeUrl },
57
+ expiresAt: Date.now() + BROWSER_TTL_MS,
58
+ driver: {
59
+ cancel: () => device?.cancel() ?? Promise.resolve(),
60
+ submit: (input) => (input.mode === 'device' ? toDevice() : finish(input)),
61
+ },
62
+ };
63
+ }
@@ -2,18 +2,20 @@ import {
2
2
  assertAttachmentSize,
3
3
  assertContentLength,
4
4
  guessMime,
5
- saveBufferToCache,
6
- } from './attachments.js';
5
+ } from '@metro-labs/core/stations/attachments';
7
6
  import {
8
7
  assertInlineTotal,
9
8
  decodeInline,
10
9
  removeInlineTemp,
11
10
  splitInlineData,
11
+ streamToTemp,
12
12
  writeInlineTemp,
13
13
  } from './attach-inline.js';
14
14
  import { realpathSync } from 'node:fs';
15
- import { readUpload, UPLOAD_TTL_MS } from '../daemon/upload-store.js';
16
- import type { CanonicalAttachment } from './types.js';
15
+ import { agentCommand, agentUser, type AgentUser } from '../agent-user/user.js';
16
+ import { log } from '@metro-labs/core/log';
17
+ import { readUpload, UPLOAD_TTL_MS } from '../files/upload-store.js';
18
+ import type { CanonicalAttachment } from '@metro-labs/core/stations/types';
17
19
 
18
20
  export interface ResolvedAttachment {
19
21
  path: string;
@@ -38,8 +40,6 @@ const basenameOf = (src: string): string =>
38
40
 
39
41
  const isHttpUrl = (src: string): boolean => /^https?:\/\//i.test(src);
40
42
 
41
- const mintId = (): string => Math.random().toString(36).slice(2, 12);
42
-
43
43
  const HIDDEN_SEGMENT = /(^|\/)\.[^/]/;
44
44
 
45
45
  function assertReadablePath(path: string): void {
@@ -57,11 +57,38 @@ function assertReadablePath(path: string): void {
57
57
  );
58
58
  }
59
59
 
60
+ async function fromPathAs(
61
+ user: AgentUser,
62
+ a: CanonicalAttachment,
63
+ path: string,
64
+ ): Promise<ResolvedAttachment> {
65
+ const proc = Bun.spawn(agentCommand(['cat', '--', path]), { stdout: 'pipe', stderr: 'pipe' });
66
+ const name = a.name ?? basenameOf(path) ?? 'attachment';
67
+ let saved: Awaited<ReturnType<typeof streamToTemp>>;
68
+ try {
69
+ saved = await streamToTemp(proc.stdout, name);
70
+ } catch (err) {
71
+ proc.kill();
72
+ throw err;
73
+ }
74
+ if ((await proc.exited) !== 0) {
75
+ await removeInlineTemp(saved.dir);
76
+ throw new Error(
77
+ `attachment path '${path}' cannot be read by the ${user.name} user Claude Code runs as. ` +
78
+ '`path` is read on the daemon machine with the agent\'s own rights; pass `upload` instead.',
79
+ );
80
+ }
81
+ assertAttachmentSize(saved.bytes);
82
+ return { path: saved.path, mime: a.mime ?? guessMime(path), name, bytes: saved.bytes, temp: saved.dir };
83
+ }
84
+
60
85
  async function fromPath(
61
86
  a: CanonicalAttachment,
62
87
  path: string,
63
88
  ): Promise<ResolvedAttachment> {
64
89
  assertReadablePath(path);
90
+ const user = agentUser();
91
+ if (user !== null) return fromPathAs(user, a, path);
65
92
  const file = Bun.file(path);
66
93
  if (!(await file.exists()))
67
94
  throw new Error(
@@ -92,15 +119,12 @@ async function fromUrl(
92
119
  if (!res.ok)
93
120
  throw new Error(`attachment fetch failed ${res.status} for '${url}'`);
94
121
  assertContentLength(res.headers.get('content-length'));
95
- const data = new Uint8Array(await res.arrayBuffer());
96
122
  const mime =
97
123
  a.mime ?? res.headers.get('content-type')?.split(';')[0] ?? guessMime(url);
98
124
  const name = a.name ?? basenameOf(url) ?? 'attachment';
99
- const saved = await saveBufferToCache(data, `out${mintId()}`, 0, {
100
- mime,
101
- name,
102
- });
103
- return { path: saved.path, mime, name, bytes: data.length };
125
+ if (res.body === null) throw new Error(`attachment fetch returned no body for '${url}'`);
126
+ const { dir, path, bytes } = await streamToTemp(res.body, name);
127
+ return { path, mime, name, bytes, temp: dir };
104
128
  }
105
129
 
106
130
  async function fromData(
@@ -151,7 +175,7 @@ const sourcesOf = (a: CanonicalAttachment): string[] =>
151
175
  return typeof v === 'string' && v.length > 0;
152
176
  });
153
177
 
154
- export async function resolveAttachment(
178
+ async function resolveAttachment(
155
179
  a: CanonicalAttachment,
156
180
  budget: InlineBudget = { used: 0 },
157
181
  opts: ResolveOptions = {},
@@ -167,6 +191,7 @@ export async function resolveAttachment(
167
191
  .map((s) => `\`${s}\``)
168
192
  .join(', ')}); pass exactly one of \`upload\`, \`data\`, \`url\` or \`path\``,
169
193
  );
194
+ log.info({ source: sources[0] }, 'send: attachment source');
170
195
  if (a.upload) return fromUpload(a, a.upload, opts.allowed);
171
196
  if (a.data) return fromData(a, a.data, budget);
172
197
  if (a.path) return fromPath(a, a.path);