@stage-labs/metro 0.1.0-beta.24 → 0.1.0-beta.241

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 (473) hide show
  1. package/dist/api.js +9 -129
  2. package/dist/background.js +123 -0
  3. package/dist/claude-settings.js +40 -0
  4. package/dist/claude.js +197 -0
  5. package/dist/cli.js +59 -111
  6. package/dist/control.js +19 -103
  7. package/dist/hold.js +210 -0
  8. package/dist/local.js +43 -0
  9. package/dist/mcp-config.js +27 -0
  10. package/dist/memory.js +222 -0
  11. package/dist/node-name.js +71 -0
  12. package/dist/onboarding.js +44 -0
  13. package/dist/provider-flags.js +9 -0
  14. package/dist/route.js +58 -0
  15. package/dist/runtime-install.js +96 -0
  16. package/dist/runtime.js +29 -52
  17. package/dist/serve.js +157 -0
  18. package/dist/service.js +304 -0
  19. package/dist/tail.js +10 -11
  20. package/dist/update.js +13 -1
  21. package/dist/voice.js +56 -0
  22. package/package.json +2 -23
  23. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  24. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
  25. package/runtime/marketplace/plugin/.mcp.json +1 -0
  26. package/runtime/marketplace/plugin/MEMORY.md +78 -0
  27. package/runtime/marketplace/plugin/METRO.md +26 -0
  28. package/runtime/marketplace/plugin/STAGE.md +75 -0
  29. package/runtime/marketplace/plugin/bin/guard.mjs +75 -0
  30. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
  31. package/runtime/marketplace/plugin/bin/policy.mjs +97 -0
  32. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  33. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  34. package/runtime/node_modules/@metro-labs/core/package.json +51 -0
  35. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  36. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
  37. package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
  38. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  39. package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +22 -26
  40. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
  41. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
  42. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +14 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
  44. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +83 -0
  45. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
  46. package/runtime/node_modules/@metro-labs/core/src/stations/mail-text.ts +72 -0
  47. package/runtime/node_modules/@metro-labs/core/src/stations/mail-trust.ts +29 -0
  48. package/runtime/node_modules/@metro-labs/core/src/stations/mail.ts +212 -0
  49. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
  50. package/runtime/node_modules/@metro-labs/core/src/stations/oauth.ts +126 -0
  51. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  52. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  53. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +6 -11
  54. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  55. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +84 -0
  56. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +19 -1
  57. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  58. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  59. package/runtime/node_modules/@metro-labs/daemon/package.json +51 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/crontab.ts +44 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +89 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +39 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +144 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +43 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +91 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +142 -0
  83. package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
  84. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +133 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  86. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
  87. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +150 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +231 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
  94. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
  95. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
  96. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
  97. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
  98. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +75 -45
  99. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
  100. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
  101. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +380 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory-routine.ts +123 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +299 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +351 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/claude/usage-probe.ts +127 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  116. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
  117. package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  120. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
  121. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
  122. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
  123. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
  124. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +347 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-api.ts +116 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-crypto.ts +44 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-store.ts +48 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  135. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
  136. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
  137. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  138. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
  139. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
  140. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +214 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +209 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +155 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +93 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +307 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +235 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +203 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +285 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-lists.ts +57 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +161 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +72 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +104 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +118 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +35 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/gateway/tool-search.ts +126 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage-refresh.ts +102 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +348 -0
  173. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
  174. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  175. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +81 -38
  176. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
  177. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
  178. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
  179. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +37 -79
  180. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +30 -0
  181. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
  182. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +92 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +88 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  187. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  188. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  190. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +75 -0
  191. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +61 -57
  192. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +93 -0
  193. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  194. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
  195. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +97 -114
  196. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
  197. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  198. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  199. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  200. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  201. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  202. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  203. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  204. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  205. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +65 -52
  206. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +209 -0
  207. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +71 -0
  208. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  209. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  210. package/runtime/node_modules/@metro-labs/daemon/src/server/cloudwatch.ts +178 -0
  211. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  212. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  213. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  214. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  215. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  216. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-browser.ts +102 -0
  217. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  218. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
  219. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +5 -31
  220. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
  221. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
  222. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
  223. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +69 -3
  224. package/runtime/node_modules/@metro-labs/{mcp/src/db → daemon/src/stations}/materialize.ts +60 -175
  225. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +20 -2
  226. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  227. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
  228. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
  229. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  230. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
  231. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  232. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  233. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  234. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  235. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  236. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  237. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +9 -0
  238. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  239. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  240. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  241. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +147 -0
  242. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
  243. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
  244. package/runtime/node_modules/@metro-labs/daemon/src/voice/api.ts +14 -0
  245. package/runtime/node_modules/@metro-labs/daemon/src/voice/approvals.ts +43 -0
  246. package/runtime/node_modules/@metro-labs/daemon/src/voice/audio.ts +153 -0
  247. package/runtime/node_modules/@metro-labs/daemon/src/voice/brain.ts +135 -0
  248. package/runtime/node_modules/@metro-labs/daemon/src/voice/call.ts +200 -0
  249. package/runtime/node_modules/@metro-labs/daemon/src/voice/calls.ts +130 -0
  250. package/runtime/node_modules/@metro-labs/daemon/src/voice/peer.ts +84 -0
  251. package/runtime/node_modules/@metro-labs/daemon/src/voice/scribe.ts +103 -0
  252. package/runtime/node_modules/@metro-labs/daemon/src/voice/speakable.ts +54 -0
  253. package/runtime/node_modules/@metro-labs/daemon/src/voice/speech.ts +104 -0
  254. package/runtime/node_modules/@metro-labs/daemon/src/voice/store.ts +116 -0
  255. package/runtime/node_modules/@metro-labs/daemon/src/voice/talk.ts +256 -0
  256. package/runtime/node_modules/@metro-labs/discord-bot/package.json +2 -4
  257. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +3 -23
  258. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +19 -187
  259. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +2 -2
  260. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +26 -67
  261. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +1 -1
  262. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +2 -2
  263. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +8 -14
  264. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +2 -2
  265. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  266. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +12 -13
  267. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +5 -1
  268. package/runtime/node_modules/@metro-labs/gmail/package.json +25 -0
  269. package/runtime/node_modules/@metro-labs/gmail/src/accounts.ts +81 -0
  270. package/runtime/node_modules/@metro-labs/gmail/src/actions.ts +14 -0
  271. package/runtime/node_modules/@metro-labs/gmail/src/api.ts +31 -0
  272. package/runtime/node_modules/@metro-labs/gmail/src/attachments.ts +35 -0
  273. package/runtime/node_modules/@metro-labs/gmail/src/auth.ts +105 -0
  274. package/runtime/node_modules/@metro-labs/gmail/src/config.ts +13 -0
  275. package/runtime/node_modules/@metro-labs/gmail/src/inbound.ts +96 -0
  276. package/runtime/node_modules/@metro-labs/gmail/src/index.ts +12 -0
  277. package/runtime/node_modules/@metro-labs/gmail/src/login.ts +37 -0
  278. package/runtime/node_modules/@metro-labs/gmail/src/message.ts +125 -0
  279. package/runtime/node_modules/@metro-labs/gmail/src/mime.ts +70 -0
  280. package/runtime/node_modules/@metro-labs/gmail/src/outbound.ts +124 -0
  281. package/runtime/node_modules/@metro-labs/gmail/src/query.ts +99 -0
  282. package/runtime/node_modules/@metro-labs/gmail/src/state.ts +24 -0
  283. package/runtime/node_modules/@metro-labs/gmail/src/station.ts +15 -0
  284. package/runtime/node_modules/@metro-labs/gmail/src/trust.ts +42 -0
  285. package/runtime/node_modules/@metro-labs/http/package.json +30 -0
  286. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
  287. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
  288. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  289. package/runtime/node_modules/@metro-labs/http/src/sigv4.ts +90 -0
  290. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  291. package/runtime/node_modules/@metro-labs/outlook/package.json +25 -0
  292. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +78 -0
  293. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
  294. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +81 -0
  295. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +120 -0
  296. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +42 -0
  297. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +23 -0
  298. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +59 -0
  299. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +55 -0
  300. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +96 -0
  301. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  302. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +129 -0
  303. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +31 -0
  304. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +103 -0
  305. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +84 -0
  306. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +28 -0
  307. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  308. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +41 -0
  309. package/runtime/node_modules/@metro-labs/telegram/package.json +1 -3
  310. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +5 -24
  311. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +8 -6
  312. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +1 -1
  313. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +2 -2
  314. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +19 -34
  315. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +3 -3
  316. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +13 -34
  317. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +4 -43
  318. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +2 -3
  319. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +1 -1
  320. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  321. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +2 -2
  322. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +4 -1
  323. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +0 -1
  324. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +1 -1
  325. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +7 -41
  326. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +22 -77
  327. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +2 -2
  328. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +46 -48
  329. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +15 -15
  330. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +6 -82
  331. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +1 -1
  332. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
  333. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +5 -1
  334. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +20 -12
  335. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  336. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +96 -0
  337. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -0
  338. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  339. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  340. package/runtime/node_modules/@metro-labs/threema/src/files.ts +47 -0
  341. package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -0
  342. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  343. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  344. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +87 -0
  345. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  346. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  347. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  348. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  349. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  350. package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
  351. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
  352. package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
  353. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
  354. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
  355. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
  356. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
  357. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
  358. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
  359. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
  360. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
  361. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
  362. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
  363. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
  364. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
  365. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
  366. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
  367. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  368. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
  369. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
  370. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
  371. package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
  372. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
  373. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
  374. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +35 -220
  375. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +40 -53
  376. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +95 -222
  377. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
  378. package/runtime/node_modules/@metro-labs/xmtp/src/call-leftovers.ts +48 -0
  379. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +95 -3
  380. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +11 -7
  381. package/runtime/node_modules/@metro-labs/xmtp/src/delete-requests.ts +34 -0
  382. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +37 -25
  383. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
  384. package/runtime/node_modules/@metro-labs/xmtp/src/frames.ts +178 -0
  385. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
  386. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
  387. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
  388. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +13 -20
  389. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +57 -33
  390. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
  391. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  392. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  393. package/runtime/node_modules/@metro-labs/xmtp/src/resolve.ts +44 -0
  394. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  395. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  396. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +8 -2
  397. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +22 -58
  398. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +55 -48
  399. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
  400. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
  401. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
  402. package/runtime/runtime.json +1 -1
  403. package/runtime/server.ts +1 -0
  404. package/runtime/stations.json +40 -0
  405. package/dist/prompt.js +0 -61
  406. package/dist/store.js +0 -70
  407. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +0 -195
  408. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +0 -292
  409. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +0 -197
  410. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +0 -5
  411. package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
  412. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
  413. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
  414. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -341
  415. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
  416. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -320
  417. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
  418. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
  419. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
  420. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
  421. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
  422. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
  423. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
  424. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
  425. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
  426. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
  427. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
  428. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
  429. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
  430. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
  431. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
  432. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
  433. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
  434. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
  435. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
  436. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
  437. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
  438. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +0 -71
  439. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
  440. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
  441. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
  442. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
  443. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
  444. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
  445. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
  446. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
  447. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
  448. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
  449. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
  450. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
  451. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
  452. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
  453. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
  454. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
  455. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
  456. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
  457. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
  458. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
  459. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +0 -22
  460. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -1
  461. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +0 -3
  462. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  463. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
  464. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
  465. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
  466. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
  467. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
  468. /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
  469. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
  470. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
  471. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
  472. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
  473. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.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,116 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { randomUUID, type KeyObject } from 'node:crypto';
