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

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 +284 -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,150 @@
1
+ import {
2
+ OutlookAuthError,
3
+ pollDeviceCode,
4
+ requestDeviceCode,
5
+ type DeviceCode,
6
+ type FetchLike,
7
+ type Poll,
8
+ type Tokens,
9
+ } from './auth.js';
10
+ import { authorizeUrl, newState, pkcePair, redeemCode, type Pkce } from './browser.js';
11
+ import { verifyMailbox, type Mailbox } from './me.js';
12
+
13
+ export { parseMailbox } from './me.js';
14
+
15
+ export { OutlookAuthError as OutlookLoginError } from './auth.js';
16
+ export { NOT_SET_UP } from './config.js';
17
+ export { failureOf } from './auth.js';
18
+
19
+ export interface OutlookLoginResult {
20
+ config: Record<string, unknown>;
21
+ identity: Record<string, string>;
22
+ }
23
+
24
+ export interface OutlookLoginEvents {
25
+ onDone: (result: OutlookLoginResult) => void;
26
+ onFailed: (message: string) => void;
27
+ }
28
+
29
+ export interface OutlookLoginDeps {
30
+ fetch?: FetchLike;
31
+ now?: () => number;
32
+ mailbox?: string | null;
33
+ }
34
+
35
+ const SLOW_DOWN_MS = 5_000;
36
+
37
+ function resultOf(tokens: Tokens, tenantId: string | null, mailbox: Mailbox, now: number): OutlookLoginResult {
38
+ return {
39
+ config: {
40
+ accountEmail: mailbox.email,
41
+ refreshToken: tokens.refreshToken,
42
+ accessToken: tokens.accessToken,
43
+ expiresAt: tokens.expiresAt,
44
+ tenantId,
45
+ createdAt: new Date(now).toISOString(),
46
+ },
47
+ identity: { email: mailbox.email, ...(mailbox.name === '' ? {} : { name: mailbox.name }) },
48
+ };
49
+ }
50
+
51
+ export class OutlookBrowserLogin {
52
+ readonly state = newState();
53
+ readonly authorizeUrl: string;
54
+ private readonly pkce: Pkce = pkcePair();
55
+ private used = false;
56
+ private readonly fetchImpl: FetchLike;
57
+ private readonly now: () => number;
58
+ private readonly mailbox: string | null;
59
+
60
+ constructor(deps: OutlookLoginDeps = {}) {
61
+ this.fetchImpl = deps.fetch ?? ((input, init) => fetch(input, init));
62
+ this.now = deps.now ?? Date.now;
63
+ this.mailbox = deps.mailbox ?? null;
64
+ this.authorizeUrl = authorizeUrl(this.pkce.challenge, this.state, this.mailbox);
65
+ }
66
+
67
+ async finish(code: string, state: string): Promise<OutlookLoginResult> {
68
+ if (state !== this.state)
69
+ throw new OutlookAuthError('This sign-in link belongs to another attempt. Start again from the Channels page.');
70
+ if (this.used) throw new OutlookAuthError('This sign-in was already used. Start again from the Channels page.');
71
+ this.used = true;
72
+ const { tokens, tenantId } = await redeemCode(code, this.pkce.verifier, this.fetchImpl, this.now());
73
+ return resultOf(tokens, tenantId, await verifyMailbox(tokens.accessToken, this.fetchImpl, this.mailbox), this.now());
74
+ }
75
+ }
76
+
77
+ export class OutlookLogin {
78
+ private code: DeviceCode | null = null;
79
+ private timer: ReturnType<typeof setTimeout> | null = null;
80
+ private stopped = false;
81
+ private readonly fetchImpl: FetchLike;
82
+ private readonly now: () => number;
83
+ private readonly mailbox: string | null;
84
+
85
+ constructor(private readonly events: OutlookLoginEvents, deps: OutlookLoginDeps = {}) {
86
+ this.fetchImpl = deps.fetch ?? ((input, init) => fetch(input, init));
87
+ this.now = deps.now ?? Date.now;
88
+ this.mailbox = deps.mailbox ?? null;
89
+ }
90
+
91
+ async start(): Promise<DeviceCode> {
92
+ const code = await requestDeviceCode(this.fetchImpl, this.now());
93
+ this.code = code;
94
+ this.schedule(code.intervalMs);
95
+ return code;
96
+ }
97
+
98
+ cancel(): Promise<void> {
99
+ this.stopped = true;
100
+ if (this.timer !== null) clearTimeout(this.timer);
101
+ this.timer = null;
102
+ return Promise.resolve();
103
+ }
104
+
105
+ private schedule(delayMs: number): void {
106
+ if (this.stopped) return;
107
+ this.timer = setTimeout(() => {
108
+ this.tick().catch((err: unknown) => {
109
+ this.fail(err instanceof OutlookAuthError ? err.message : 'Metro could not finish the Microsoft sign-in.');
110
+ });
111
+ }, delayMs);
112
+ this.timer.unref();
113
+ }
114
+
115
+ private fail(message: string): void {
116
+ if (this.stopped) return;
117
+ this.stopped = true;
118
+ this.events.onFailed(message);
119
+ }
120
+
121
+ private async tick(): Promise<void> {
122
+ const code = this.code;
123
+ if (code === null || this.stopped) return;
124
+ if (this.now() >= code.expiresAt) {
125
+ this.fail('The sign-in code expired before it was used. Start again.');
126
+ return;
127
+ }
128
+ await this.settle(code, await pollDeviceCode(code, this.fetchImpl, this.now()));
129
+ }
130
+
131
+ private async settle(code: DeviceCode, poll: Poll): Promise<void> {
132
+ if (poll.kind === 'pending') {
133
+ this.schedule(code.intervalMs);
134
+ return;
135
+ }
136
+ if (poll.kind === 'slow_down') {
137
+ code.intervalMs += SLOW_DOWN_MS;
138
+ this.schedule(code.intervalMs);
139
+ return;
140
+ }
141
+ if (poll.kind === 'failed') {
142
+ this.fail(poll.message);
143
+ return;
144
+ }
145
+ const mailbox = await verifyMailbox(poll.tokens.accessToken, this.fetchImpl, this.mailbox);
146
+ if (this.stopped) return;
147
+ this.stopped = true;
148
+ this.events.onDone(resultOf(poll.tokens, poll.tenantId, mailbox, this.now()));
149
+ }
150
+ }
@@ -0,0 +1,41 @@
1
+ import { OutlookAuthError, type FetchLike } from './auth.js';
2
+ import { graphBase } from './config.js';
3
+
4
+ export interface Mailbox {
5
+ email: string;
6
+ name: string;
7
+ }
8
+
9
+ const text = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');
10
+
11
+ const MAILBOX_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
12
+
13
+ export function parseMailbox(value: unknown): string | null {
14
+ const mailbox = text(value).toLowerCase();
15
+ if (mailbox === '') return null;
16
+ if (!MAILBOX_RE.test(mailbox))
17
+ throw new OutlookAuthError('Type the whole address of the mailbox, like andy@company.com, or leave it blank.');
18
+ return mailbox;
19
+ }
20
+
21
+ const wrongMailbox = (actual: string, wanted: string): string =>
22
+ `You signed in as ${actual}, not ${wanted}. Nothing was connected. Start again and pick ${wanted} on Microsoft's page, or use "Use another account".`;
23
+
24
+ export async function verifyMailbox(accessToken: string, fetchImpl: FetchLike, wanted: string | null = null): Promise<Mailbox> {
25
+ let res: Response;
26
+ try {
27
+ res = await fetchImpl(`${graphBase()}/me?$select=mail,userPrincipalName,displayName`, {
28
+ headers: { authorization: `Bearer ${accessToken}`, accept: 'application/json' },
29
+ });
30
+ } catch (err) {
31
+ throw new OutlookAuthError(`Metro could not reach Microsoft Graph: ${err instanceof Error ? err.message : String(err)}`);
32
+ }
33
+ if (!res.ok) throw new OutlookAuthError(`Microsoft Graph refused the new sign-in (${String(res.status)}), so nothing was connected.`);
34
+ const body = (await res.json().catch(() => ({}))) as Record<string, unknown>;
35
+ const mail = text(body.mail).toLowerCase();
36
+ const principal = text(body.userPrincipalName).toLowerCase();
37
+ const email = mail || principal;
38
+ if (email === '') throw new OutlookAuthError('Microsoft Graph did not say which mailbox this is, so nothing was connected.');
39
+ if (wanted !== null && wanted !== mail && wanted !== principal) throw new OutlookAuthError(wrongMailbox(email, wanted));
40
+ return { email, name: text(body.displayName) };
41
+ }
@@ -0,0 +1,116 @@
1
+ import { respond } from '@metro-labs/core/stations/station-runtime';
2
+ import { TrainError } from '@metro-labs/core/train-error';
3
+ import { accountOf, type Account } from './accounts.js';
4
+ import { assertSendable, attachFile, filesOf, type OutgoingFile } from './attachments.js';
5
+ import { conversationOfLine, lineOf } from './format.js';
6
+ import { graph, graphJson, jsonInit, messagePath, odataQuote, queryOf } from './graph.js';
7
+ import { escapeHtml } from './html.js';
8
+
9
+ type Args = Record<string, unknown>;
10
+
11
+ export const EPOCH = '1900-01-01T00:00:00Z';
12
+
13
+ const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');
14
+
15
+ export async function latestInConversation(acct: Account, conversationId: string): Promise<string> {
16
+ const filter = `receivedDateTime ge ${EPOCH} and conversationId eq ${odataQuote(conversationId)}`;
17
+ const params = queryOf({ $filter: filter, $orderby: 'receivedDateTime desc', $top: '1', $select: 'id' });
18
+ const body = await graphJson<{ value?: { id: string }[] }>(acct, `/me/messages?${params}`);
19
+ const id = body.value?.[0]?.id;
20
+ if (id === undefined)
21
+ throw new TrainError('outlook_empty_conversation', 'Outlook has no message in this conversation to answer', { retryable: false });
22
+ return id;
23
+ }
24
+
25
+ async function discard(acct: Account, draftId: string): Promise<void> {
26
+ await graph(acct, messagePath(draftId), { method: 'DELETE' }).catch((err: unknown) => {
27
+ process.stderr.write(`outlook[${acct.id}] could not remove an unsent draft: ${err instanceof Error ? err.message : String(err)}\n`);
28
+ });
29
+ }
30
+
31
+ async function withFiles(acct: Account, target: string, verb: 'reply' | 'replyAll', comment: string, files: OutgoingFile[]): Promise<string[]> {
32
+ const draft = await graphJson<{ id?: string }>(acct, `${messagePath(target)}/${verb === 'reply' ? 'createReply' : 'createReplyAll'}`, jsonInit('POST', { comment }));
33
+ const draftId = draft.id;
34
+ if (draftId === undefined) throw new TrainError('outlook_graph_error', 'Outlook did not open a reply draft', { retryable: true });
35
+ const labels: string[] = [];
36
+ try {
37
+ for (const file of files) labels.push(await attachFile(acct, draftId, file));
38
+ await graph(acct, `${messagePath(draftId)}/send`, { method: 'POST' });
39
+ } catch (err) {
40
+ await discard(acct, draftId);
41
+ throw err;
42
+ }
43
+ return labels;
44
+ }
45
+
46
+ const ADDRESS_RE = /^[^@\s/]+@[^@\s/]+\.[^@\s/]+$/;
47
+ const SUBJECT_MAX = 255;
48
+
49
+ export const isAddress = (target: string): boolean => target.includes('@');
50
+
51
+ export function subjectOf(subject: string, text: string): string {
52
+ const chosen = subject !== '' ? subject : (text.split('\n').find((l) => l.trim() !== '') ?? '').trim();
53
+ if (chosen === '') throw new TrainError('outlook_subject_required', 'a new email needs a subject or some text', { retryable: false });
54
+ return chosen.length > SUBJECT_MAX ? `${chosen.slice(0, SUBJECT_MAX - 3)}...` : chosen;
55
+ }
56
+
57
+ async function compose(id: string, acct: Account, to: string, args: Args, text: string, files: OutgoingFile[]): Promise<void> {
58
+ if (!ADDRESS_RE.test(to)) throw new TrainError('outlook_bad_address', `not an email address: ${to}`, { retryable: false });
59
+ const draft = await graphJson<{ id?: string; conversationId?: string }>(acct, '/me/messages', jsonInit('POST', {
60
+ subject: subjectOf(str(args.subject), text),
61
+ body: { contentType: 'Text', content: text },
62
+ toRecipients: [{ emailAddress: { address: to } }],
63
+ }));
64
+ if (draft.id === undefined) throw new TrainError('outlook_graph_error', 'Outlook did not open a draft', { retryable: true });
65
+ const labels: string[] = [];
66
+ try {
67
+ for (const file of files) labels.push(await attachFile(acct, draft.id, file));
68
+ await graph(acct, `${messagePath(draft.id)}/send`, { method: 'POST' });
69
+ } catch (err) {
70
+ await discard(acct, draft.id);
71
+ throw err;
72
+ }
73
+ const line = draft.conversationId === undefined ? undefined : lineOf(acct.id, draft.conversationId);
74
+ respond(id, { result: { account: acct.id, messageId: draft.id, ...(line === undefined ? {} : { line }), ...(labels.length > 0 ? { attachments: labels } : {}) } });
75
+ }
76
+
77
+ interface Outgoing {
78
+ target: string;
79
+ acct: Account;
80
+ files: OutgoingFile[];
81
+ text: string;
82
+ replyTo: string;
83
+ }
84
+
85
+ async function outgoing(args: Args): Promise<Outgoing> {
86
+ const line = str(args.line);
87
+ const parsed = conversationOfLine(line);
88
+ if (parsed === null) throw new TrainError('outlook_bad_line', `not an outlook line: ${line}`, { retryable: false });
89
+ const acct = accountOf(args);
90
+ const files = filesOf(args.attachments);
91
+ const text = typeof args.text === 'string' ? args.text : '';
92
+ if (text.trim() === '' && files.length === 0)
93
+ throw new TrainError('outlook_text_required', 'give some text or a file to send', { retryable: false });
94
+ await assertSendable(files);
95
+ return { target: parsed.conversationId, acct, files, text, replyTo: str(args.replyTo) };
96
+ }
97
+
98
+ async function answer(id: string, args: Args, verb: 'reply' | 'replyAll'): Promise<void> {
99
+ const { target: conversation, acct, files, text, replyTo } = await outgoing(args);
100
+ if (isAddress(conversation)) {
101
+ if (verb === 'reply' || replyTo !== '')
102
+ throw new TrainError('outlook_reply_needs_thread', 'reply on the thread\'s own line; an address line starts a new email', { retryable: false });
103
+ await compose(id, acct, conversation.toLowerCase(), args, text, files);
104
+ return;
105
+ }
106
+ const target = replyTo === '' ? await latestInConversation(acct, conversation) : replyTo;
107
+ const comment = escapeHtml(text);
108
+ let labels: string[] = [];
109
+ if (files.length === 0) await graph(acct, `${messagePath(target)}/${verb}`, jsonInit('POST', { comment }));
110
+ else labels = await withFiles(acct, target, verb, comment, files);
111
+ respond(id, { result: { account: acct.id, repliedTo: target, ...(labels.length > 0 ? { attachments: labels } : {}) } });
112
+ }
113
+
114
+ export const reply = (id: string, args: Args): Promise<void> => answer(id, args, 'reply');
115
+
116
+ export const send = (id: string, args: Args): Promise<void> => answer(id, args, str(args.replyTo) === '' ? 'replyAll' : 'reply');
@@ -0,0 +1,119 @@
1
+ import { respond } from '@metro-labs/core/stations/station-runtime';
2
+ import { TrainError } from '@metro-labs/core/train-error';
3
+ import { accountOf, type Account } from './accounts.js';
4
+ import { listFiles, saveFile } from './attachments.js';
5
+ import { conversationOfLine, fullOf, LIST_FIELDS, MESSAGE_FIELDS, summaryOf, type GraphMessage } from './format.js';
6
+ import { graph, graphJson, jsonInit, messagePath, odataQuote, queryOf } from './graph.js';
7
+ import { EPOCH } from './outbound.js';
8
+
9
+ type Args = Record<string, unknown>;
10
+
11
+ export const DEFAULT_LIMIT = 20;
12
+ export const MAX_LIMIT = 50;
13
+
14
+ const str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');
15
+
16
+ export interface ReadQuery {
17
+ conversationId: string | null;
18
+ query: string;
19
+ from: string;
20
+ since: string;
21
+ until: string;
22
+ before: string;
23
+ unreadOnly: boolean;
24
+ limit: number;
25
+ }
26
+
27
+ function isoOf(raw: string, field: string): string {
28
+ if (raw === '') return '';
29
+ const at = Date.parse(raw);
30
+ if (Number.isNaN(at)) throw new TrainError('outlook_bad_date', `${field} must be a date, for example 2026-09-24 or 2026-09-24T10:00:00Z`, { retryable: false });
31
+ return new Date(at).toISOString();
32
+ }
33
+
34
+ export function readQueryOf(args: Args): ReadQuery {
35
+ const line = str(args.line);
36
+ const conversation = line === '' ? null : conversationOfLine(line);
37
+ if (line !== '' && conversation === null) throw new TrainError('outlook_bad_line', `not an outlook line: ${line}`, { retryable: false });
38
+ const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.min(Math.floor(args.limit), MAX_LIMIT) : DEFAULT_LIMIT;
39
+ return {
40
+ conversationId: conversation?.conversationId ?? null,
41
+ query: str(args.query).replace(/"/g, ''),
42
+ from: str(args.from).toLowerCase(),
43
+ since: isoOf(str(args.since), 'since'),
44
+ until: isoOf(str(args.until), 'until'),
45
+ before: str(args.before),
46
+ unreadOnly: args.unreadOnly === true,
47
+ limit,
48
+ };
49
+ }
50
+
51
+ export function filterOf(q: ReadQuery, beforeAt: string): string {
52
+ const parts = [`receivedDateTime ge ${q.since === '' ? EPOCH : q.since}`];
53
+ const until = [q.until, beforeAt].filter((s) => s !== '').sort()[0];
54
+ if (until !== undefined) parts.push(`receivedDateTime lt ${until}`);
55
+ if (q.conversationId !== null) parts.push(`conversationId eq ${odataQuote(q.conversationId)}`);
56
+ if (q.from !== '') parts.push(`from/emailAddress/address eq ${odataQuote(q.from)}`);
57
+ if (q.unreadOnly) parts.push('isRead eq false');
58
+ return parts.join(' and ');
59
+ }
60
+
61
+ function inWindow(at: number, q: ReadQuery, beforeAt: string): boolean {
62
+ if (q.since !== '' && at < Date.parse(q.since)) return false;
63
+ if (q.until !== '' && at >= Date.parse(q.until)) return false;
64
+ return beforeAt === '' || at < Date.parse(beforeAt);
65
+ }
66
+
67
+ function matches(q: ReadQuery, m: GraphMessage, beforeAt: string): boolean {
68
+ if (q.conversationId !== null && m.conversationId !== q.conversationId) return false;
69
+ if (q.unreadOnly && m.isRead === true) return false;
70
+ return inWindow(Date.parse(m.receivedDateTime ?? ''), q, beforeAt);
71
+ }
72
+
73
+ async function receivedAt(acct: Account, messageId: string): Promise<string> {
74
+ if (messageId === '') return '';
75
+ const m = await graphJson<GraphMessage>(acct, `${messagePath(messageId)}?$select=receivedDateTime`);
76
+ return m.receivedDateTime ?? '';
77
+ }
78
+
79
+ export function searchParams(q: ReadQuery, beforeAt: string): string {
80
+ if (q.query === '')
81
+ return queryOf({ $filter: filterOf(q, beforeAt), $orderby: 'receivedDateTime desc', $top: String(q.limit), $select: LIST_FIELDS });
82
+ const terms = q.from === '' ? q.query : `from:${q.from} ${q.query}`;
83
+ return queryOf({ $search: `"${terms}"`, $top: String(MAX_LIMIT), $select: LIST_FIELDS });
84
+ }
85
+
86
+ async function list(id: string, acct: Account, q: ReadQuery): Promise<void> {
87
+ const beforeAt = await receivedAt(acct, q.before);
88
+ const body = await graphJson<{ value?: GraphMessage[] }>(acct, `/me/messages?${searchParams(q, beforeAt)}`);
89
+ const found = (body.value ?? []).filter((m) => q.query === '' || matches(q, m, beforeAt)).slice(0, q.limit);
90
+ respond(id, {
91
+ result: {
92
+ account: acct.id,
93
+ mode: q.query === '' ? 'filter' : 'search',
94
+ messages: found.map((m) => summaryOf(acct.id, m)),
95
+ },
96
+ });
97
+ }
98
+
99
+ async function one(id: string, acct: Account, messageId: string): Promise<void> {
100
+ const m = await graphJson<GraphMessage>(acct, `${messagePath(messageId)}?$select=${MESSAGE_FIELDS}`);
101
+ const files = m.hasAttachments === true ? await listFiles(acct, m.id) : [];
102
+ const attachments = [];
103
+ for (const [index, file] of files.entries()) {
104
+ const saved = await saveFile(acct, m.id, file, index);
105
+ attachments.push({ name: file.name, mime: file.mime, kind: file.kind, size: saved.bytes, local_path: saved.path });
106
+ }
107
+ if (m.isRead !== true) await graph(acct, messagePath(m.id), jsonInit('PATCH', { isRead: true }));
108
+ respond(id, { result: { account: acct.id, message: { ...fullOf(acct.id, m), is_read: true, attachments } } });
109
+ }
110
+
111
+ export async function read(id: string, args: Args): Promise<void> {
112
+ const acct = accountOf(args);
113
+ const messageId = str(args.messageId);
114
+ if (messageId !== '') {
115
+ await one(id, acct, messageId);
116
+ return;
117
+ }
118
+ await list(id, acct, readQueryOf(args));
119
+ }
@@ -0,0 +1,43 @@
1
+ import { readJson, writeSecure } from '@metro-labs/core/secure-fs';
2
+ import { accountFiles } from '@metro-labs/core/stations/account-files';
3
+
4
+ export interface AccountState {
5
+ accessToken: string;
6
+ refreshToken: string;
7
+ expiresAt: number;
8
+ deltaLink: string | null;
9
+ syncedAt: string | null;
10
+ seen: string[];
11
+ }
12
+
13
+ const SEEN_MAX = 500;
14
+
15
+ export const stateFiles = accountFiles('OUTLOOK_STATE_DIR', 'outlook-state-');
16
+
17
+ const statePath =(accountId: string): string => stateFiles.path(accountId);
18
+
19
+ const text = (v: unknown): string => (typeof v === 'string' ? v : '');
20
+ const nullable = (v: unknown): string | null => (typeof v === 'string' && v !== '' ? v : null);
21
+
22
+ export function loadState(accountId: string, seed: Partial<AccountState>): AccountState {
23
+ const saved = readJson<Record<string, unknown>>(statePath(accountId), {});
24
+ const refreshToken = text(saved.refreshToken) || text(seed.refreshToken);
25
+ const fromFile = text(saved.refreshToken) !== '';
26
+ return {
27
+ refreshToken,
28
+ accessToken: fromFile ? text(saved.accessToken) : text(seed.accessToken),
29
+ expiresAt: fromFile ? Number(saved.expiresAt) || 0 : Number(seed.expiresAt) || 0,
30
+ deltaLink: nullable(saved.deltaLink),
31
+ syncedAt: nullable(saved.syncedAt),
32
+ seen: Array.isArray(saved.seen) ? saved.seen.filter((s): s is string => typeof s === 'string') : [],
33
+ };
34
+ }
35
+
36
+ export function saveState(accountId: string, state: AccountState): void {
37
+ writeSecure(statePath(accountId), JSON.stringify(state));
38
+ }
39
+
40
+ export function noteSeen(state: AccountState, messageId: string): void {
41
+ state.seen.push(messageId);
42
+ if (state.seen.length > SEEN_MAX) state.seen.splice(0, state.seen.length - SEEN_MAX);
43
+ }
@@ -0,0 +1,15 @@
1
+ import type { ReadFilter, Station, Verb } from '@metro-labs/core/stations/types';
2
+ import { stateFiles } from './state.js';
3
+
4
+ export const outlookStation: Station = {
5
+ name: 'outlook',
6
+ hasAccounts: true,
7
+ hasTrain: true,
8
+ messageVerbs: new Set<Verb>(['send', 'reply', 'read']),
9
+ readFilters: new Set<ReadFilter>(['account', 'query', 'from', 'until', 'unread_only', 'message_id']),
10
+ approvals: false,
11
+ attachmentMode: 'canonical',
12
+ forget: stateFiles.forget,
13
+ forgetExcept: stateFiles.forgetExcept,
14
+ tools: [],
15
+ };
@@ -0,0 +1,70 @@
1
+ import type { Account } from './accounts.js';
2
+ import { addressOf, type GraphMessage } from './format.js';
3
+ import { graphJson, messagePath } from './graph.js';
4
+
5
+ export interface Header {
6
+ name?: string;
7
+ value?: string;
8
+ }
9
+
10
+ export type Screened = { skip: string } | { verified: boolean };
11
+
12
+ const AUTOMATED_LOCAL = /^(no-?reply|do-?not-?reply|donotreply|mailer-daemon|notifications?)([+._-].*)?$/i;
13
+ const BULK = new Set(['bulk', 'list', 'junk']);
14
+
15
+ const valuesOf = (headers: Header[], name: string): string[] =>
16
+ headers.filter((h) => (h.name ?? '').toLowerCase() === name).map((h) => (h.value ?? '').trim());
17
+
18
+ const first = (headers: Header[], name: string): string | undefined => valuesOf(headers, name)[0];
19
+
20
+ export function automatedBySender(address: string): string | null {
21
+ const local = address.split('@')[0] ?? '';
22
+ return AUTOMATED_LOCAL.test(local) ? `sender ${address}` : null;
23
+ }
24
+
25
+ export function automatedByHeaders(headers: Header[]): string | null {
26
+ if (first(headers, 'list-unsubscribe') !== undefined) return 'List-Unsubscribe';
27
+ const auto = first(headers, 'auto-submitted');
28
+ if (auto !== undefined && auto.toLowerCase() !== 'no') return `Auto-Submitted: ${auto}`;
29
+ const precedence = (first(headers, 'precedence') ?? '').toLowerCase();
30
+ return BULK.has(precedence) ? `Precedence: ${precedence}` : null;
31
+ }
32
+
33
+ const domainOf = (address: string): string => (address.split('@')[1] ?? '').toLowerCase();
34
+
35
+ function headerFrom(result: string): string | null {
36
+ return /header\.from=([^\s;]+)/i.exec(result)?.[1]?.toLowerCase() ?? null;
37
+ }
38
+
39
+ function passes(result: string, fromDomain: string): boolean {
40
+ const aligned = headerFrom(result);
41
+ if (aligned !== null && aligned !== fromDomain) return false;
42
+ if (/\bdmarc=pass\b/i.test(result)) return true;
43
+ return /\bcompauth=pass\b/i.test(result) && aligned === fromDomain;
44
+ }
45
+
46
+ export function senderVerified(headers: Header[], fromAddress: string): boolean {
47
+ if ((first(headers, 'x-ms-exchange-organization-authas') ?? '').toLowerCase() === 'internal') return true;
48
+ const fromDomain = domainOf(fromAddress);
49
+ if (fromDomain === '') return false;
50
+ const ours = first(headers, 'authentication-results');
51
+ if (ours !== undefined && passes(ours, fromDomain)) return true;
52
+ const arc = valuesOf(headers, 'arc-authentication-results').find((v) => /\bmx\.microsoft\.com\b/i.test(v));
53
+ return arc !== undefined && passes(arc, fromDomain);
54
+ }
55
+
56
+ async function headersOf(acct: Account, messageId: string): Promise<Header[]> {
57
+ const m = await graphJson<{ internetMessageHeaders?: Header[] }>(acct, `${messagePath(messageId)}?$select=internetMessageHeaders`);
58
+ return m.internetMessageHeaders ?? [];
59
+ }
60
+
61
+ export async function screen(acct: Account, m: GraphMessage): Promise<Screened> {
62
+ const from = addressOf(m.from);
63
+ const keepAutomated = acct.cfg.includeAutomated === true;
64
+ const bySender = keepAutomated ? null : automatedBySender(from);
65
+ if (bySender !== null) return { skip: bySender };
66
+ const headers = await headersOf(acct, m.id);
67
+ const byHeaders = keepAutomated ? null : automatedByHeaders(headers);
68
+ if (byHeaders !== null) return { skip: byHeaders };
69
+ return { verified: senderVerified(headers, from) };
70
+ }
@@ -6,14 +6,15 @@
6
6
  "exports": {
7
7
  ".": "./src/station.ts",
8
8
  "./train": "./src/index.ts",
9
- "./verify": "./src/verify.ts"
9
+ "./login": "./src/login.ts"
10
10
  },
11
11
  "scripts": {
12
12
  "build": "tsc",
13
13
  "test": "tsc --noEmit && bun test test/"
14
14
  },
15
15
  "dependencies": {
16
- "@metro-labs/mcp": "workspace:*"
16
+ "@metro-labs/core": "workspace:*",
17
+ "@mtcute/bun": "0.30.1"
17
18
  },
18
19
  "devDependencies": {
19
20
  "@stage-labs/config": "0.1.0-beta.2",