@stage-labs/metro 0.1.0-beta.20 → 0.1.0-beta.201

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 (442) hide show
  1. package/dist/api.js +9 -101
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +163 -0
  4. package/dist/cli.js +50 -109
  5. package/dist/control.js +47 -75
  6. package/dist/hold.js +211 -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 +97 -0
  14. package/dist/runtime.js +32 -52
  15. package/dist/serve.js +158 -0
  16. package/dist/service.js +243 -0
  17. package/dist/tail.js +119 -0
  18. package/dist/update.js +13 -1
  19. package/package.json +1 -22
  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 +24 -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 +8 -6
  32. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  33. package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
  34. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  35. package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +12 -3
  36. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
  37. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
  38. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  39. package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
  40. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/account-store.ts +4 -24
  41. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  43. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +3 -6
  44. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  45. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +89 -0
  46. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +16 -0
  47. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  48. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  49. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  50. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +119 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +99 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +100 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +252 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +61 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +91 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +168 -0
  72. package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
  73. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-map.ts → daemon/src/agents/map.ts} +58 -1
  74. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  75. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
  76. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +86 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/approvals/needs.ts +2 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +149 -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 +221 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -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/daemon/src/channels/dedupe.ts +12 -0
  89. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +47 -44
  90. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +9 -4
  91. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
  92. package/runtime/node_modules/@metro-labs/daemon/src/channels/react-note.ts +13 -0
  93. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
  94. package/runtime/node_modules/@metro-labs/daemon/src/channels/reply-id.ts +1 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +355 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +82 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +283 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +271 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  107. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +77 -109
  108. package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +55 -26
  109. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  111. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
  112. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +28 -13
  113. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -29
  114. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
  115. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +29 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +348 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +358 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  124. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
  125. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
  126. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  127. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +11 -12
  128. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
  129. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +2 -2
  130. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +194 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +137 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +223 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +299 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +15 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
  158. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +59 -18
  159. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  160. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +43 -20
  161. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
  162. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
  163. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
  164. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +24 -79
  165. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
  166. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
  167. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +83 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  173. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  174. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
  176. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +33 -42
  177. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
  178. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  179. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
  180. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +55 -119
  181. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
  182. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/api.ts +43 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/move.ts +165 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  187. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  188. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  190. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  191. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +59 -51
  192. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +208 -0
  193. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +71 -0
  194. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → daemon/src/routes/session.ts} +9 -7
  195. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  196. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  197. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  198. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  199. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  200. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  201. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  202. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
  203. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +9 -36
  204. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
  205. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
  206. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +30 -20
  207. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +2 -2
  208. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +75 -9
  209. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
  210. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +22 -6
  211. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  212. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +2 -2
  213. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +5 -8
  214. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  215. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
  216. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  217. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  218. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  219. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  220. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  221. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  222. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +48 -0
  223. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  224. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  225. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  226. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +148 -0
  227. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +130 -0
  228. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +61 -0
  229. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/package.json +3 -5
  230. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +11 -13
  231. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +6 -154
  232. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +3 -3
  233. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +28 -64
  234. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +1 -1
  235. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +2 -2
  236. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +14 -19
  237. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/members.ts +2 -2
  238. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  239. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/send-files.ts +1 -1
  240. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +22 -0
  241. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +1 -0
  242. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  243. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +138 -0
  244. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  245. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  246. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  247. package/runtime/node_modules/@metro-labs/{telegram-user → outlook}/package.json +2 -5
  248. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +107 -0
  249. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +20 -0
  250. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
  251. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
  252. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
  253. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
  254. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +141 -0
  255. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
  256. package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
  257. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
  258. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  259. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
  260. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
  261. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
  262. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
  263. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
  264. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  265. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -0
  266. package/runtime/node_modules/@metro-labs/telegram/package.json +3 -2
  267. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +48 -88
  268. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +214 -253
  269. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +67 -108
  270. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +6 -6
  271. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +102 -146
  272. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +4 -4
  273. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
  274. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
  275. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +6 -102
  276. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
  277. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +65 -151
  278. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +61 -33
  279. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/normalize.ts +1 -1
  280. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  281. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +2 -2
  282. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +11 -1
  283. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +5 -47
  284. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -3
  285. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  286. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +119 -0
  287. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +246 -0
  288. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  289. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +173 -0
  290. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  291. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +95 -0
  292. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  293. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +65 -0
  294. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +20 -0
  295. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
  296. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +1 -0
  297. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  298. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +106 -0
  299. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +134 -0
  300. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  301. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  302. package/runtime/node_modules/@metro-labs/threema/src/files.ts +58 -0
  303. package/runtime/node_modules/@metro-labs/threema/src/format.ts +169 -0
  304. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  305. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  306. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
  307. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  308. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  309. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  310. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  311. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  312. package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
  313. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
  314. package/runtime/node_modules/@metro-labs/whatsapp/package.json +1 -3
  315. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +1 -2
  316. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
  317. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +40 -5
  318. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
  319. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
  320. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +74 -4
  321. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -56
  322. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +10 -27
  323. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
  324. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
  325. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
  326. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
  327. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +28 -0
  328. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +1 -1
  329. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
  330. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  331. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +8 -1
  332. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
  333. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
  334. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +1 -5
  335. package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
  336. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +9 -8
  337. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +1 -1
  338. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +3 -201
  339. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +3 -42
  340. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +12 -143
  341. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
  342. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +3 -3
  343. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
  344. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +7 -16
  345. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
  346. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
  347. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
  348. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +11 -14
  349. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +1 -19
  350. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
  351. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  352. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  353. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  354. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  355. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +5 -1
  356. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +3 -50
  357. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +30 -44
  358. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
  359. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
  360. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +15 -3
  361. package/runtime/runtime.json +1 -1
  362. package/runtime/server.ts +1 -0
  363. package/runtime/stations.json +37 -0
  364. package/dist/prompt.js +0 -10
  365. package/dist/store.js +0 -70
  366. package/runtime/node_modules/@metro-labs/discord/src/station.ts +0 -19
  367. package/runtime/node_modules/@metro-labs/discord/src/voice-speak.ts +0 -195
  368. package/runtime/node_modules/@metro-labs/discord/src/voice-transcribe.ts +0 -292
  369. package/runtime/node_modules/@metro-labs/discord/src/voice.ts +0 -197
  370. package/runtime/node_modules/@metro-labs/discord/src/wire.ts +0 -5
  371. package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
  372. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
  373. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
  374. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -335
  375. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
  376. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -322
  377. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
  378. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
  379. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
  380. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
  381. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
  382. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
  383. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
  384. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
  385. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
  386. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
  387. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
  388. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
  389. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
  390. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
  391. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
  392. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
  393. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
  394. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
  395. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
  396. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
  397. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
  398. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
  399. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
  400. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
  401. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +0 -340
  402. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
  403. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -28
  404. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
  405. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
  406. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
  407. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
  408. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
  409. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
  410. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
  411. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
  412. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
  413. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
  414. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
  415. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
  416. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
  417. package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
  418. package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
  419. package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
  420. package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
  421. package/runtime/node_modules/@metro-labs/telegram-user/src/inbound.ts +0 -78
  422. package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
  423. package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
  424. package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
  425. package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
  426. package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
  427. package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
  428. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
  429. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
  430. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
  431. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
  432. /package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -0
  433. /package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/messaging-normalize.ts +0 -0
  434. /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
  435. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
  436. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
  437. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
  438. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/api-base.ts +0 -0
  439. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.ts +0 -0
  440. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
  441. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
  442. /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