3
+ import { isRecord } from '@metro-labs/core/is-record';
4
+ import { parseId } from '@metro-labs/core/ids';
5
+ import { log } from '@metro-labs/core/log';
6
+ import { ApiError } from '@metro-labs/http/api-error';
7
+ import { apiSession, cors, readJsonBody, requireAdmin, sendJson } from '@metro-labs/http/api-http';
8
+ import { openTransfer, sealTransfer, transferKeys } from './transfer-crypto.js';
9
+ import type { LocalConnectorRow } from './store.js';
10
+ import type { ConnectorCopyResult } from './transfer-store.js';
11
+
12
+ const PREFIX = '/api/connectors/transfer';
13
+ const MAX_BODY = 4 * 1024 * 1024 + 4096;
14
+ const TTL = 5 * 60_000;
15
+
16
+ export interface ConnectorTransferDeps {
17
+ read: () => LocalConnectorRow[];
18
+ copy: (value: unknown) => ConnectorCopyResult[];
19
+ now?: () => number;
20
+ }
21
+
22
+ interface Ticket {
23
+ privateKey: KeyObject;
24
+ organization: string;
25
+ expiresAt: number;
26
+ }
27
+
28
+ function selectedIds(raw: unknown): string[] | null {
29
+ if (raw === null) return null;
30
+ if (!Array.isArray(raw) || raw.length === 0 || raw.length > 500) throw new ApiError('choose connectors to copy', 400);
31
+ return [...new Set(raw.map((id: unknown) => {
32
+ if (typeof id !== 'string' || parseId(id) === null) throw new ApiError('choose connectors to copy', 400);
33
+ return id;
34
+ }))];
35
+ }
36
+
37
+ export class ConnectorTransfers {
38
+ private readonly tickets = new Map<string, Ticket>();
39
+
40
+ constructor(private readonly deps: ConnectorTransferDeps) {}
41
+
42
+ private prepare(organization: string): unknown {
43
+ const now = (this.deps.now ?? Date.now)();
44
+ for (const [id, ticket] of this.tickets) if (ticket.expiresAt <= now) this.tickets.delete(id);
45
+ if (this.tickets.size >= 64) throw new ApiError('too many pending connector copies, try again later', 429);
46
+ const { publicKey, privateKey } = transferKeys();
47
+ const ticket = randomUUID();
48
+ this.tickets.set(ticket, { privateKey, organization, expiresAt: now + TTL });
49
+ return { ticket, publicKey };
50
+ }
51
+
52
+ private export(body: Record<string, unknown>): unknown {
53
+ if (typeof body.publicKey !== 'string') throw new ApiError('invalid transfer key', 400);
54
+ const ids = selectedIds(body.ids);
55
+ const all = this.deps.read();
56
+ const rows = ids === null ? all : all.filter((row) => ids.includes(row.id));
57
+ const expected = ids?.length ?? rows.length;
58
+ if (rows.length !== expected) throw new ApiError('a selected connector no longer exists', 409);
59
+ if (rows.length > 500) throw new ApiError('copy at most 500 connectors at once', 413);
60
+ try {
61
+ return sealTransfer({ version: 1, connectors: rows }, body.publicKey);
62
+ } catch (err) {
63
+ if (err instanceof ApiError) throw err;
64
+ throw new ApiError('invalid transfer key', 400);
65
+ }
66
+ }
67
+
68
+ private receive(body: Record<string, unknown>, organization: string): unknown {
69
+ const id = typeof body.ticket === 'string' ? body.ticket : '';
70
+ const ticket = this.tickets.get(id);
71
+ if (ticket?.organization !== organization || ticket.expiresAt <= (this.deps.now ?? Date.now)()) {
72
+ this.tickets.delete(id);
73
+ throw new ApiError('this connector copy has expired, start again', 410);
74
+ }
75
+ let value: unknown;
76
+ try {
77
+ value = openTransfer(body.envelope, ticket.privateKey);
78
+ } catch {
79
+ throw new ApiError('invalid encrypted connector copy', 400);
80
+ }
81
+ const results = this.deps.copy(value);
82
+ this.tickets.delete(id);
83
+ return { results };
84
+ }
85
+
86
+ private async answer(req: IncomingMessage, path: string): Promise<unknown> {
87
+ const session = await apiSession(req);
88
+ if (session === null) throw new ApiError('unauthorized', 401);
89
+ requireAdmin(session);
90
+ const body = await readJsonBody(req, MAX_BODY);
91
+ if (!isRecord(body) || body.confirmed !== true) throw new ApiError('confirm copying saved logins and settings', 400);
92
+ if (path === `${PREFIX}/prepare`) return this.prepare(session.subject);
93
+ if (path === `${PREFIX}/export`) return this.export(body);
94
+ return this.receive(body, session.subject);
95
+ }
96
+
97
+ handle(req: IncomingMessage, res: ServerResponse): boolean {
98
+ const path = (req.url ?? '').split('?')[0] ?? '';
99
+ if (![`${PREFIX}/prepare`, `${PREFIX}/export`, `${PREFIX}/receive`].includes(path)) return false;
100
+ if (req.method === 'OPTIONS') {
101
+ res.writeHead(204, cors(req)).end();
102
+ return true;
103
+ }
104
+ if (req.method !== 'POST') {
105
+ sendJson(req, res, 405, { error: 'method not allowed' });
106
+ return true;
107
+ }
108
+ this.answer(req, path).then((body) => {
109
+ sendJson(req, res, 200, body);
110
+ }).catch((err: unknown) => {
111
+ if (!(err instanceof ApiError)) log.warn('connector-transfer: request failed');
112
+ sendJson(req, res, err instanceof ApiError ? err.status : 500, { error: err instanceof ApiError ? err.message : 'connector copy failed' });
113
+ });
114
+ return true;
115
+ }
116
+ }
@@ -0,0 +1,44 @@
1
+ import { createCipheriv, createDecipheriv, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, type KeyObject } from 'node:crypto';
2
+ import { isRecord } from '@metro-labs/core/is-record';
3
+ import { ApiError } from '@metro-labs/http/api-error';
4
+
5
+ export interface TransferEnvelope {
6
+ key: string;
7
+ iv: string;
8
+ tag: string;
9
+ data: string;
10
+ }
11
+
12
+ export const transferKeys = (): { publicKey: string; privateKey: KeyObject } => {
13
+ const pair = generateKeyPairSync('x25519');
14
+ return { publicKey: pair.publicKey.export({ type: 'spki', format: 'pem' }).toString(), privateKey: pair.privateKey };
15
+ };
16
+
17
+ function sharedKey(privateKey: KeyObject, pem: string): ArrayBuffer {
18
+ if (pem.length > 1024) throw new ApiError('invalid transfer key', 400);
19
+ const publicKey = createPublicKey(pem);
20
+ if (publicKey.asymmetricKeyType !== 'x25519') throw new ApiError('invalid transfer key', 400);
21
+ return hkdfSync('sha256', diffieHellman({ privateKey, publicKey }), '', 'metro-connector-copy-v1', 32);
22
+ }
23
+
24
+ export function sealTransfer(value: unknown, publicKey: string): TransferEnvelope {
25
+ const pair = transferKeys();
26
+ const iv = randomBytes(12);
27
+ const cipher = createCipheriv('aes-256-gcm', Buffer.from(sharedKey(pair.privateKey, publicKey)), iv);
28
+ const plain = JSON.stringify(value);
29
+ if (Buffer.byteLength(plain) > 3 * 1024 * 1024) throw new ApiError('too many connector settings for one copy', 413);
30
+ const data = Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()]);
31
+ return { key: pair.publicKey, iv: iv.toString('base64'), tag: cipher.getAuthTag().toString('base64'), data: data.toString('base64') };
32
+ }
33
+
34
+ export function openTransfer(raw: unknown, privateKey: KeyObject): unknown {
35
+ if (!isRecord(raw) || typeof raw.key !== 'string' || typeof raw.iv !== 'string' || typeof raw.tag !== 'string' || typeof raw.data !== 'string')
36
+ throw new ApiError('invalid encrypted transfer', 400);
37
+ const iv = Buffer.from(raw.iv, 'base64');
38
+ const tag = Buffer.from(raw.tag, 'base64');
39
+ if (iv.length !== 12 || tag.length !== 16) throw new ApiError('invalid encrypted transfer', 400);
40
+ const decipher = createDecipheriv('aes-256-gcm', Buffer.from(sharedKey(privateKey, raw.key)), iv);
41
+ decipher.setAuthTag(tag);
42
+ const plain = Buffer.concat([decipher.update(Buffer.from(raw.data, 'base64')), decipher.final()]);
43
+ return JSON.parse(plain.toString('utf8')) as unknown;
44
+ }
@@ -0,0 +1,48 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { isRecord } from '@metro-labs/core/is-record';
3
+ import { newId, parseId } from '@metro-labs/core/ids';
4
+ import { ApiError } from '@metro-labs/http/api-error';
5
+ import { connectorAuth, connectorName, readConfig } from './config.js';
6
+ import { parseConnectorUrl } from './url.js';
7
+ import { normalizePolicy } from '../policy/policy.js';
8
+ import { localImportConnectors, readLocalConnectors, type LocalConnectorRow } from './store.js';
9
+
10
+ export interface ConnectorCopyResult {
11
+ sourceId: string | null;
12
+ id?: string;
13
+ status: 'copied' | 'skipped' | 'invalid';
14
+ }
15
+
16
+ function copyRow(raw: unknown): LocalConnectorRow {
17
+ if (!isRecord(raw) || typeof raw.id !== 'string' || parseId(raw.id) === null || !isRecord(raw.config)) throw new ApiError('invalid connector', 400);
18
+ const name = connectorName(raw.name);
19
+ const url = parseConnectorUrl(raw.url).toString();
20
+ if (raw.config.policy !== undefined) normalizePolicy(raw.config.policy);
21
+ const config = readConfig(raw.config);
22
+ if (!isDeepStrictEqual(config, raw.config)) throw new ApiError('invalid connector settings', 400);
23
+ if (config.auth.kind === 'header') connectorAuth(config.auth.name, config.auth.value);
24
+ return { id: raw.id, name, url, config };
25
+ }
26
+
27
+ export function copyConnectorRows(value: unknown, dir?: string): ConnectorCopyResult[] {
28
+ if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.connectors) || value.connectors.length > 500)
29
+ throw new ApiError('invalid connector transfer', 400);
30
+ const names = new Set(readLocalConnectors(dir).map((row) => row.name));
31
+ const imported: LocalConnectorRow[] = [];
32
+ const results: ConnectorCopyResult[] = value.connectors.map((raw: unknown) => {
33
+ const sourceId = isRecord(raw) && typeof raw.id === 'string' ? parseId(raw.id) : null;
34
+ let row: LocalConnectorRow;
35
+ try {
36
+ row = copyRow(raw);
37
+ } catch {
38
+ return { sourceId, status: 'invalid' };
39
+ }
40
+ if (names.has(row.name)) return { sourceId, status: 'skipped' };
41
+ names.add(row.name);
42
+ const id = newId();
43
+ imported.push({ ...row, id });
44
+ return { sourceId, id, status: 'copied' };
45
+ });
46
+ if (imported.length > 0) localImportConnectors(imported.map((row) => ({ ...row, config: { ...row.config } })), dir, 'append');
47
+ return results;
48
+ }