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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (436) hide show
  1. package/dist/api.js +9 -101
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +152 -0
  4. package/dist/cli.js +52 -109
  5. package/dist/control.js +47 -75
  6. package/dist/hold.js +210 -0
  7. package/dist/local.js +43 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +44 -0
  11. package/dist/provider-flags.js +9 -0
  12. package/dist/route.js +58 -0
  13. package/dist/runtime-install.js +96 -0
  14. package/dist/runtime.js +29 -52
  15. package/dist/serve.js +157 -0
  16. package/dist/service.js +304 -0
  17. package/dist/tail.js +119 -0
  18. package/dist/update.js +13 -1
  19. package/package.json +2 -23
  20. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  21. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
  22. package/runtime/marketplace/plugin/.mcp.json +1 -0
  23. package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
  24. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
  25. package/runtime/marketplace/plugin/bin/policy.mjs +97 -0
  26. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  27. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  28. package/runtime/marketplace/plugin/orchestrator.md +26 -0
  29. package/runtime/node_modules/@metro-labs/core/package.json +47 -0
  30. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  31. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
  32. package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
  33. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  34. package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +24 -28
  35. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
  36. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
  37. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  38. package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
  39. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +83 -0
  40. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
  41. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  44. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +6 -11
  45. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +84 -0
  47. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +18 -1
  48. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  49. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  50. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +89 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +144 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +37 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +89 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +142 -0
  73. package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
  74. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-map.ts → daemon/src/agents/map.ts} +58 -1
  75. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  76. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +150 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +218 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
  84. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
  85. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
  86. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
  87. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
  88. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +70 -45
  89. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
  90. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
  91. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
  92. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +355 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +313 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +273 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  104. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
  105. package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  108. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
  109. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
  110. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
  111. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
  112. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +347 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  120. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
  121. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
  122. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  123. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
  124. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
  125. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +195 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +128 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +224 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +278 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +14 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
  155. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
  156. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  157. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +49 -20
  158. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
  159. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
  160. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
  161. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +24 -79
  162. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
  163. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
  164. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
  173. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +33 -42
  174. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  176. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
  177. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +68 -119
  178. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
  179. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  180. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  181. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  182. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  187. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +59 -51
  188. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +205 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +66 -0
  190. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  191. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  192. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  193. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  194. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  195. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  196. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  197. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  198. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
  199. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +9 -36
  200. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
  201. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
  202. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
  203. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
  204. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +75 -9
  205. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
  206. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +22 -6
  207. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  208. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
  209. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
  210. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  211. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
  212. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  213. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  214. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  215. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  216. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  217. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  218. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +9 -0
  219. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  220. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  221. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  222. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +147 -0
  223. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
  224. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
  225. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/package.json +3 -5
  226. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +8 -28
  227. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +19 -187
  228. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +3 -3
  229. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +31 -72
  230. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +2 -2
  231. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +2 -2
  232. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +15 -21
  233. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/members.ts +2 -2
  234. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  235. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/send-files.ts +12 -13
  236. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +23 -0
  237. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  238. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
  239. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
  240. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  241. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  242. package/runtime/node_modules/@metro-labs/{telegram-user → outlook}/package.json +2 -5
  243. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +93 -0
  244. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
  245. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
  246. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
  247. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
  248. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
  249. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +140 -0
  250. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
  251. package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
  252. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
  253. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  254. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
  255. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
  256. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
  257. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
  258. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
  259. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  260. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -0
  261. package/runtime/node_modules/@metro-labs/telegram/package.json +3 -2
  262. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +38 -96
  263. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +214 -253
  264. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +67 -108
  265. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +6 -6
  266. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +101 -147
  267. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +4 -4
  268. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
  269. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
  270. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +6 -102
  271. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
  272. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +64 -151
  273. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +61 -33
  274. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  275. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +3 -3
  276. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +11 -1
  277. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +5 -47
  278. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  279. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +86 -0
  280. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +240 -0
  281. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  282. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +170 -0
  283. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  284. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +90 -0
  285. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  286. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
  287. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +21 -0
  288. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
  289. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  290. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +95 -0
  291. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -0
  292. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  293. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  294. package/runtime/node_modules/@metro-labs/threema/src/files.ts +47 -0
  295. package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -0
  296. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  297. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  298. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
  299. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  300. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  301. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  302. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  303. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  304. package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
  305. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
  306. package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
  307. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
  308. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
  309. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
  310. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
  311. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
  312. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
  313. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
  314. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
  315. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
  316. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
  317. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
  318. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
  319. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
  320. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
  321. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  322. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
  323. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
  324. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
  325. package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
  326. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
  327. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
  328. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +15 -204
  329. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +4 -43
  330. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +41 -219
  331. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
  332. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +3 -3
  333. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
  334. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
  335. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
  336. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
  337. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
  338. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +12 -20
  339. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +1 -19
  340. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
  341. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  342. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  343. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  344. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  345. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +5 -1
  346. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +4 -51
  347. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +30 -44
  348. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
  349. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
  350. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
  351. package/runtime/runtime.json +1 -1
  352. package/runtime/server.ts +1 -0
  353. package/runtime/stations.json +37 -0
  354. package/dist/prompt.js +0 -10
  355. package/dist/store.js +0 -70
  356. package/runtime/node_modules/@metro-labs/discord/src/station.ts +0 -19
  357. package/runtime/node_modules/@metro-labs/discord/src/voice-speak.ts +0 -195
  358. package/runtime/node_modules/@metro-labs/discord/src/voice-transcribe.ts +0 -292
  359. package/runtime/node_modules/@metro-labs/discord/src/voice.ts +0 -197
  360. package/runtime/node_modules/@metro-labs/discord/src/wire.ts +0 -5
  361. package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
  362. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
  363. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
  364. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -335
  365. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
  366. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -322
  367. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
  368. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
  369. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
  370. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
  371. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
  372. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
  373. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
  374. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
  375. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
  376. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
  377. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
  378. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
  379. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
  380. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
  381. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
  382. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
  383. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
  384. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
  385. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
  386. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
  387. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
  388. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
  389. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
  390. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
  391. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +0 -364
  392. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
  393. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
  394. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
  395. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
  396. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
  397. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
  398. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
  399. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
  400. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
  401. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
  402. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
  403. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
  404. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
  405. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
  406. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
  407. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
  408. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
  409. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -3
  410. package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
  411. package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
  412. package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
  413. package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
  414. package/runtime/node_modules/@metro-labs/telegram-user/src/inbound.ts +0 -78
  415. package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
  416. package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
  417. package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
  418. package/runtime/node_modules/@metro-labs/telegram-user/src/normalize.ts +0 -22
  419. package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
  420. package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
  421. package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
  422. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  423. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
  424. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
  425. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
  426. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
  427. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
  428. /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
  429. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
  430. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
  431. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
  432. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/api-base.ts +0 -0
  433. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.ts +0 -0
  434. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
  435. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
  436. /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