@@ -0,0 +1,316 @@
1
+ import { execFile, spawn, type ChildProcess } from 'node:child_process';
2
+ import { Resolver } from 'node:dns/promises';
3
+ import { request as httpsRequest } from 'node:https';
4
+ import { errMsg, log } from '@metro-labs/core/log';
5
+ import { localOwner } from '../agents/file-admin.js';
6
+
7
+ const RESTART_DELAY_MS = 2_000;
8
+ const RESTART_DELAY_MAX_MS = 30_000;
9
+ const TAKEN_RE = /listener already exists/i;
10
+ const ADOPTED_MISSES = 2;
11
+ const adoptedCheckMs = (): number => Number(process.env.METRO_TUNNEL_WATCH_MS) || 30_000;
12
+ const FUNNEL_ON_RE = /\(Funnel on\)/i;
13
+
14
+ export const tunnelWanted = (): boolean => process.env.METRO_TUNNEL?.trim() === 'tailscale';
15
+
16
+ const FUNNEL_URL_RE = /https:\/\/[a-z0-9-]+(?:\.[a-z0-9-]+)+\.ts\.net(?::\d{2,5})?(?=[\s/]|$)/i;
17
+ const SILENT_MS = 20_000;
18
+
19
+ export const funnelUrlIn = (text: string): string | null =>
20
+ FUNNEL_URL_RE.exec(text)?.[0]?.toLowerCase() ?? null;
21
+
22
+ export interface Adopted {
23
+ url: string | null;
24
+ hint: string;
25
+ alive?: () => Promise<boolean>;
26
+ }
27
+
28
+ export interface TunnelDriver {
29
+ name: string;
30
+ command: string;
31
+ args: string[];
32
+ urlIn: (text: string) => string | null;
33
+ adopt?: () => Promise<Adopted>;
34
+ }
35
+
36
+ const runText = (command: string, args: string[]): Promise<string> =>
37
+ new Promise((resolve) => {
38
+ execFile(command, args, { encoding: 'utf8', timeout: 10_000 }, (_err, stdout, stderr) => {
39
+ resolve(`${stdout}\n${stderr}`);
40
+ });
41
+ });
42
+
43
+ export type Probe = (url: string, owner: string | null) => Promise<boolean>;
44
+
45
+ const PROBE_MS = 5_000;
46
+ const PUBLIC_RESOLVERS = ['1.1.1.1', '8.8.8.8'];
47
+
48
+ async function publicAddresses(host: string): Promise<string[]> {
49
+ const found = await Promise.all(
50
+ PUBLIC_RESOLVERS.map(async (server) => {
51
+ const resolver = new Resolver();
52
+ resolver.setServers([server]);
53
+ const [v4, v6] = await Promise.all([resolver.resolve4(host).catch(() => []), resolver.resolve6(host).catch(() => [])]);
54
+ return [...v4, ...v6];
55
+ }),
56
+ );
57
+ return [...new Set(found.flat())];
58
+ }
59
+
60
+ function modeThrough(ip: string, host: string): Promise<{ mode?: unknown; owner?: unknown } | null> {
61
+ return new Promise((resolve) => {
62
+ const req = httpsRequest(
63
+ { host: ip, servername: host, port: 443, path: '/api/mode', method: 'GET', headers: { host }, timeout: PROBE_MS },
64
+ (res) => {
65
+ const chunks: Buffer[] = [];
66
+ res.on('data', (c: Buffer) => chunks.push(c));
67
+ res.on('end', () => {
68
+ try {
69
+ resolve(res.statusCode === 200 ? (JSON.parse(Buffer.concat(chunks).toString('utf8')) as { mode?: unknown }) : null);
70
+ } catch {
71
+ resolve(null);
72
+ }
73
+ });
74
+ },
75
+ );
76
+ req.on('timeout', () => {
77
+ req.destroy();
78
+ });
79
+ req.on('error', () => {
80
+ resolve(null);
81
+ });
82
+ req.end();
83
+ });
84
+ }
85
+
86
+ export async function daemonAnswersAt(url: string, owner: string | null): Promise<boolean> {
87
+ const host = new URL(url).host;
88
+ const addresses = await publicAddresses(host);
89
+ if (addresses.length === 0) return false;
90
+ const body = await modeThrough(addresses[0] ?? '', host);
91
+ return body !== null && body.mode === 'local' && (owner === null || body.owner === owner);
92
+ }
93
+
94
+ export function nodeNameIn(statusJson: string): string | null {
95
+ try {
96
+ const parsed: unknown = JSON.parse(statusJson);
97
+ const self = typeof parsed === 'object' && parsed !== null ? (parsed as { Self?: { DNSName?: unknown } }).Self : undefined;
98
+ const name = typeof self?.DNSName === 'string' ? self.DNSName.replace(/\.$/, '').toLowerCase() : '';
99
+ return name === '' ? null : name;
100
+ } catch {
101
+ return null;
102
+ }
103
+ }
104
+
105
+ async function adoptFunnel(bin: string, port: number, probe: Probe, owner: () => string | null): Promise<Adopted> {
106
+ const name = nodeNameIn(await runText(bin, ['status', '--json']));
107
+ const watched = (url: string): Adopted => ({
108
+ url,
109
+ hint: `a Funnel already publishes this daemon at ${url}; using it`,
110
+ alive: () => probe(url, owner()),
111
+ });
112
+ if (name !== null) {
113
+ const url = `https://${name}`;
114
+ if (await probe(url, owner())) return watched(url);
115
+ }
116
+ const found = funnelAlreadyServing(await runText(bin, ['funnel', 'status']), port);
117
+ return found.url === null ? found : watched(found.url);
118
+ }
119
+
120
+ export function funnelAlreadyServing(status: string, port: number): Adopted {
121
+ const lines = status.split('\n');
122
+ const target = `http://127.0.0.1:${String(port)}`;
123
+ for (const [i, line] of lines.entries()) {
124
+ if (line.trimStart().startsWith('#')) continue;
125
+ const url = funnelUrlIn(line);
126
+ if (url === null) continue;
127
+ const body = lines.slice(i + 1, i + 6).join('\n');
128
+ if (!body.includes(target)) continue;
129
+ if (FUNNEL_ON_RE.test(line)) return { url, hint: `a Funnel already publishes this daemon at ${url}; using it` };
130
+ return {
131
+ url: null,
132
+ hint: `${url} is a tailnet-only serve config on this node, not a Funnel; run: tailscale serve reset`,
133
+ };
134
+ }
135
+ return { url: null, hint: 'port 443 is held by another serve config on this node; run: tailscale serve reset' };
136
+ }
137
+
138
+ function tailscaleBin(): string {
139
+ const configured = process.env.METRO_TAILSCALE_BIN?.trim() ?? '';
140
+ return configured === '' ? 'tailscale' : configured;
141
+ }
142
+
143
+ export const funnelDriver = (
144
+ port: number,
145
+ bin = tailscaleBin(),
146
+ probe: Probe = daemonAnswersAt,
147
+ owner: () => string | null = localOwner,
148
+ ): TunnelDriver => ({
149
+ name: 'tailscale funnel',
150
+ command: bin,
151
+ args: ['funnel', String(port)],
152
+ urlIn: funnelUrlIn,
153
+ adopt: () => adoptFunnel(bin, port, probe, owner),
154
+ });
155
+
156
+ let liveUrl: string | null = null;
157
+
158
+ export const currentTunnelUrl = (): string | null => liveUrl;
159
+
160
+ export type Resolves = (host: string) => Promise<boolean>;
161
+
162
+ const RESOLVE_EVERY_MS = 3_000;
163
+ const RESOLVE_GIVE_UP_MS = 180_000;
164
+
165
+ export async function resolvesPublicly(host: string): Promise<boolean> {
166
+ return (await publicAddresses(host)).length > 0;
167
+ }
168
+
169
+ async function untilResolvable(host: string, resolves: Resolves): Promise<boolean> {
170
+ const deadline = Date.now() + RESOLVE_GIVE_UP_MS;
171
+ while (Date.now() < deadline) {
172
+ if (await resolves(host)) return true;
173
+ await new Promise((r) => setTimeout(r, RESOLVE_EVERY_MS));
174
+ }
175
+ return false;
176
+ }
177
+
178
+
179
+ export class Tunnel {
180
+ private child: ChildProcess | null = null;
181
+ private closed = false;
182
+ private output: string[] = [];
183
+ private restartDelay = RESTART_DELAY_MS;
184
+ private watch: ReturnType<typeof setInterval> | null = null;
185
+
186
+ constructor(
187
+ private driver: TunnelDriver,
188
+ private onUrl: (url: string) => void = () => undefined,
189
+ private resolves: Resolves = resolvesPublicly,
190
+ ) {}
191
+
192
+ private noticeUrl(text: string): void {
193
+ const url = this.driver.urlIn(text);
194
+ if (url === null || url === liveUrl) return;
195
+ liveUrl = url;
196
+ this.restartDelay = RESTART_DELAY_MS;
197
+ log.info({ url }, `${this.driver.name} up; waiting for its name to resolve`);
198
+ this.announceWhenResolvable(url).catch((err: unknown) => {
199
+ log.warn({ err: errMsg(err) }, `${this.driver.name}: announce failed`);
200
+ });
201
+ }
202
+
203
+ private async announceWhenResolvable(url: string): Promise<void> {
204
+ const resolved = await untilResolvable(new URL(url).host, this.resolves);
205
+ if (liveUrl !== url) return;
206
+ if (!resolved) log.warn({ url }, `${this.driver.name} name still not resolving; announcing anyway`);
207
+ this.onUrl(url);
208
+ }
209
+
210
+ private read(d: Buffer | string): void {
211
+ const text = (typeof d === 'string' ? d : d.toString('utf8')).trim();
212
+ if (text === '') return;
213
+ this.output.push(text);
214
+ log.debug({ output: text }, this.driver.name);
215
+ this.noticeUrl(text);
216
+ }
217
+
218
+ private silentCheck(): void {
219
+ if (this.closed || liveUrl !== null || this.child === null) return;
220
+ log.warn(
221
+ { output: this.output.join('\n') },
222
+ `${this.driver.name}: no public address after ${String(SILENT_MS / 1000)}s; its own output follows`,
223
+ );
224
+ }
225
+
226
+ start(): void {
227
+ if (this.closed) return;
228
+ log.info({ command: this.driver.command, args: this.driver.args }, `${this.driver.name} starting`);
229
+ this.output = [];
230
+ const child = spawn(this.driver.command, this.driver.args, { stdio: ['ignore', 'pipe', 'pipe'] });
231
+ this.child = child;
232
+ child.stdout?.on('data', (d: Buffer | string) => {
233
+ this.read(d);
234
+ });
235
+ child.stderr?.on('data', (d: Buffer | string) => {
236
+ this.read(d);
237
+ });
238
+ setTimeout(() => {
239
+ this.silentCheck();
240
+ }, SILENT_MS).unref();
241
+ child.on('exit', (code) => {
242
+ this.child = null;
243
+ liveUrl = null;
244
+ if (this.closed) return;
245
+ this.afterExit(code).catch((err: unknown) => {
246
+ log.warn({ err: errMsg(err) }, `${this.driver.name}: exit handling failed`);
247
+ });
248
+ });
249
+ child.on('error', (err) => {
250
+ if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
251
+ this.closed = true;
252
+ log.error(
253
+ { command: this.driver.command },
254
+ `${this.driver.name}: the binary is not installed, so there is no public address; install it and restart`,
255
+ );
256
+ return;
257
+ }
258
+ log.warn({ err: errMsg(err) }, `${this.driver.name} spawn error`);
259
+ });
260
+ }
261
+
262
+ private async afterExit(code: number | null): Promise<void> {
263
+ const output = this.output.join('\n');
264
+ if (this.driver.adopt !== undefined && TAKEN_RE.test(output)) {
265
+ const found = await this.driver.adopt();
266
+ if (this.closed) return;
267
+ if (found.url !== null) {
268
+ log.info({ url: found.url }, `${this.driver.name}: ${found.hint}`);
269
+ this.noticeUrl(found.url);
270
+ if (found.alive !== undefined) this.watchAdopted(found.url, found.alive);
271
+ return;
272
+ }
273
+ log.error({ output }, `${this.driver.name}: ${found.hint}`);
274
+ } else log.warn({ code, output: this.output.slice(-5).join('\n') }, `${this.driver.name} exited; restarting`);
275
+ setTimeout(() => {
276
+ this.start();
277
+ }, this.restartDelay);
278
+ this.restartDelay = Math.min(this.restartDelay * 2, RESTART_DELAY_MAX_MS);
279
+ }
280
+
281
+ private watchAdopted(url: string, alive: () => Promise<boolean>): void {
282
+ let misses = 0;
283
+ const tick = async (): Promise<void> => {
284
+ if (this.closed || liveUrl !== url) {
285
+ this.unwatch();
286
+ return;
287
+ }
288
+ misses = (await alive()) ? 0 : misses + 1;
289
+ if (misses < ADOPTED_MISSES) return;
290
+ this.unwatch();
291
+ log.warn({ url }, `${this.driver.name}: the Funnel this daemon adopted no longer answers; publishing our own`);
292
+ liveUrl = null;
293
+ this.restartDelay = RESTART_DELAY_MS;
294
+ this.start();
295
+ };
296
+ this.unwatch();
297
+ this.watch = setInterval(() => {
298
+ tick().catch((err: unknown) => {
299
+ log.warn({ err: errMsg(err) }, `${this.driver.name}: adopted-funnel check failed`);
300
+ });
301
+ }, adoptedCheckMs());
302
+ this.watch.unref();
303
+ }
304
+
305
+ private unwatch(): void {
306
+ if (this.watch !== null) clearInterval(this.watch);
307
+ this.watch = null;
308
+ }
309
+
310
+ stop(): void {
311
+ this.closed = true;
312
+ this.unwatch();
313
+ this.child?.kill('SIGINT');
314
+ this.child = null;
315
+ }
316
+ }
@@ -0,0 +1,118 @@
1
+ import { ApiError } from '@metro-labs/http/api-error';
2
+ import { isRecord } from '@metro-labs/core/is-record';
3
+ import { log } from '@metro-labs/core/log';
4
+ import type { ToolGroup } from '@metro-labs/core/stations/types';
5
+
6
+ export type Access = 'allow' | 'ask' | 'deny';
7
+
8
+ export const ACCESS: readonly Access[] = ['allow', 'ask', 'deny'];
9
+
10
+ export interface ToolPolicy {
11
+ read?: Access;
12
+ write?: Access;
13
+ tools?: Record<string, Access>;
14
+ }
15
+
16
+ export type PolicyTarget =
17
+ | { kind: 'channel'; station: string; account: string }
18
+ | { kind: 'connector'; id: string };
19
+
20
+ export interface PolicyTool {
21
+ name: string;
22
+ group: ToolGroup;
23
+ }
24
+
25
+ const TOOL_NAME_RE = /^[A-Za-z0-9_.-]{1,128}$/;
26
+ const MAX_TOOLS = 500;
27
+ const RANK: Record<Access, number> = { allow: 0, ask: 1, deny: 2 };
28
+
29
+ const isAccess = (value: unknown): value is Access => typeof value === 'string' && (ACCESS as readonly string[]).includes(value);
30
+
31
+ export const targetKey = (target: PolicyTarget): string =>
32
+ target.kind === 'channel' ? `channel:${target.station}/${target.account}` : `connector:${target.id}`;
33
+
34
+ export function decide(policy: ToolPolicy | undefined, tool: PolicyTool): Access {
35
+ return policy?.tools?.[tool.name] ?? policy?.[tool.group] ?? 'allow';
36
+ }
37
+
38
+ export function strictest(list: readonly Access[]): Access {
39
+ return list.reduce<Access>((worst, a) => (RANK[a] > RANK[worst] ? a : worst), 'allow');
40
+ }
41
+
42
+ function tolerantTools(raw: unknown, where: string): Record<string, Access> | undefined {
43
+ const kept: Record<string, Access> = {};
44
+ for (const [name, value] of Object.entries(isRecord(raw) ? raw : {})) {
45
+ if (TOOL_NAME_RE.test(name) && isAccess(value)) kept[name] = value;
46
+ else log.warn({ where, tool: name }, 'policy: a tool entry is not valid, ignored');
47
+ }
48
+ return Object.keys(kept).length > 0 ? kept : undefined;
49
+ }
50
+
51
+ export function parsePolicy(raw: unknown, where: string): ToolPolicy | undefined {
52
+ if (raw === undefined || raw === null) return undefined;
53
+ if (!isRecord(raw)) {
54
+ log.warn({ where }, 'policy: not an object, ignored');
55
+ return undefined;
56
+ }
57
+ const out: ToolPolicy = {};
58
+ for (const group of ['read', 'write'] as const) {
59
+ const value = raw[group];
60
+ if (isAccess(value)) out[group] = value;
61
+ else if (value !== undefined) log.warn({ where, group, value }, 'policy: a group value is not allow, ask or deny, ignored');
62
+ }
63
+ const tools = tolerantTools(raw.tools, where);
64
+ if (tools !== undefined) out.tools = tools;
65
+ return out;
66
+ }
67
+
68
+ function strictAccess(value: unknown, what: string): Access | undefined {
69
+ if (value === undefined || value === null) return undefined;
70
+ if (!isAccess(value)) throw new ApiError(`${what} must be allow, ask or deny`, 400);
71
+ return value;
72
+ }
73
+
74
+ function strictTools(raw: unknown): Record<string, Access> | undefined {
75
+ if (raw === undefined || raw === null) return undefined;
76
+ if (!isRecord(raw)) throw new ApiError('policy.tools must be an object', 400);
77
+ const entries = Object.entries(raw);
78
+ if (entries.length > MAX_TOOLS) throw new ApiError(`policy.tools holds at most ${String(MAX_TOOLS)} tools`, 400);
79
+ const tools: Record<string, Access> = {};
80
+ for (const [name, value] of entries) {
81
+ if (!TOOL_NAME_RE.test(name)) throw new ApiError(`'${name}' is not a tool name`, 400);
82
+ const access = strictAccess(value, name);
83
+ if (access !== undefined) tools[name] = access;
84
+ }
85
+ return Object.keys(tools).length > 0 ? tools : undefined;
86
+ }
87
+
88
+ export function normalizePolicy(raw: unknown): ToolPolicy {
89
+ if (!isRecord(raw)) throw new ApiError('policy must be an object', 400);
90
+ const read = strictAccess(raw.read, 'read');
91
+ const write = strictAccess(raw.write, 'write');
92
+ const tools = strictTools(raw.tools);
93
+ return {
94
+ ...(read === undefined ? {} : { read }),
95
+ ...(write === undefined ? {} : { write }),
96
+ ...(tools === undefined ? {} : { tools }),
97
+ };
98
+ }
99
+
100
+ const stored = new Map<string, { target: PolicyTarget; policy: ToolPolicy }>();
101
+ const listeners = new Set<() => void>();
102
+
103
+ export function onPoliciesChanged(listener: () => void): () => void {
104
+ listeners.add(listener);
105
+ return () => listeners.delete(listener);
106
+ }
107
+
108
+ export function setPolicies(kind: PolicyTarget['kind'], entries: readonly (readonly [PolicyTarget, ToolPolicy])[]): void {
109
+ for (const key of [...stored.keys()]) if (key.startsWith(`${kind}:`)) stored.delete(key);
110
+ for (const [target, policy] of entries) if (target.kind === kind) stored.set(targetKey(target), { target, policy });
111
+ for (const listener of listeners) listener();
112
+ }
113
+
114
+ export const storedPolicies = (): { target: PolicyTarget; policy: ToolPolicy }[] => [...stored.values()];
115
+
116
+ export const storedPolicy = (target: PolicyTarget): ToolPolicy | undefined => stored.get(targetKey(target))?.policy;
117
+
118
+ export const policyFor = (target: PolicyTarget, tool: PolicyTool): Access => decide(storedPolicy(target), tool);
@@ -0,0 +1,35 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { ApiError } from '@metro-labs/http/api-error';
4
+ import { setBearerSessions, type ApiSession } from '@metro-labs/http/api-http';
5
+ import { bearerSession, clientId, jwksUrl, SigningKeys, workosBase, type KeyStore } from '@metro-labs/http/workos-token';
6
+ import { ensureSecureDir, writeSecure } from '@metro-labs/core/secure-fs';
7
+
8
+ const KEYS_FILE = '.jwks';
9
+
10
+ export function jwksStore(dir: string): KeyStore {
11
+ const path = join(dir, KEYS_FILE);
12
+ return {
13
+ read: () => (existsSync(path) ? readFileSync(path, 'utf8') : null),
14
+ write: (text) => {
15
+ ensureSecureDir(dir);
16
+ writeSecure(path, text);
17
+ },
18
+ };
19
+ }
20
+
21
+ export function bearerSessionsFor(owner: () => string | null, keys: SigningKeys): (req: Parameters<typeof bearerSession>[0]) => Promise<ApiSession | null> {
22
+ return async (req) => {
23
+ const session = await bearerSession(req, keys);
24
+ if (session === null) return null;
25
+ const held = owner();
26
+ if (held === null || session.organization === null || session.organization !== held)
27
+ throw new ApiError('this machine belongs to another organization', 403);
28
+ return { subject: held, role: session.role === 'admin' ? 'admin' : 'member' };
29
+ };
30
+ }
31
+
32
+ export function installBearerSessions(dir: string, owner: () => string | null, env: NodeJS.ProcessEnv = process.env): void {
33
+ const keys = new SigningKeys(jwksUrl(clientId(env), workosBase(env)), jwksStore(dir));
34
+ setBearerSessions(bearerSessionsFor(owner, keys));
35
+ }
@@ -0,0 +1,24 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+
3
+ export const WEBHOOK_BODY_MAX = 25 * 1024 * 1024;
4
+
5
+ export class BodyTooLargeError extends Error {
6
+ constructor(readonly limit: number) {
7
+ super(`request body exceeds ${limit} bytes`);
8
+ }
9
+ }
10
+
11
+ export async function readBody(
12
+ req: IncomingMessage,
13
+ maxBytes: number,
14
+ ): Promise<Buffer> {
15
+ const chunks: Buffer[] = [];
16
+ let total = 0;
17
+ for await (const c of req) {
18
+ const buf = c as Buffer;
19
+ total += buf.length;
20
+ if (total > maxBytes) throw new BodyTooLargeError(maxBytes);
21
+ chunks.push(buf);
22
+ }
23
+ return Buffer.concat(chunks);
24
+ }
@@ -1,12 +1,18 @@
1
1
  import { handleSessionApis, type SessionApis } from './session-apis.js';