@@ -0,0 +1,385 @@
1
+ import { join } from 'node:path';
2
+ import { syncPluginServers } from './plugin-sync.js';
3
+ import { registerConnectors } from './gates.js';
4
+ import type { ToolPolicy } from '../policy/policy.js';
5
+ import type { ToolGroup } from '@metro-labs/core/stations/types';
6
+ import { listRemoteTools, type RemoteTool } from './tools.js';
7
+ import { readJson, writeSecure } from '@metro-labs/core/secure-fs';
8
+ import { errMsg, log } from '@metro-labs/core/log';
9
+ import {
10
+ authHeaders,
11
+ connectorUrlText,
12
+ ConnectorVerifyError,
13
+ parseConnectorUrl,
14
+ verifyRemoteMcp,
15
+ type ConnectorAuth,
16
+ type OAuthAuth,
17
+ type VerifiedRecord,
18
+ } from './verify.js';
19
+ import { oauthExpired, refreshOAuth } from './oauth.js';
20
+ import { advertisesOAuth } from './oauth-discovery.js';
21
+ import {
22
+ ConnectorError,
23
+ connectorAuth,
24
+ connectorClient,
25
+ connectorName,
26
+ readConfig,
27
+ signInState,
28
+ stamp,
29
+ type ConnectorConfig,
30
+ type ConnectorSignIn,
31
+ } from './config.js';
32
+ import type { OAuthClient } from './oauth-client.js';
33
+ import { agentsDir } from '../agents/files.js';
34
+ import { newId } from '@metro-labs/core/ids';
35
+ import type { LoadedConnector } from '../stations/materialize.js';
36
+
37
+ export interface LocalConnectorRow {
38
+ id: string;
39
+ name: string;
40
+ url: string;
41
+ config: ConnectorConfig;
42
+ }
43
+
44
+ export interface Connector {
45
+ id: string;
46
+ name: string;
47
+ url: string;
48
+ auth: ConnectorAuth['kind'];
49
+ header: string | null;
50
+ signIn: ConnectorSignIn;
51
+ verified: VerifiedRecord;
52
+ client: OAuthClient | null;
53
+ policy: ToolPolicy;
54
+ }
55
+
56
+ export interface PendingConnectorInput {
57
+ name: unknown;
58
+ url: unknown;
59
+ clientId: unknown;
60
+ clientSecret: unknown;
61
+ }
62
+
63
+ export interface ConnectorInput extends PendingConnectorInput {
64
+ header: unknown;
65
+ value: unknown;
66
+ }
67
+
68
+ export type ConnectorCheck =
69
+ | { id: string; name: string; ok: true; verified: VerifiedRecord }
70
+ | { id: string; name: string; ok: false; reason: string };
71
+
72
+ export interface DeletedConnector {
73
+ id: string;
74
+ name: string;
75
+ }
76
+
77
+ const UNVERIFIED = { at: '', server: '' };
78
+
79
+ function connectorFromRow(row: LocalConnectorRow): Connector {
80
+ const auth = row.config.auth;
81
+ return {
82
+ id: row.id,
83
+ name: row.name,
84
+ url: row.url,
85
+ auth: auth.kind,
86
+ header: auth.kind === 'header' ? auth.name : null,
87
+ signIn: signInState(row.config),
88
+ verified: row.config.verified,
89
+ client: row.config.client,
90
+ policy: row.config.policy ?? {},
91
+ };
92
+ }
93
+
94
+ const FILE = 'connectors.json';
95
+ const filePath = (dir: string): string => join(dir, FILE);
96
+
97
+ const isRow = (v: unknown): v is { id: string; name: string; url: string; config?: unknown } =>
98
+ typeof v === 'object' &&
99
+ v !== null &&
100
+ typeof (v as { id?: unknown }).id === 'string' &&
101
+ typeof (v as { name?: unknown }).name === 'string' &&
102
+ typeof (v as { url?: unknown }).url === 'string';
103
+
104
+ const asList = (value: unknown): unknown[] =>
105
+ Array.isArray(value) ? value.map((item: unknown) => item) : [];
106
+
107
+ export function readLocalConnectors(dir = agentsDir()): LocalConnectorRow[] {
108
+ const raw = readJson<{ connectors?: unknown }>(filePath(dir), {}, {
109
+ warn: 'connectors.json: malformed, ignoring',
110
+ });
111
+ return asList(raw.connectors).filter(isRow).map((r) => ({
112
+ id: r.id,
113
+ name: r.name,
114
+ url: r.url,
115
+ config: readConfig(r.config),
116
+ }));
117
+ }
118
+
119
+ function writeRows(dir: string, rows: LocalConnectorRow[]): void {
120
+ writeSecure(filePath(dir), `${JSON.stringify({ version: 1, connectors: rows }, null, 2)}\n`);
121
+ if (dir !== agentsDir()) return;
122
+ syncPluginServers(rows);
123
+ registerConnectors(rows);
124
+ }
125
+
126
+ const missing = (): ConnectorError => new ConnectorError('no such connector', 404);
127
+
128
+ function rowOrThrow(id: string, dir: string): LocalConnectorRow {
129
+ const row = readLocalConnectors(dir).find((r) => r.id === id);
130
+ if (row === undefined) throw missing();
131
+ return row;
132
+ }
133
+
134
+ function replace(dir: string, row: LocalConnectorRow): Connector {
135
+ writeRows(dir, readLocalConnectors(dir).map((r) => (r.id === row.id ? row : r)));
136
+ return connectorFromRow(row);
137
+ }
138
+
139
+ async function oauthCapable(url: URL, auth: ConnectorAuth): Promise<boolean> {
140
+ if (auth.kind === 'oauth') return true;
141
+ if (auth.kind === 'header') return false;
142
+ return advertisesOAuth(url);
143
+ }
144
+
145
+ export function localImportConnectors(
146
+ rows: LoadedConnector[],
147
+ dir = agentsDir(),
148
+ mode: 'append' | 'overwrite' = 'overwrite',
149
+ ): number {
150
+ const current = readLocalConnectors(dir);
151
+ const fresh =
152
+ mode === 'overwrite'
153
+ ? rows
154
+ : rows.filter((row) => !current.some((r) => r.id === row.id || r.name === row.name));
155
+ const imported: LocalConnectorRow[] = fresh.map((row) => ({
156
+ id: row.id,
157
+ name: row.name,
158
+ url: row.url,
159
+ config: readConfig(row.config),
160
+ }));
161
+ const ids = new Set(imported.map((r) => r.id));
162
+ const names = new Set(imported.map((r) => r.name));
163
+ writeRows(dir, [...current.filter((r) => !ids.has(r.id) && !names.has(r.name)), ...imported]);
164
+ return imported.length;
165
+ }
166
+
167
+ export async function localListConnectors(dir = agentsDir()): Promise<Connector[]> {
168
+ return Promise.resolve(readLocalConnectors(dir).map(connectorFromRow));
169
+ }
170
+
171
+ export async function localGetConnector(id: string, dir = agentsDir()): Promise<Connector> {
172
+ return Promise.resolve(connectorFromRow(rowOrThrow(id, dir)));
173
+ }
174
+
175
+ function assertNameFree(name: string, exceptId: string | null, dir: string): void {
176
+ if (readLocalConnectors(dir).some((r) => r.id !== exceptId && r.name === name))
177
+ throw new ConnectorError(`a connector named '${name}' already exists on this daemon`, 409);
178
+ }
179
+
180
+ function insert(dir: string, name: string, url: URL, config: ConnectorConfig): Connector {
181
+ assertNameFree(name, null, dir);
182
+ const row: LocalConnectorRow = { id: newId(), name, url: connectorUrlText(url), config };
183
+ writeRows(dir, [...readLocalConnectors(dir), row]);
184
+ return connectorFromRow(row);
185
+ }
186
+
187
+ export async function localCreateConnector(input: ConnectorInput, dir = agentsDir()): Promise<Connector> {
188
+ const name = connectorName(input.name);
189
+ const url = parseConnectorUrl(input.url);
190
+ const auth = connectorAuth(input.header, input.value);
191
+ const client = connectorClient(input.clientId, input.clientSecret);
192
+ const verified = stamp(await verifyRemoteMcp(url, auth));
193
+ return insert(dir, name, url, {
194
+ auth,
195
+ createdAt: new Date().toISOString(),
196
+ verified,
197
+ oauth: await oauthCapable(url, auth),
198
+ client,
199
+ });
200
+ }
201
+
202
+ export async function localCreatePendingConnector(input: PendingConnectorInput, dir = agentsDir()): Promise<Connector> {
203
+ const name = connectorName(input.name);
204
+ const url = parseConnectorUrl(input.url);
205
+ const client = connectorClient(input.clientId, input.clientSecret);
206
+ return Promise.resolve(
207
+ insert(dir, name, url, { auth: { kind: 'none' }, createdAt: new Date().toISOString(), verified: UNVERIFIED, oauth: true, client }),
208
+ );
209
+ }
210
+
211
+ export async function localReconnectConnector(
212
+ id: string,
213
+ auth: OAuthAuth,
214
+ dir = agentsDir(),
215
+ ): Promise<Connector> {
216
+ const row = rowOrThrow(id, dir);
217
+ const verified = stamp(await verifyRemoteMcp(parseConnectorUrl(row.url), auth));
218
+ return replace(dir, { ...row, config: { ...row.config, auth, verified, oauth: true } });
219
+ }
220
+
221
+ const signedOut = (row: LocalConnectorRow): LocalConnectorRow => ({ ...row, config: { ...row.config, auth: { kind: 'none' }, oauth: true } });
222
+
223
+ export async function localDisconnectConnector(id: string, dir = agentsDir()): Promise<Connector> {
224
+ const row = rowOrThrow(id, dir);
225
+ if (row.config.auth.kind !== 'oauth') throw new ConnectorError('that connector is not signed in', 400);
226
+ return Promise.resolve(replace(dir, signedOut(row)));
227
+ }
228
+
229
+ export function localMarkSignedOut(id: string, dir = agentsDir()): void {
230
+ const row = readLocalConnectors(dir).find((r) => r.id === id);
231
+ if (row?.config.auth.kind !== 'oauth') return;
232
+ log.info({ id, name: row.name }, 'local relay: the vendor refused the token after a refresh; the connector is signed out');
233
+ replace(dir, signedOut(row));
234
+ }
235
+
236
+ async function freshAuth(row: LocalConnectorRow, dir: string): Promise<ConnectorAuth> {
237
+ const auth = row.config.auth;
238
+ if (auth.kind !== 'oauth' || !oauthExpired(auth)) return auth;
239
+ return refreshOnce(row, auth, dir);
240
+ }
241
+
242
+ function updateRow(dir: string, id: string, config: Partial<LocalConnectorRow['config']>): void {
243
+ const now = readLocalConnectors(dir).find((r) => r.id === id);
244
+ if (now !== undefined) replace(dir, { ...now, config: { ...now.config, ...config } });
245
+ }
246
+
247
+ export async function localVerifyConnector(id: string, dir = agentsDir()): Promise<ConnectorCheck> {
248
+ const row = rowOrThrow(id, dir);
249
+ try {
250
+ const url = parseConnectorUrl(row.url);
251
+ const auth = await freshAuth(row, dir);
252
+ const verified = stamp(await verifyRemoteMcp(url, auth));
253
+ const oauth = row.config.oauth || (await oauthCapable(url, auth));
254
+ updateRow(dir, row.id, { auth, verified, oauth });
255
+ return { id: row.id, name: row.name, ok: true, verified };
256
+ } catch (err) {
257
+ if (!(err instanceof ConnectorVerifyError)) throw err;
258
+ return { id: row.id, name: row.name, ok: false, reason: err.message };
259
+ }
260
+ }
261
+
262
+ const groupsOf = (tools: RemoteTool[]): Record<string, ToolGroup> =>
263
+ Object.fromEntries(tools.map((tool) => [tool.name, tool.readOnly ? 'read' : 'write'] as const));
264
+
265
+ function keepGroups(dir: string, id: string, tools: RemoteTool[]): void {
266
+ const now = readLocalConnectors(dir).find((r) => r.id === id);
267
+ const groups = groupsOf(tools);
268
+ if (now === undefined || JSON.stringify(now.config.toolGroups ?? {}) === JSON.stringify(groups)) return;
269
+ const config = { ...now.config };
270
+ delete config.toolGroups;
271
+ replace(dir, { ...now, config: Object.keys(groups).length > 0 ? { ...config, toolGroups: groups } : config });
272
+ }
273
+
274
+ export async function localConnectorTools(id: string, dir = agentsDir()): Promise<RemoteTool[]> {
275
+ const row = rowOrThrow(id, dir);
276
+ const url = parseConnectorUrl(row.url);
277
+ const tools = await listRemoteTools(url, await freshAuth(row, dir));
278
+ keepGroups(dir, id, tools);
279
+ return tools;
280
+ }
281
+
282
+ async function refreshGroups(id: string, dir: string): Promise<void> {
283
+ try {
284
+ await localConnectorTools(id, dir);
285
+ } catch (err) {
286
+ log.warn({ id, err: errMsg(err) }, 'connector policy: could not list the tools; the last known read and write groups stay');
287
+ }
288
+ }
289
+
290
+ export async function localSetConnectorPolicy(id: string, policy: ToolPolicy, dir = agentsDir()): Promise<Connector> {
291
+ const row = rowOrThrow(id, dir);
292
+ const config = { ...row.config };
293
+ delete config.policy;
294
+ const saved = replace(dir, { ...row, config: Object.keys(policy).length > 0 ? { ...config, policy } : config });
295
+ await refreshGroups(id, dir);
296
+ return saved;
297
+ }
298
+
299
+ export async function loadConnectorPolicies(dir = agentsDir()): Promise<void> {
300
+ const rows = readLocalConnectors(dir);
301
+ registerConnectors(rows);
302
+ for (const row of rows) if (row.config.policy !== undefined) await refreshGroups(row.id, dir);
303
+ }
304
+
305
+ export async function localRenameConnector(id: string, raw: string, dir = agentsDir()): Promise<Connector> {
306
+ const name = connectorName(raw);
307
+ const row = rowOrThrow(id, dir);
308
+ assertNameFree(name, id, dir);
309
+ return Promise.resolve(replace(dir, { ...row, name }));
310
+ }
311
+
312
+ export async function localDeleteConnector(id: string, dir = agentsDir()): Promise<DeletedConnector> {
313
+ const row = rowOrThrow(id, dir);
314
+ writeRows(dir, readLocalConnectors(dir).filter((r) => r.id !== id));
315
+ return Promise.resolve({ id: row.id, name: row.name });
316
+ }
317
+
318
+ const inflight = new Map<string, Promise<OAuthAuth>>();
319
+
320
+ function refreshOnce(row: LocalConnectorRow, auth: OAuthAuth, dir: string): Promise<OAuthAuth> {
321
+ const running = inflight.get(row.id);
322
+ if (running !== undefined) return running;
323
+ const job = refreshOAuth(auth, parseConnectorUrl(row.url).toString())
324
+ .then((fresh) => {
325
+ updateRow(dir, row.id, { auth: fresh });
326
+ return fresh;
327
+ })
328
+ .finally(() => {
329
+ inflight.delete(row.id);
330
+ });
331
+ inflight.set(row.id, job);
332
+ return job;
333
+ }
334
+
335
+ export type RelayTarget =
336
+ | { kind: 'ok'; url: string; headers: Record<string, string> }
337
+ | { kind: 'missing' }
338
+ | { kind: 'signin' };
339
+
340
+ function staleUsable(auth: OAuthAuth, now = Date.now()): boolean {
341
+ return auth.expiresAt === undefined || auth.expiresAt > now;
342
+ }
343
+
344
+ function unrefreshedTarget(
345
+ url: string,
346
+ auth: OAuthAuth,
347
+ force: boolean,
348
+ now = Date.now(),
349
+ ): RelayTarget {
350
+ if (force) return { kind: 'signin' };
351
+ return { kind: 'ok', url, headers: staleUsable(auth, now) ? authHeaders(auth) : {} };
352
+ }
353
+
354
+ function fixedTarget(
355
+ url: string,
356
+ auth: Exclude<ConnectorAuth, OAuthAuth>,
357
+ force: boolean,
358
+ ): RelayTarget {
359
+ if (force) return { kind: 'signin' };
360
+ return { kind: 'ok', url, headers: authHeaders(auth) };
361
+ }
362
+
363
+ async function oauthTarget(row: LocalConnectorRow, auth: OAuthAuth, force: boolean, dir: string): Promise<RelayTarget> {
364
+ if (!force && !oauthExpired(auth)) return { kind: 'ok', url: row.url, headers: authHeaders(auth) };
365
+ try {
366
+ const fresh = await refreshOnce(row, auth, dir);
367
+ return { kind: 'ok', url: row.url, headers: authHeaders(fresh) };
368
+ } catch (err) {
369
+ log.warn({ id: row.id, err: errMsg(err) }, 'local relay: token refresh failed');
370
+ return unrefreshedTarget(row.url, auth, force);
371
+ }
372
+ }
373
+
374
+ export async function localRelayTarget(
375
+ connectorId: string,
376
+ force: boolean,
377
+ dir = agentsDir(),
378
+ ): Promise<RelayTarget> {
379
+ const row = readLocalConnectors(dir).find((r) => r.id === connectorId);
380
+ if (row === undefined) return { kind: 'missing' };
381
+ parseConnectorUrl(row.url);
382
+ const auth = row.config.auth;
383
+ if (auth.kind === 'oauth') return oauthTarget(row, auth, force, dir);
384
+ return fixedTarget(row.url, auth, force);
385
+ }
@@ -0,0 +1,81 @@
1
+ import { isRecord } from '@metro-labs/core/is-record';
2
+ import { errMsg, log } from '@metro-labs/core/log';
3
+ import { authHeaders, INITIALIZE, mcpPost, payloadOf, refused, type ConnectorAuth } from './verify.js';
4
+
5
+ export interface RemoteTool {
6
+ name: string;
7
+ title: string;
8
+ description: string;
9
+ readOnly: boolean;
10
+ }
11
+
12
+ const TIMEOUT_MS = 15_000;
13
+ const MAX_PAGES = 10;
14
+
15
+ function resultOf(text: string, contentType: string): Record<string, unknown> | null {
16
+ let parsed: unknown = null;
17
+ try {
18
+ parsed = JSON.parse(payloadOf(text, contentType));
19
+ } catch {
20
+ return null;
21
+ }
22
+ return isRecord(parsed) && isRecord(parsed.result) ? parsed.result : null;
23
+ }
24
+
25
+ async function rpc(url: URL, auth: ConnectorAuth, session: Map<string, string>, body: unknown): Promise<Record<string, unknown> | null> {
26
+ const res = await mcpPost(url, auth, body, { timeoutMs: TIMEOUT_MS, headers: Object.fromEntries(session) });
27
+ const id = res.headers.get('mcp-session-id');
28
+ if (id !== null && id !== '') session.set('mcp-session-id', id);
29
+ const text = await res.text().catch(() => '');
30
+ if (res.status === 401 || res.status === 403) throw refused(`${url.hostname} rejected the credential; connect it again`);
31
+ if (!res.ok) throw refused(`${url.hostname} answered ${String(res.status)}`);
32
+ return resultOf(text, res.headers.get('content-type') ?? '');
33
+ }
34
+
35
+ function toTool(raw: unknown): RemoteTool | null {
36
+ if (!isRecord(raw) || typeof raw.name !== 'string') return null;
37
+ const annotations = isRecord(raw.annotations) ? raw.annotations : {};
38
+ const title = typeof raw.title === 'string' && raw.title !== '' ? raw.title : raw.name;
39
+ return { name: raw.name, title, description: typeof raw.description === 'string' ? raw.description : '', readOnly: annotations.readOnlyHint === true };
40
+ }
41
+
42
+ async function endSession(url: URL, auth: ConnectorAuth, session: Map<string, string>): Promise<void> {
43
+ if (!session.has('mcp-session-id')) return;
44
+ try {
45
+ const res = await fetch(url, { method: 'DELETE', redirect: 'manual', signal: AbortSignal.timeout(TIMEOUT_MS), headers: { ...authHeaders(auth), ...Object.fromEntries(session) } });
46
+ await res.body?.cancel();
47
+ } catch (err) {
48
+ log.debug({ host: url.hostname, err: errMsg(err) }, 'connector-tools: the remote declined to end the session');
49
+ }
50
+ }
51
+
52
+ const toolsOf = (result: Record<string, unknown>): RemoteTool[] =>
53
+ Array.isArray(result.tools) ? result.tools.flatMap((raw: unknown) => toTool(raw) ?? []) : [];
54
+
55
+ function nextCursor(result: Record<string, unknown>, current: string): string | null {
56
+ const next = result.nextCursor;
57
+ return typeof next !== 'string' || next === '' || next === current ? null : next;
58
+ }
59
+
60
+ async function walkTools(url: URL, auth: ConnectorAuth, session: Map<string, string>): Promise<RemoteTool[]> {
61
+ const tools: RemoteTool[] = [];
62
+ let cursor: string | null = '';
63
+ for (let page = 0; page < MAX_PAGES && cursor !== null; page += 1) {
64
+ const result = await rpc(url, auth, session, { jsonrpc: '2.0', id: 2 + page, method: 'tools/list', params: cursor === '' ? {} : { cursor } });
65
+ if (result === null) break;
66
+ tools.push(...toolsOf(result));
67
+ cursor = nextCursor(result, cursor);
68
+ }
69
+ return tools;
70
+ }
71
+
72
+ export async function listRemoteTools(url: URL, auth: ConnectorAuth): Promise<RemoteTool[]> {
73
+ const session = new Map<string, string>();
74
+ const init = await rpc(url, auth, session, INITIALIZE);
75
+ if (init === null || typeof init.protocolVersion !== 'string') throw refused(`${url.hostname} answered, but it does not speak MCP.`);
76
+ session.set('mcp-protocol-version', init.protocolVersion);
77
+ await rpc(url, auth, session, { jsonrpc: '2.0', method: 'notifications/initialized' });
78
+ const tools = await walkTools(url, auth, session);
79
+ await endSession(url, auth, session);
80
+ return tools;
81
+ }
@@ -0,0 +1,37 @@
1
+ import { ApiError } from '@metro-labs/http/api-error';
2
+
3
+ export class ConnectorVerifyError extends ApiError {}
4
+
5
+ export class ConnectorUnauthorized extends ConnectorVerifyError {}
6
+
7
+ export function refused(message: string): ConnectorVerifyError {
8
+ return new ConnectorVerifyError(message, 400);
9
+ }
10
+
11
+ function refuseShape(url: URL): void {
12
+ if (url.protocol !== 'https:' && url.protocol !== 'http:')
13
+ throw refused('a connector url must start with https:// or http://');
14
+ if (url.username !== '' || url.password !== '')
15
+ throw refused('a connector url must not carry a user:password');
16
+ if (url.hash !== '')
17
+ throw refused('a connector url must not carry a #fragment');
18
+ }
19
+
20
+ export function parseConnectorUrl(raw: unknown): URL {
21
+ const text = typeof raw === 'string' ? raw.trim() : '';
22
+ if (text === '') throw refused('a connector url is required');
23
+ let url: URL;
24
+ try {
25
+ url = new URL(text);
26
+ } catch {
27
+ throw refused('that is not a valid url');
28
+ }
29
+ refuseShape(url);
30
+ return url;
31
+ }
32
+
33
+ export function connectorUrlText(url: URL): string {
34
+ const text = url.toString();
35
+ if (url.pathname !== '/' || url.search !== '') return text;
36
+ return text.endsWith('/') ? text.slice(0, -1) : text;
37
+ }
@@ -0,0 +1,106 @@
1
+ import { isRecord } from '@metro-labs/core/is-record';
2
+ import { whyUnreachable } from './reach.js';
3
+ import { ConnectorUnauthorized, refused } from './url.js';
4
+
5
+ export {
6
+ ConnectorUnauthorized,
7
+ ConnectorVerifyError,
8
+ connectorUrlText,
9
+ parseConnectorUrl,
10
+ refused,
11
+ } from './url.js';
12
+
13
+ export interface OAuthTokens {
14
+ accessToken: string;
15
+ refreshToken?: string;
16
+ expiresAt?: number;
17
+ }
18
+
19
+ export interface OAuthAuth extends OAuthTokens {
20
+ kind: 'oauth';
21
+ issuer: string;
22
+ tokenEndpoint: string;
23
+ clientId: string;
24
+ clientSecret?: string;
25
+ scope?: string;
26
+ }
27
+
28
+ export type ConnectorAuth =
29
+ | { kind: 'none' }
30
+ | { kind: 'header'; name: string; value: string }
31
+ | OAuthAuth;
32
+
33
+ export interface VerifiedServer {
34
+ server: string;
35
+ }
36
+
37
+ export interface VerifiedRecord extends VerifiedServer {
38
+ at: string;
39
+ }
40
+
41
+ const PROBE_TIMEOUT_MS = 10_000;
42
+
43
+ export const INITIALIZE = {
44
+ jsonrpc: '2.0',
45
+ id: 1,
46
+ method: 'initialize',
47
+ params: { protocolVersion: '2025-11-25', capabilities: {}, clientInfo: { name: 'metro', version: '0.1.0' } },
48
+ };
49
+
50
+ export function authHeaders(auth: ConnectorAuth): Record<string, string> {
51
+ if (auth.kind === 'header') return { [auth.name]: auth.value };
52
+ if (auth.kind === 'oauth') return { Authorization: `Bearer ${auth.accessToken}` };
53
+ return {};
54
+ }
55
+
56
+ export function payloadOf(text: string, contentType: string): string {
57
+ if (!contentType.includes('text/event-stream')) return text;
58
+ for (const line of text.replace(/\r\n/g, '\n').split('\n')) if (line.startsWith('data:')) return line.slice(5).trim();
59
+ return '';
60
+ }
61
+
62
+ export async function mcpPost(
63
+ url: URL,
64
+ auth: ConnectorAuth,
65
+ body: unknown,
66
+ opts: { timeoutMs: number; headers?: Record<string, string> },
67
+ ): Promise<Response> {
68
+ try {
69
+ return await fetch(url, {
70
+ method: 'POST',
71
+ redirect: 'manual',
72
+ signal: AbortSignal.timeout(opts.timeoutMs),
73
+ headers: { 'content-type': 'application/json', accept: 'application/json, text/event-stream', ...authHeaders(auth), ...opts.headers },
74
+ body: JSON.stringify(body),
75
+ });
76
+ } catch (err) {
77
+ throw refused(`Metro could not reach ${url.hostname}: ${whyUnreachable(err)}`);
78
+ }
79
+ }
80
+
81
+ function serverNameOf(url: URL, text: string, contentType: string): string {
82
+ let parsed: unknown;
83
+ try {
84
+ parsed = JSON.parse(payloadOf(text, contentType));
85
+ } catch {
86
+ throw refused(`${url.hostname} answered, but it does not speak MCP.`);
87
+ }
88
+ const result = isRecord(parsed) && isRecord(parsed.result) ? parsed.result : null;
89
+ if (result === null || typeof result.protocolVersion !== 'string') throw refused(`${url.hostname} answered, but it does not speak MCP.`);
90
+ const info = isRecord(result.serverInfo) ? result.serverInfo : {};
91
+ const title = typeof info.title === 'string' ? info.title : '';
92
+ if (title !== '') return title;
93
+ return typeof info.name === 'string' ? info.name : url.hostname;
94
+ }
95
+
96
+ export async function verifyRemoteMcp(url: URL, auth: ConnectorAuth): Promise<VerifiedServer> {
97
+ const res = await mcpPost(url, auth, INITIALIZE, { timeoutMs: PROBE_TIMEOUT_MS });
98
+ const text = await res.text().catch(() => '');
99
+ if (res.status >= 300 && res.status < 400) throw refused('that url redirects — use the url it redirects to');
100
+ if (res.status === 401 || res.status === 403)
101
+ throw new ConnectorUnauthorized(auth.kind === 'none' ? `${url.hostname} requires authorization.` : `${url.hostname} rejected that credential.`, 400);
102
+ if (res.status === 404 || res.status === 405)
103
+ throw refused(`${url.hostname} did not accept an MCP initialize over HTTP POST. If it is a legacy SSE server, Metro cannot verify it yet.`);
104
+ if (!res.ok) throw refused(`${url.hostname} answered ${String(res.status)} instead of an MCP initialize.`);
105
+ return { server: serverNameOf(url, text, res.headers.get('content-type') ?? '') };
106
+ }
@@ -1,8 +1,8 @@
1
1
  import { randomBytes, timingSafeEqual } from 'node:crypto';