2
+ import { handleGatewayRequest } from '../gateway/gateway.js';
3
+ import { handleRelayRequest } from '../connectors/relay.js';
4
+ import { handleThreemaCallback } from './threema-callback.js';
5
+ import { BodyTooLargeError, readBody, WEBHOOK_BODY_MAX } from './body.js';
6
+
7
+ export { BodyTooLargeError, readBody } from './body.js';
2
8
  import {
3
9
  createServer,
4
10
  type IncomingMessage,
5
11
  type Server,
6
12
  type ServerResponse,
7
13
  } from 'node:http';
8
- import { Line } from '../stations/lines.js';
9
- import { errMsg, log } from './log.js';
14
+ import { Line } from '@metro-labs/core/lines';
15
+ import { errMsg, log } from '@metro-labs/core/log';
10
16
  import {
11
17
  classifyEvent,
12
18
  formatDisplay,
@@ -14,30 +20,25 @@ import {
14
20
  publishEvent,
15
21
  userSelf,
16
22
  type MetroEvent,
17
- } from './events.js';
18
- import type { TrainEvent } from './protocol.js';
19
- import { agentForLine, agentIdForLine } from '../db/agent-map.js';
20
- import {
21
- findEndpointByWebhookId,
22
- listEndpoints,
23
- tokenMatches,
24
- webhookPort,
25
- type Endpoint,
26
- } from './tunnel.js';
27
- import { attachmentEventUrl, handleAttachRequest } from './attach-serve.js';
23
+ } from '@metro-labs/core/events';
24
+ import type { TrainEvent } from '@metro-labs/core/trains/protocol';
25
+ import { agentForLine, agentIdForLine } from '../agents/map.js';
26
+ import type { Endpoint } from '@metro-labs/core/endpoints';
27
+ import { findEndpointByWebhookId, listEndpoints, tokenMatches } from '../stations/webhook-endpoints.js';
28
+ import { attachmentEventUrl, handleAttachRequest, webhookPort } from '../files/attach-serve.js';
28
29
  import { webhookEntry } from '@metro-labs/webhook';