2
- import { ID_RE } from '../db/ids.js';
2
+ import { ID_RE } from '@metro-labs/core/ids';
3
3
  import { readFileSync } from 'node:fs';
4
- import { resolveCachedAttachment } from '../stations/attachments.js';
5
- import { writeSecure } from './secure-fs.js';
4
+ import { resolveCachedAttachment } from '@metro-labs/core/stations/attachments';
5
+ import { writeSecure } from '@metro-labs/core/secure-fs';
6
6
 
7
7
  const GRANT_SUFFIX = '.grant';
8
8
 
@@ -34,7 +34,7 @@ function parseGrant(raw: string): AttachmentGrant | undefined {
34
34
  };
35
35
  }
36
36
 
37
- export function readGrant(path: string): AttachmentGrant | undefined {
37
+ function readGrant(path: string): AttachmentGrant | undefined {
38
38
  try {
39
39
  return parseGrant(readFileSync(`${path}${GRANT_SUFFIX}`, 'utf8'));
40
40
  } catch {
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from 'node:fs';
2
- import { ID_RE } from '../db/ids.js';
3
- import { resolveCachedAttachment } from '../stations/attachments.js';
4
- import { writeSecure } from './secure-fs.js';
2
+ import { ID_RE } from '@metro-labs/core/ids';
3
+ import { resolveCachedAttachment } from '@metro-labs/core/stations/attachments';
4
+ import { writeSecure } from '@metro-labs/core/secure-fs';
5
5
 
6
6
  const OWNER_SUFFIX = '.owner';
7
7