29
- import { handleGoogleAuthRequest } from './google-oauth.js';
30
- import { handleUploadRequest } from './upload-api.js';
30
+ import { attachTerminalSockets } from '../terminal/socket.js';
31
+ import { tmuxCommand } from '../terminal/api.js';
32
+ import { handleModeRequest } from '@metro-labs/http/mode-api';
33
+ import { handleUploadRequest } from '../files/upload-api.js';
31
34
  import {
32
35
  handleMonitorRequest,
33
- type MonitorCall,
34
36
  } from '../monitor/api.js';
35
- import { applyMcpCors, handleMcpPreflight } from './cors.js';
37
+ import { applyMcpCors, handleMcpPreflight } from '@metro-labs/http/cors';
38
+ import { METRO_VERSION } from '@metro-labs/core/version';
36
39
 
37
40
  const LRU_CAP = 2_000;
38
41
 
39
- const METRO_VERSION = process.env.npm_package_version ?? '0.1.0-beta.15';
40
-
41
42
  function dedupKey(
42
43
  e: Pick<MetroEvent, 'station' | 'line' | 'messageId'>,
43
44
  ): string | null {
@@ -125,6 +126,18 @@ function eventText(env: TrainEvent): string | undefined {
125
126
  return env.emoji ? `[react ${env.emoji}]` : undefined;
126
127
  }
127
128
 
129
+ function addressingFacts(
130
+ env: TrainEvent,
131
+ isPrivate: boolean,
132
+ ): Pick<MetroEvent, 'isPrivate' | 'mentionsSelf' | 'replyToSelf' | 'senderVerified'> {
133
+ return {
134
+ ...(isPrivate ? { isPrivate: true } : {}),
135
+ ...(env.mentions_self === true ? { mentionsSelf: true } : {}),
136
+ ...(env.reply_to_self === true ? { replyToSelf: true } : {}),
137
+ ...(typeof env.sender_verified === 'boolean' ? { senderVerified: env.sender_verified } : {}),
138
+ };
139
+ }
140
+
128
141
  export function trainEventToMetroEvent(
129
142
  env: TrainEvent,
130
143
  trainName: string,
@@ -152,6 +165,7 @@ export function trainEventToMetroEvent(
152
165
  text,
153
166
  messageId: env.message_id,
154
167
  replyTo: env.reply_to,
168
+ ...addressingFacts(env, isPrivate),
155
169
  payload: env.payload,
156
170
  };
157
171
  }
@@ -162,16 +176,18 @@ export async function startWebhookServer(
162
176
  emit: Emit,
163
177
  apis: SessionApis = {},
164
178
  mcp?: McpHandler,
165
- monitorCall?: MonitorCall,
179
+ monitor = false,
166
180
  ): Promise<Server> {
167
181
  const port = webhookPort();
168
182
  const server = createServer((req, res) => {
169
- handleRequest(req, res, emit, apis, mcp, monitorCall).catch(
183
+ handleRequest(req, res, emit, apis, mcp, monitor).catch(
170
184
  (err: unknown) => {
171
185
  log.warn({ err: errMsg(err) }, 'webhook handler error');
172
186
  if (!res.headersSent) res.writeHead(500).end();
173
187
  });
174
188
  });
189
+ if (apis.terminalApi !== undefined)
190
+ attachTerminalSockets(server, { command: apis.terminalApi.command ?? tmuxCommand });
175
191
  await new Promise<void>((resolve, reject) => {
176
192
  server.once('error', reject);
177
193
  const host = process.env.METRO_HTTP_HOST ?? '127.0.0.1';
@@ -196,29 +212,6 @@ function isMcpPath(req: IncomingMessage): boolean {
196
212
  return path === '/' || path === '/mcp';
197
213
  }
198
214
 
199
- export const WEBHOOK_BODY_MAX = 25 * 1024 * 1024;
200
-
201
- export class BodyTooLargeError extends Error {
202
- constructor(readonly limit: number) {
203
- super(`request body exceeds ${limit} bytes`);
204
- }
205
- }
206
-
207
- export async function readBody(
208
- req: IncomingMessage,
209
- maxBytes: number,
210
- ): Promise<Buffer> {
211
- const chunks: Buffer[] = [];
212
- let total = 0;
213
- for await (const c of req) {
214
- const buf = c as Buffer;
215
- total += buf.length;
216
- if (total > maxBytes) throw new BodyTooLargeError(maxBytes);
217
- chunks.push(buf);
218
- }
219
- return Buffer.concat(chunks);
220
- }
221
-
222
215
  function flatHeaders(req: IncomingMessage): Record<string, string> {
223
216
  return Object.fromEntries(
224
217
  Object.entries(req.headers).map(([k, v]) => [
@@ -322,20 +315,35 @@ async function handleWebhookRoute(
322
315
  return true;
323
316
  }
324
317
 
318
+ function handleSignInRoutes(
319
+ req: IncomingMessage,
320
+ res: ServerResponse,
321
+ apis: SessionApis,
322
+ ): boolean {
323
+ return apis.mode !== undefined && handleModeRequest(req, res, apis.mode);
324
+ }
325
+
326
+ function handleEarlyRoutes(req: IncomingMessage, res: ServerResponse, apis: SessionApis): boolean {
327
+ if (handleHealth(req, res)) return true;
328
+ if (apis.gateway !== undefined && handleGatewayRequest(req, res, apis.gateway)) return true;
329
+ if (handleSignInRoutes(req, res, apis)) return true;
330
+ return handleSessionApis(req, res, apis);
331
+ }
332
+
325
333
  async function handlePreMcpRoutes(
326
334
  req: IncomingMessage,
327
335
  res: ServerResponse,
328
336
  emit: Emit,
329
337
  apis: SessionApis,
330
- monitorCall?: MonitorCall,
338
+ monitor = false,
331
339
  ): Promise<boolean> {
332
- if (handleHealth(req, res)) return true;
333
- if (await handleGoogleAuthRequest(req, res)) return true;
334
- if (handleSessionApis(req, res, apis)) return true;
340
+ if (handleEarlyRoutes(req, res, apis)) return true;
335
341
  if (handleUploadRequest(req, res)) return true;
336
342
  if (handleAttachRequest(req, res)) return true;
343
+ if (apis.relayApi && handleRelayRequest(req, res, apis.relayApi)) return true;
337
344
  if (await handleWebhookRoute(req, res, emit)) return true;
338
- return Boolean(monitorCall && handleMonitorRequest(req, res, monitorCall));
345
+ if (await handleThreemaCallback(req, res)) return true;
346
+ return monitor && handleMonitorRequest(req, res);
339
347
  }
340
348
 
341
349
  async function handleRequest(
@@ -344,9 +352,9 @@ async function handleRequest(
344
352
  emit: Emit,
345
353
  apis: SessionApis = {},
346
354
  mcp?: McpHandler,
347
- monitorCall?: MonitorCall,
355
+ monitor = false,
348
356
  ): Promise<void> {
349
- if (await handlePreMcpRoutes(req, res, emit, apis, monitorCall)) return;
357
+ if (await handlePreMcpRoutes(req, res, emit, apis, monitor)) return;
350
358
  if (mcp && isMcpPath(req)) {
351
359
  applyMcpCors(req, res);
352
360
  if (handleMcpPreflight(req, res)) return;