@desplega.ai/agent-swarm 1.113.0 → 1.115.0

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 (237) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
  3. package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
  4. package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
  5. package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
  6. package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
  7. package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
  8. package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
  9. package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
  10. package/dist/cli-0pygq9r1.js +429 -0
  11. package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
  12. package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
  13. package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
  14. package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
  15. package/dist/cli-52fg7s7x.js +269 -0
  16. package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
  17. package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
  18. package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
  19. package/dist/cli-86fdnerz.js +405 -0
  20. package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
  21. package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
  22. package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
  23. package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
  24. package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
  25. package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
  26. package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
  27. package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
  28. package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
  29. package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
  30. package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
  31. package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
  32. package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
  33. package/dist/cli-jkhwmmgg.js +136 -0
  34. package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
  35. package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
  36. package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
  37. package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
  38. package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
  39. package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
  40. package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
  41. package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
  42. package/dist/cli-t0vqevnm.js +393 -0
  43. package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
  44. package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
  45. package/dist/cli-wh91zby6.js +187222 -0
  46. package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
  47. package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
  48. package/dist/cli.js +32 -15
  49. package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
  50. package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
  51. package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
  52. package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
  53. package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
  54. package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
  55. package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
  56. package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
  57. package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
  58. package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
  59. package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
  60. package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
  61. package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
  62. package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
  63. package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
  64. package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
  65. package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
  66. package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
  67. package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
  68. package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
  69. package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
  70. package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
  71. package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
  72. package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
  73. package/dist/rbac-roles-kfesjy9y.js +28 -0
  74. package/dist/rbac-roles-mwcwhbxc.js +283 -0
  75. package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
  76. package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
  77. package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
  78. package/openapi.json +1327 -20
  79. package/package.json +3 -3
  80. package/src/agentmail/handlers.ts +16 -6
  81. package/src/agentmail/templates.ts +25 -5
  82. package/src/be/audit-user.ts +28 -4
  83. package/src/be/db-queries/oauth.ts +42 -0
  84. package/src/be/db.ts +314 -28
  85. package/src/be/identity.ts +79 -0
  86. package/src/be/mcp-proxy.ts +132 -0
  87. package/src/be/migrations/109_rbac_roles.sql +79 -0
  88. package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
  89. package/src/be/migrations/112_script_connections_graphql.sql +96 -0
  90. package/src/be/migrations/113_task_routing_affinity.sql +9 -0
  91. package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
  92. package/src/be/modelsdev-cache.json +135 -0
  93. package/src/be/oauth-credential-bindings.ts +35 -0
  94. package/src/be/oauth-refresh-sweep.ts +159 -0
  95. package/src/be/rbac-audit.ts +52 -13
  96. package/src/be/rbac-roles.ts +424 -0
  97. package/src/be/script-connections.ts +459 -40
  98. package/src/be/script-credential-broker.ts +29 -3
  99. package/src/be/scripts/typecheck.ts +51 -7
  100. package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
  101. package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
  102. package/src/be/users.ts +24 -0
  103. package/src/cli.tsx +17 -0
  104. package/src/github/handlers.ts +10 -9
  105. package/src/github/templates.ts +45 -9
  106. package/src/gitlab/handlers.ts +20 -8
  107. package/src/gitlab/templates.ts +18 -6
  108. package/src/heartbeat/heartbeat.ts +143 -21
  109. package/src/http/all-routes.ts +3 -0
  110. package/src/http/approval-requests.ts +12 -0
  111. package/src/http/config.ts +44 -10
  112. package/src/http/core.ts +30 -3
  113. package/src/http/favorites.ts +1 -5
  114. package/src/http/index.ts +25 -1
  115. package/src/http/mcp-oauth.ts +2 -0
  116. package/src/http/mcp-servers.ts +44 -3
  117. package/src/http/oauth-generic.ts +105 -0
  118. package/src/http/poll.ts +20 -3
  119. package/src/http/script-connection-proxy.ts +116 -0
  120. package/src/http/script-connections.ts +1657 -0
  121. package/src/http/scripts.ts +27 -7
  122. package/src/http/skills.ts +11 -0
  123. package/src/http/tasks.ts +27 -13
  124. package/src/http/x.ts +6 -2
  125. package/src/integrations/kapso/inbound.ts +23 -6
  126. package/src/jira/sync.ts +111 -17
  127. package/src/jira/templates.ts +10 -2
  128. package/src/mcp-client/http-client.ts +194 -0
  129. package/src/oauth/app-validation.ts +16 -0
  130. package/src/oauth/ensure-token.ts +69 -14
  131. package/src/oauth/mcp-wrapper.ts +15 -0
  132. package/src/oauth/wrapper.ts +48 -20
  133. package/src/prompts/session-templates.ts +3 -3
  134. package/src/providers/codex-models.ts +100 -30
  135. package/src/providers/modelsdev-reasoning.json +51 -0
  136. package/src/providers/pi-mono-mcp-client.ts +2 -133
  137. package/src/providers/reasoning-effort.ts +11 -5
  138. package/src/rbac/admission.ts +68 -0
  139. package/src/rbac/index.ts +2 -0
  140. package/src/rbac/legacy-policy.ts +12 -0
  141. package/src/rbac/permissions.ts +48 -0
  142. package/src/scheduler/scheduler.ts +9 -1
  143. package/src/scripts-runtime/api-client.ts +151 -19
  144. package/src/scripts-runtime/api-types.ts +38 -6
  145. package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
  146. package/src/scripts-runtime/credential-broker/types.ts +8 -0
  147. package/src/scripts-runtime/ctx.ts +11 -1
  148. package/src/scripts-runtime/eval-harness.ts +5 -1
  149. package/src/scripts-runtime/executors/types.ts +2 -1
  150. package/src/scripts-runtime/loader.ts +3 -1
  151. package/src/scripts-runtime/mcp-client.ts +87 -0
  152. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  153. package/src/scripts-runtime/types/stdlib.d.ts +6 -0
  154. package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
  155. package/src/server-user.ts +114 -59
  156. package/src/server.ts +12 -0
  157. package/src/slack/assistant.ts +14 -6
  158. package/src/slack/enrich.ts +17 -0
  159. package/src/slack/handlers.ts +8 -22
  160. package/src/slack/responses.ts +91 -1
  161. package/src/slack/thread-buffer.ts +6 -3
  162. package/src/slack/watcher.ts +9 -6
  163. package/src/tasks/worker-follow-up.ts +100 -3
  164. package/src/tests/agentmail-handlers.test.ts +32 -3
  165. package/src/tests/approval-requests.test.ts +61 -0
  166. package/src/tests/audit-user.test.ts +47 -1
  167. package/src/tests/codex-adapter.test.ts +46 -23
  168. package/src/tests/credential-broker.test.ts +5 -2
  169. package/src/tests/delete-page-tool.test.ts +197 -0
  170. package/src/tests/github-handlers.test.ts +28 -2
  171. package/src/tests/gitlab-handlers.test.ts +125 -0
  172. package/src/tests/heartbeat.test.ts +48 -3
  173. package/src/tests/identity.test.ts +117 -0
  174. package/src/tests/jira-sync.test.ts +99 -1
  175. package/src/tests/kapso-inbound.test.ts +7 -0
  176. package/src/tests/mcp-tools-user.test.ts +60 -6
  177. package/src/tests/model-control.test.ts +8 -2
  178. package/src/tests/oauth-credential-bindings.test.ts +490 -0
  179. package/src/tests/oauth-refresh-sweep.test.ts +160 -0
  180. package/src/tests/pool-affinity.test.ts +401 -0
  181. package/src/tests/prompt-template-remaining.test.ts +2 -2
  182. package/src/tests/rbac-admission-e2e.test.ts +386 -0
  183. package/src/tests/rbac-admission.test.ts +492 -0
  184. package/src/tests/rbac-e2e-helpers.ts +3 -0
  185. package/src/tests/rbac-engine.test.ts +50 -0
  186. package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
  187. package/src/tests/rbac-roles.test.ts +328 -0
  188. package/src/tests/reasoning-effort.test.ts +15 -2
  189. package/src/tests/schedule-target-type.test.ts +50 -0
  190. package/src/tests/scheduled-tasks.test.ts +40 -0
  191. package/src/tests/script-apis-mcp.test.ts +37 -13
  192. package/src/tests/script-connections-http.test.ts +1158 -0
  193. package/src/tests/script-connections-mcp.test.ts +894 -0
  194. package/src/tests/script-connections.test.ts +754 -6
  195. package/src/tests/scripts-external-api.test.ts +49 -1
  196. package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
  197. package/src/tests/scripts-runtime.test.ts +291 -0
  198. package/src/tests/seed-scripts.test.ts +36 -0
  199. package/src/tests/slack-identity-resolution.test.ts +37 -3
  200. package/src/tests/slack-inline-output.test.ts +65 -0
  201. package/src/tests/slack-thread-buffer.test.ts +28 -0
  202. package/src/tests/tool-annotations.test.ts +1 -0
  203. package/src/tests/user-token-rest-auth.test.ts +140 -1
  204. package/src/tests/workflow-swarm-script.test.ts +44 -0
  205. package/src/tests/workflow-triggers-v2.test.ts +300 -1
  206. package/src/tools/credential-bindings/tool.ts +277 -18
  207. package/src/tools/delete-page.ts +145 -0
  208. package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
  209. package/src/tools/request-human-input.ts +2 -0
  210. package/src/tools/resolve-user.ts +77 -27
  211. package/src/tools/script-apis.ts +62 -3
  212. package/src/tools/script-connections/tool.ts +275 -31
  213. package/src/tools/send-task.ts +15 -1
  214. package/src/tools/task-action.ts +34 -2
  215. package/src/tools/templates.ts +53 -1
  216. package/src/tools/tool-config.ts +2 -1
  217. package/src/tools/utils.ts +2 -0
  218. package/src/tools/workflows/create-workflow.ts +8 -1
  219. package/src/tools/workflows/update-workflow.ts +11 -2
  220. package/src/types.ts +90 -11
  221. package/src/utils/scoped-resource.ts +25 -0
  222. package/src/workflows/executors/human-in-the-loop.ts +1 -0
  223. package/src/workflows/executors/swarm-script.ts +7 -1
  224. package/src/workflows/triggers.ts +146 -18
  225. package/templates/skills/swarm-scripts/content.md +33 -8
  226. package/dist/cli-bgef578c.js +0 -771
  227. package/dist/cli-c9mtm09b.js +0 -43
  228. package/dist/cli-ceewqajw.js +0 -1698
  229. package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
  230. package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
  231. package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
  232. package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
  233. package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
  234. package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
  235. package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
  236. package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
  237. package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
@@ -1,19 +1,31 @@
1
+ import {
2
+ McpHttpClient
3
+ } from "./cli-jkhwmmgg.js";
1
4
  import {
2
5
  embedScript
3
- } from "./cli-30bbaveh.js";
6
+ } from "./cli-cmw9rzy5.js";
7
+ import {
8
+ calculatePKCECodeChallenge,
9
+ generateRandomCodeVerifier,
10
+ generateRandomState
11
+ } from "./cli-0pygq9r1.js";
4
12
  import {
5
13
  computeContentHash,
6
14
  decryptSecret,
7
15
  encryptSecret,
8
16
  getDb,
9
17
  getEncryptionKey,
18
+ getMcpServerById,
19
+ getResolvedConfig,
10
20
  getSwarmConfigs,
11
21
  init_crypto,
12
- init_db
13
- } from "./cli-ncfgsqbd.js";
22
+ init_date_utils,
23
+ init_db,
24
+ normalizeDateRequired
25
+ } from "./cli-15y7qhzp.js";
14
26
  import {
15
27
  init_zod
16
- } from "./cli-q21d49ac.js";
28
+ } from "./cli-bw80ck94.js";
17
29
  import {
18
30
  _enum,
19
31
  array,
@@ -21,7 +33,7 @@ import {
21
33
  object,
22
34
  string,
23
35
  union
24
- } from "./cli-anrj584m.js";
36
+ } from "./cli-xz9aq0rf.js";
25
37
  import {
26
38
  init_secret_scrubber,
27
39
  registerVolatileSecret
@@ -168753,337 +168765,278 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
168753
168765
  } });
168754
168766
  });
168755
168767
 
168756
- // src/be/scripts/db.ts
168757
- init_secret_scrubber();
168768
+ // src/be/script-connections.ts
168769
+ init_db();
168758
168770
 
168759
- // src/utils/short-id.ts
168760
- import { randomBytes } from "node:crypto";
168761
- var LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
168762
- var BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
168763
- function generateShortId(len = 12) {
168764
- let out = "";
168765
- while (out.length < len) {
168766
- for (const byte of randomBytes((len - out.length) * 2)) {
168767
- if (byte < 208) {
168768
- out += LETTERS[byte % 52];
168769
- if (out.length === len)
168770
- break;
168771
- }
168772
- }
168771
+ // src/oauth/mcp-wrapper.ts
168772
+ var PRIVATE_IPV4_BLOCKS = [
168773
+ { prefix: "10.", mask: 8 },
168774
+ { prefix: "127.", mask: 8 },
168775
+ { prefix: "169.254.", mask: 16 },
168776
+ { prefix: "172.16.", mask: 12 },
168777
+ { prefix: "192.168.", mask: 16 },
168778
+ { prefix: "0.", mask: 8 }
168779
+ ];
168780
+ function isPrivateIPv4(host) {
168781
+ if (host === "127.0.0.1")
168782
+ return true;
168783
+ if (host.startsWith("169.254."))
168784
+ return true;
168785
+ if (host.startsWith("10."))
168786
+ return true;
168787
+ if (host.startsWith("192.168."))
168788
+ return true;
168789
+ if (host.startsWith("0."))
168790
+ return true;
168791
+ if (host.startsWith("172.")) {
168792
+ const parts = host.split(".");
168793
+ const second = parseInt(parts[1] ?? "", 10);
168794
+ if (Number.isFinite(second) && second >= 16 && second <= 31)
168795
+ return true;
168773
168796
  }
168774
- return out;
168775
- }
168776
- function base62(bytes) {
168777
- let out = "";
168778
- for (const byte of bytes)
168779
- out += BASE62[byte % 62];
168780
- return out;
168797
+ for (const block of PRIVATE_IPV4_BLOCKS) {
168798
+ if (block.prefix !== "172." && host.startsWith(block.prefix))
168799
+ return true;
168800
+ }
168801
+ return false;
168781
168802
  }
168782
- function generateBearerToken() {
168783
- return `xsk_${base62(randomBytes(24))}`;
168803
+ function isPrivateIPv6(host) {
168804
+ const lower = host.toLowerCase();
168805
+ if (lower === "::1" || lower === "[::1]")
168806
+ return true;
168807
+ if (lower.startsWith("fe80:") || lower.startsWith("[fe80:"))
168808
+ return true;
168809
+ if (lower.startsWith("fc") || lower.startsWith("fd"))
168810
+ return true;
168811
+ if (lower.startsWith("[fc") || lower.startsWith("[fd"))
168812
+ return true;
168813
+ return false;
168784
168814
  }
168785
-
168786
- // src/be/scripts/db.ts
168787
- init_crypto();
168788
- init_db();
168789
- function normalizeScopeId(scope, scopeId) {
168790
- if (scope === "global")
168791
- return null;
168792
- if (!scopeId) {
168793
- throw new Error("scopeId is required for agent-scoped scripts");
168815
+ function assertUrlSafe(rawUrl, opts = {}) {
168816
+ let parsed;
168817
+ try {
168818
+ parsed = new URL(rawUrl);
168819
+ } catch {
168820
+ throw new Error(`Invalid URL: ${rawUrl}`);
168794
168821
  }
168795
- return scopeId;
168822
+ const allowPrivate = opts.allowPrivateHosts === true;
168823
+ const allowInsecure = opts.allowInsecure === true;
168824
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
168825
+ throw new Error(`Refusing unsupported protocol: ${parsed.protocol}`);
168826
+ }
168827
+ if (parsed.protocol === "http:" && !allowInsecure) {
168828
+ throw new Error(`Refusing insecure (http://) URL in production: ${rawUrl}`);
168829
+ }
168830
+ const host = parsed.hostname;
168831
+ if (!host) {
168832
+ throw new Error(`Missing hostname: ${rawUrl}`);
168833
+ }
168834
+ if (host === "localhost" && !allowPrivate) {
168835
+ throw new Error(`Refusing loopback hostname: ${host}`);
168836
+ }
168837
+ if (isPrivateIPv4(host) && !allowPrivate) {
168838
+ throw new Error(`Refusing private IPv4 host: ${host}`);
168839
+ }
168840
+ if (isPrivateIPv6(host) && !allowPrivate) {
168841
+ throw new Error(`Refusing private IPv6 host: ${host}`);
168842
+ }
168843
+ return parsed;
168796
168844
  }
168797
- function rowToScript(row) {
168845
+ function publicEndpointSsrfOptions() {
168846
+ const env = "development";
168847
+ const dev = env === "development" || env === "test" || process.env.ALLOW_PRIVATE_NETWORK_URLS === "true";
168798
168848
  return {
168799
- ...row,
168800
- scopeId: row.scopeId ?? null,
168801
- isScratch: row.isScratch === 1,
168802
- typeChecked: row.typeChecked === 1,
168803
- createdByAgentId: row.createdByAgentId ?? null
168849
+ allowPrivateHosts: dev,
168850
+ allowInsecure: dev
168804
168851
  };
168805
168852
  }
168806
- function rowToScriptVersion(row) {
168853
+ function defaultSsrfOptions() {
168807
168854
  return {
168808
- ...row,
168809
- changedByAgentId: row.changedByAgentId ?? null,
168810
- changeReason: row.changeReason ?? null
168855
+ allowPrivateHosts: process.env.MCP_OAUTH_ALLOW_PRIVATE_HOSTS === "true",
168856
+ allowInsecure: true
168811
168857
  };
168812
168858
  }
168813
- function insertScriptVersion(args) {
168814
- getDb().prepare(`INSERT INTO script_versions (
168815
- id, scriptId, version, source, description, intent, signatureJson,
168816
- contentHash, changedByAgentId, changedAt, changeReason
168817
- )
168818
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(crypto.randomUUID(), args.scriptId, args.version, args.source, args.description, args.intent, args.signatureJson, args.contentHash, args.changedByAgentId ?? null, new Date().toISOString(), args.changeReason ?? null);
168859
+ async function safeFetch(url, init) {
168860
+ assertUrlSafe(url, defaultSsrfOptions());
168861
+ return fetch(url, init);
168819
168862
  }
168820
- function insertScript(args) {
168821
- const now = new Date().toISOString();
168822
- const id = crypto.randomUUID();
168823
- const scopeId = normalizeScopeId(args.scope, args.scopeId);
168824
- const contentHash = computeContentHash(args.source);
168825
- const fsMode = args.fsMode ?? "none";
168826
- const isScratch = args.isScratch ? 1 : 0;
168827
- const typeChecked = args.typeChecked ? 1 : 0;
168828
- const txn = getDb().transaction(() => {
168829
- const row = getDb().prepare(`INSERT INTO scripts (
168830
- id, name, scope, scopeId, source, description, intent, signatureJson,
168831
- argsJsonSchema, contentHash, isScratch, typeChecked, fsMode, createdByAgentId, createdAt, updatedAt,
168832
- created_by, updated_by
168833
- )
168834
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
168835
- RETURNING *`).get(id, args.name, args.scope, scopeId, args.source, args.description, args.intent, args.signatureJson, args.argsJsonSchema ?? null, contentHash, isScratch, typeChecked, fsMode, args.agentId ?? null, now, now, args.createdBy ?? null, args.createdBy ?? null);
168836
- if (!row)
168837
- throw new Error("Failed to insert script");
168838
- insertScriptVersion({
168839
- scriptId: row.id,
168840
- version: row.version,
168841
- source: row.source,
168842
- description: row.description,
168843
- intent: row.intent,
168844
- signatureJson: row.signatureJson,
168845
- contentHash: row.contentHash,
168846
- changedByAgentId: args.agentId ?? null,
168847
- changeReason: args.changeReason ?? "Initial creation"
168863
+ async function discoverProtectedResourceMetadata(resourceUrl) {
168864
+ const base = new URL(resourceUrl);
168865
+ const wellKnown = new URL("/.well-known/oauth-protected-resource", base).toString();
168866
+ try {
168867
+ const res = await safeFetch(wellKnown, {
168868
+ headers: { Accept: "application/json" }
168848
168869
  });
168849
- return rowToScript(row);
168850
- });
168851
- return txn();
168852
- }
168853
- async function upsertScriptByName(args) {
168854
- const shouldEmbed = args.embeddingMode !== "skip";
168855
- const existing = getScript(args);
168856
- if (!existing) {
168857
- const script2 = insertScript(args);
168858
- if (!script2.isScratch && shouldEmbed) {
168859
- await embedScript(script2);
168870
+ if (res.ok) {
168871
+ return await res.json();
168860
168872
  }
168861
- return {
168862
- script: script2,
168863
- isNew: true,
168864
- contentDeduped: false
168865
- };
168866
- }
168867
- const contentHash = computeContentHash(args.source);
168868
- if (existing.contentHash === contentHash) {
168869
- const fsMode2 = args.fsMode ?? existing.fsMode;
168870
- const isScratch2 = args.isScratch ?? existing.isScratch;
168871
- const typeChecked2 = args.typeChecked ?? existing.typeChecked;
168872
- const argsJsonSchema2 = args.argsJsonSchema !== undefined ? args.argsJsonSchema : existing.argsJsonSchema;
168873
- const trackedMetadataChanged = args.description !== existing.description || args.intent !== existing.intent || args.signatureJson !== existing.signatureJson || argsJsonSchema2 !== existing.argsJsonSchema;
168874
- const promotedFromScratch = existing.isScratch && !isScratch2;
168875
- if (fsMode2 !== existing.fsMode || isScratch2 !== existing.isScratch || typeChecked2 !== existing.typeChecked || trackedMetadataChanged) {
168876
- const row = getDb().prepare(`UPDATE scripts
168877
- SET description = ?, intent = ?, signatureJson = ?, argsJsonSchema = ?,
168878
- isScratch = ?, typeChecked = ?, fsMode = ?, updatedAt = ?, updated_by = ?
168879
- WHERE id = ?
168880
- RETURNING *`).get(args.description, args.intent, args.signatureJson, argsJsonSchema2 ?? null, isScratch2 ? 1 : 0, typeChecked2 ? 1 : 0, fsMode2, new Date().toISOString(), args.createdBy ?? null, existing.id);
168881
- if (!row)
168882
- throw new Error("Failed to update script metadata");
168883
- const script2 = rowToScript(row);
168884
- if (!script2.isScratch && shouldEmbed && (trackedMetadataChanged || promotedFromScratch)) {
168885
- await embedScript(script2);
168873
+ } catch {}
168874
+ try {
168875
+ const probe = await safeFetch(resourceUrl, { method: "HEAD" });
168876
+ const wwwAuth = probe.headers.get("www-authenticate");
168877
+ if (wwwAuth) {
168878
+ const match = /resource_metadata="([^"]+)"/i.exec(wwwAuth);
168879
+ if (match) {
168880
+ const metaRes = await safeFetch(match[1], {
168881
+ headers: { Accept: "application/json" }
168882
+ });
168883
+ if (metaRes.ok) {
168884
+ return await metaRes.json();
168885
+ }
168886
168886
  }
168887
- return {
168888
- script: script2,
168889
- isNew: false,
168890
- contentDeduped: true
168891
- };
168892
168887
  }
168893
- return {
168894
- script: existing,
168895
- isNew: false,
168896
- contentDeduped: true
168897
- };
168898
- }
168899
- const now = new Date().toISOString();
168900
- const newVersion = existing.version + 1;
168901
- const fsMode = args.fsMode ?? existing.fsMode;
168902
- const isScratch = args.isScratch ?? existing.isScratch;
168903
- const typeChecked = args.typeChecked ?? existing.typeChecked;
168904
- const argsJsonSchema = args.argsJsonSchema !== undefined ? args.argsJsonSchema : existing.argsJsonSchema;
168905
- const txn = getDb().transaction(() => {
168906
- const row = getDb().prepare(`UPDATE scripts
168907
- SET source = ?, description = ?, intent = ?, signatureJson = ?, argsJsonSchema = ?,
168908
- contentHash = ?, version = ?, isScratch = ?, typeChecked = ?, fsMode = ?, updatedAt = ?, updated_by = ?
168909
- WHERE id = ?
168910
- RETURNING *`).get(args.source, args.description, args.intent, args.signatureJson, argsJsonSchema ?? null, contentHash, newVersion, isScratch ? 1 : 0, typeChecked ? 1 : 0, fsMode, now, args.createdBy ?? null, existing.id);
168911
- if (!row)
168912
- throw new Error("Failed to update script");
168913
- insertScriptVersion({
168914
- scriptId: row.id,
168915
- version: row.version,
168916
- source: row.source,
168917
- description: row.description,
168918
- intent: row.intent,
168919
- signatureJson: row.signatureJson,
168920
- contentHash: row.contentHash,
168921
- changedByAgentId: args.agentId ?? null,
168922
- changeReason: args.changeReason ?? null
168923
- });
168924
- return rowToScript(row);
168925
- });
168926
- const script = txn();
168927
- if (!script.isScratch && shouldEmbed) {
168928
- await embedScript(script);
168929
- }
168930
- return {
168931
- script,
168932
- isNew: false,
168933
- contentDeduped: false
168934
- };
168935
- }
168936
- function getScript(args) {
168937
- const scopeId = normalizeScopeId(args.scope, args.scopeId);
168938
- const row = scopeId === null ? getDb().prepare("SELECT * FROM scripts WHERE name = ? AND scope = ? AND scopeId IS NULL").get(args.name, args.scope) : getDb().prepare("SELECT * FROM scripts WHERE name = ? AND scope = ? AND scopeId = ?").get(args.name, args.scope, scopeId);
168939
- return row ? rowToScript(row) : null;
168940
- }
168941
- function getScriptById(id) {
168942
- const row = getDb().prepare("SELECT * FROM scripts WHERE id = ?").get(id);
168943
- return row ? rowToScript(row) : null;
168944
- }
168945
- function getScriptVersion(args) {
168946
- if (args.version === undefined && args.contentHash === undefined) {
168947
- throw new Error("version or contentHash is required");
168948
- }
168949
- const row = args.version !== undefined ? getDb().prepare("SELECT * FROM script_versions WHERE scriptId = ? AND version = ?").get(args.scriptId, args.version) : getDb().prepare("SELECT * FROM script_versions WHERE scriptId = ? AND contentHash = ? ORDER BY version DESC LIMIT 1").get(args.scriptId, args.contentHash);
168950
- return row ? rowToScriptVersion(row) : null;
168888
+ } catch {}
168889
+ return null;
168951
168890
  }
168952
- function listScripts(args) {
168953
- const conditions = [];
168954
- const params = [];
168955
- if (args?.scope) {
168956
- conditions.push("scope = ?");
168957
- params.push(args.scope);
168958
- if (args.scope === "global") {
168959
- conditions.push("scopeId IS NULL");
168960
- } else if (args.scopeId !== undefined) {
168961
- conditions.push("scopeId = ?");
168962
- params.push(normalizeScopeId(args.scope, args.scopeId));
168891
+ async function discoverAuthorizationServerMetadata(issuer) {
168892
+ const issuerUrl = new URL(issuer);
168893
+ const candidates = [
168894
+ new URL("/.well-known/oauth-authorization-server", issuerUrl).toString(),
168895
+ new URL("/.well-known/openid-configuration", issuerUrl).toString()
168896
+ ];
168897
+ let lastError = null;
168898
+ for (const candidate of candidates) {
168899
+ try {
168900
+ const res = await safeFetch(candidate, {
168901
+ headers: { Accept: "application/json" }
168902
+ });
168903
+ if (res.ok) {
168904
+ return await res.json();
168905
+ }
168906
+ lastError = new Error(`Metadata fetch ${candidate} → ${res.status}`);
168907
+ } catch (err) {
168908
+ lastError = err instanceof Error ? err : new Error(String(err));
168963
168909
  }
168964
- } else if (args?.scopeId !== undefined) {
168965
- conditions.push("scopeId = ?");
168966
- params.push(args.scopeId ?? "");
168967
- }
168968
- if (!args?.includeScratch) {
168969
- conditions.push("isScratch = 0");
168970
168910
  }
168971
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
168972
- return getDb().prepare(`SELECT * FROM scripts ${whereClause} ORDER BY scope ASC, scopeId ASC, name ASC`).all(...params).map(rowToScript);
168973
- }
168974
- function listScriptVersions(scriptId) {
168975
- return getDb().prepare("SELECT * FROM script_versions WHERE scriptId = ? ORDER BY version DESC").all(scriptId).map(rowToScriptVersion);
168976
- }
168977
- function deleteScript(args) {
168978
- const existing = getScript(args);
168979
- if (!existing)
168980
- return false;
168981
- const result = getDb().run("DELETE FROM scripts WHERE id = ?", [existing.id]);
168982
- return result.changes > 0;
168983
- }
168984
- var SCRIPT_API_ID_LEN = 12;
168985
- function rowToScriptApi(row) {
168986
- return {
168987
- id: row.id,
168988
- scriptId: row.scriptId,
168989
- agentId: row.agentId,
168990
- authMode: row.authMode,
168991
- enabled: row.enabled === 1,
168992
- label: row.label ?? null,
168993
- callCount: row.callCount,
168994
- lastUsedAt: row.lastUsedAt ?? null,
168995
- createdAt: row.createdAt
168996
- };
168911
+ throw lastError ?? new Error(`Authorization server metadata not found at ${issuer}`);
168997
168912
  }
168998
- function isUniqueConstraintError(err) {
168999
- return err instanceof Error && /UNIQUE constraint failed/i.test(err.message);
168913
+ async function registerClient(registrationEndpoint, req) {
168914
+ const res = await safeFetch(registrationEndpoint, {
168915
+ method: "POST",
168916
+ headers: {
168917
+ "Content-Type": "application/json",
168918
+ Accept: "application/json"
168919
+ },
168920
+ body: JSON.stringify(req)
168921
+ });
168922
+ if (!res.ok) {
168923
+ const body = await res.text().catch(() => "");
168924
+ throw new Error(`Dynamic client registration failed (${res.status}): ${body}`);
168925
+ }
168926
+ return await res.json();
169000
168927
  }
169001
- function createScriptApi(args) {
169002
- const now = new Date().toISOString();
169003
- const token = args.authMode === "bearer" ? generateBearerToken() : null;
169004
- const encrypted = token ? encryptSecret(token, getEncryptionKey()) : null;
169005
- const createdBy = args.createdBy ?? null;
169006
- for (let attempt = 0;attempt < 5; attempt++) {
169007
- const id = generateShortId(SCRIPT_API_ID_LEN);
169008
- try {
169009
- const row = getDb().prepare(`INSERT INTO script_apis
169010
- (id, scriptId, agentId, authMode, bearerTokenEncrypted, enabled, label,
169011
- callCount, lastUsedAt, createdAt, created_by, updated_by)
169012
- VALUES (?, ?, ?, ?, ?, 1, ?, 0, NULL, ?, ?, ?)
169013
- RETURNING *`).get(id, args.scriptId, args.agentId, args.authMode, encrypted, args.label ?? null, now, createdBy, createdBy);
169014
- if (!row)
169015
- throw new Error("Failed to create script API endpoint");
169016
- if (token)
169017
- registerVolatileSecret(token, `script-api:${id}`);
169018
- return { ...rowToScriptApi(row), token };
169019
- } catch (err) {
169020
- if (isUniqueConstraintError(err) && attempt < 4)
168928
+ async function buildAuthorizeUrl(input) {
168929
+ const state = input.state ?? generateRandomState();
168930
+ const codeVerifier = generateRandomCodeVerifier();
168931
+ const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
168932
+ const url = new URL(input.authorizeUrl);
168933
+ url.searchParams.set("response_type", "code");
168934
+ url.searchParams.set("client_id", input.clientId);
168935
+ url.searchParams.set("redirect_uri", input.redirectUri);
168936
+ if (input.scopes.length > 0) {
168937
+ url.searchParams.set("scope", input.scopes.join(" "));
168938
+ }
168939
+ url.searchParams.set("state", state);
168940
+ url.searchParams.set("code_challenge", codeChallenge);
168941
+ url.searchParams.set("code_challenge_method", "S256");
168942
+ url.searchParams.set("resource", input.resource);
168943
+ if (input.extraParams) {
168944
+ const RESERVED = new Set([
168945
+ "response_type",
168946
+ "client_id",
168947
+ "redirect_uri",
168948
+ "scope",
168949
+ "state",
168950
+ "code_challenge",
168951
+ "code_challenge_method",
168952
+ "resource"
168953
+ ]);
168954
+ for (const [k, v] of Object.entries(input.extraParams)) {
168955
+ if (RESERVED.has(k.toLowerCase())) {
168956
+ console.warn(`[mcp-oauth] extraParams key "${k}" is reserved and skipped`);
169021
168957
  continue;
169022
- throw err;
168958
+ }
168959
+ url.searchParams.set(k, v);
169023
168960
  }
169024
168961
  }
169025
- throw new Error("Failed to allocate a unique script API endpoint id");
169026
- }
169027
- function listScriptApisForScript(scriptId) {
169028
- return getDb().prepare("SELECT * FROM script_apis WHERE scriptId = ? ORDER BY createdAt DESC").all(scriptId).map(rowToScriptApi);
169029
- }
169030
- function getScriptApiById(id) {
169031
- const row = getDb().prepare("SELECT * FROM script_apis WHERE id = ?").get(id);
169032
- if (!row)
169033
- return null;
169034
- return { ...rowToScriptApi(row), bearerTokenEncrypted: row.bearerTokenEncrypted ?? null };
168962
+ return { url: url.toString(), state, codeVerifier, codeChallenge };
169035
168963
  }
169036
- function getScriptApiSecret(id) {
169037
- const row = getDb().prepare("SELECT bearerTokenEncrypted FROM script_apis WHERE id = ?").get(id);
169038
- if (!row?.bearerTokenEncrypted)
169039
- return null;
169040
- const token = decryptSecret(row.bearerTokenEncrypted, getEncryptionKey());
169041
- registerVolatileSecret(token, `script-api:${id}`);
169042
- return token;
169043
- }
169044
- function updateScriptApi(id, args) {
169045
- const sets = [];
169046
- const vals = [];
169047
- if (args.enabled !== undefined) {
169048
- sets.push("enabled = ?");
169049
- vals.push(args.enabled ? 1 : 0);
169050
- }
169051
- if (args.label !== undefined) {
169052
- sets.push("label = ?");
169053
- vals.push(args.label);
168964
+ async function exchangeCodeForTokens(input) {
168965
+ const body = new URLSearchParams({
168966
+ grant_type: "authorization_code",
168967
+ code: input.code,
168968
+ redirect_uri: input.redirectUri,
168969
+ client_id: input.clientId,
168970
+ code_verifier: input.codeVerifier,
168971
+ resource: input.resource
168972
+ });
168973
+ if (input.clientSecret)
168974
+ body.set("client_secret", input.clientSecret);
168975
+ const res = await safeFetch(input.tokenUrl, {
168976
+ method: "POST",
168977
+ headers: {
168978
+ "Content-Type": "application/x-www-form-urlencoded",
168979
+ Accept: "application/json"
168980
+ },
168981
+ body: body.toString()
168982
+ });
168983
+ if (!res.ok) {
168984
+ const text = await res.text().catch(() => "");
168985
+ throw new Error(`Token exchange failed (${res.status}): ${text}`);
169054
168986
  }
169055
- if (args.updatedBy !== undefined) {
169056
- sets.push("updated_by = ?");
169057
- vals.push(args.updatedBy);
168987
+ return await res.json();
168988
+ }
168989
+ async function refreshMcpToken(input) {
168990
+ const body = new URLSearchParams({
168991
+ grant_type: "refresh_token",
168992
+ refresh_token: input.refreshToken,
168993
+ client_id: input.clientId,
168994
+ resource: input.resource
168995
+ });
168996
+ if (input.clientSecret)
168997
+ body.set("client_secret", input.clientSecret);
168998
+ if (input.scopes && input.scopes.length > 0)
168999
+ body.set("scope", input.scopes.join(" "));
169000
+ const res = await safeFetch(input.tokenUrl, {
169001
+ method: "POST",
169002
+ headers: {
169003
+ "Content-Type": "application/x-www-form-urlencoded",
169004
+ Accept: "application/json"
169005
+ },
169006
+ body: body.toString()
169007
+ });
169008
+ if (!res.ok) {
169009
+ const text = await res.text().catch(() => "");
169010
+ throw new Error(`Token refresh failed (${res.status}): ${text}`);
169058
169011
  }
169059
- if (sets.length === 0) {
169060
- const row2 = getDb().prepare("SELECT * FROM script_apis WHERE id = ?").get(id);
169061
- return row2 ? rowToScriptApi(row2) : null;
169012
+ return await res.json();
169013
+ }
169014
+ async function revokeMcpToken(input) {
169015
+ const body = new URLSearchParams({
169016
+ token: input.token,
169017
+ client_id: input.clientId
169018
+ });
169019
+ if (input.tokenTypeHint)
169020
+ body.set("token_type_hint", input.tokenTypeHint);
169021
+ if (input.clientSecret)
169022
+ body.set("client_secret", input.clientSecret);
169023
+ const res = await safeFetch(input.revocationUrl, {
169024
+ method: "POST",
169025
+ headers: {
169026
+ "Content-Type": "application/x-www-form-urlencoded",
169027
+ Accept: "application/json"
169028
+ },
169029
+ body: body.toString()
169030
+ });
169031
+ if (!res.ok && res.status !== 200 && res.status !== 204) {
169032
+ const text = await res.text().catch(() => "");
169033
+ throw new Error(`Token revocation failed (${res.status}): ${text}`);
169062
169034
  }
169063
- vals.push(id);
169064
- const row = getDb().prepare(`UPDATE script_apis SET ${sets.join(", ")} WHERE id = ? RETURNING *`).get(...vals);
169065
- return row ? rowToScriptApi(row) : null;
169066
169035
  }
169067
- function rotateScriptApiSecret(id, updatedBy) {
169068
- const existing = getScriptApiById(id);
169069
- if (!existing || existing.authMode !== "bearer")
169036
+ function computeExpiresAt(expiresInSeconds) {
169037
+ if (!expiresInSeconds || expiresInSeconds <= 0)
169070
169038
  return null;
169071
- const token = generateBearerToken();
169072
- const encrypted = encryptSecret(token, getEncryptionKey());
169073
- const row = getDb().prepare("UPDATE script_apis SET bearerTokenEncrypted = ?, updated_by = ? WHERE id = ? RETURNING *").get(encrypted, updatedBy ?? null, id);
169074
- if (!row)
169075
- return null;
169076
- registerVolatileSecret(token, `script-api:${id}`);
169077
- return { ...rowToScriptApi(row), token };
169078
- }
169079
- function deleteScriptApi(id) {
169080
- return getDb().run("DELETE FROM script_apis WHERE id = ?", [id]).changes > 0;
169081
- }
169082
- function recordScriptApiUsage(id) {
169083
- getDb().run("UPDATE script_apis SET callCount = callCount + 1, lastUsedAt = ? WHERE id = ?", [
169084
- new Date().toISOString(),
169085
- id
169086
- ]);
169039
+ return new Date(Date.now() + expiresInSeconds * 1000).toISOString();
169087
169040
  }
169088
169041
 
169089
169042
  // src/scripts-runtime/credential-broker/types.ts
@@ -169091,6 +169044,7 @@ init_zod();
169091
169044
  var CREDENTIAL_BINDINGS_CONFIG_KEY = "SCRIPT_CREDENTIAL_BINDINGS";
169092
169045
  var REDACTED_PLACEHOLDER_PREFIX = "[REDACTED:";
169093
169046
  var CredentialBindingScopeSchema = _enum(["global", "agent", "repo"]);
169047
+ var CredentialBindingAuthKindSchema = _enum(["config", "oauth"]);
169094
169048
  var CredentialBindingSchema = object({
169095
169049
  configKey: string().min(1).max(255),
169096
169050
  allowedHosts: array(string().min(1)).min(1),
@@ -169098,9 +169052,14 @@ var CredentialBindingSchema = object({
169098
169052
  queryTemplate: string().min(1).optional(),
169099
169053
  scope: CredentialBindingScopeSchema.default("global"),
169100
169054
  scopeId: string().nullable().optional(),
169101
- active: boolean().default(true)
169055
+ active: boolean().default(true),
169056
+ authKind: CredentialBindingAuthKindSchema.default("config"),
169057
+ oauthProvider: string().min(1).max(255).optional()
169102
169058
  }).refine((binding) => binding.headerTemplate || binding.queryTemplate, {
169103
169059
  message: "At least one of headerTemplate or queryTemplate is required."
169060
+ }).refine((binding) => binding.authKind !== "oauth" || !!binding.oauthProvider, {
169061
+ message: "oauthProvider is required when authKind is oauth.",
169062
+ path: ["oauthProvider"]
169104
169063
  });
169105
169064
  var CredentialBindingsDocumentSchema = union([
169106
169065
  array(CredentialBindingSchema),
@@ -169161,7 +169120,6 @@ class CredentialBroker {
169161
169120
  return resolved;
169162
169121
  }
169163
169122
  }
169164
-
169165
169123
  // src/scripts-runtime/credential-broker/default-bindings.ts
169166
169124
  var DEFAULT_CREDENTIAL_BINDINGS = [
169167
169125
  {
@@ -169170,10 +169128,10 @@ var DEFAULT_CREDENTIAL_BINDINGS = [
169170
169128
  headerTemplate: "Authorization: Bearer [REDACTED:GITHUB_TOKEN]",
169171
169129
  scope: "global",
169172
169130
  scopeId: null,
169173
- active: true
169131
+ active: true,
169132
+ authKind: "config"
169174
169133
  }
169175
169134
  ];
169176
-
169177
169135
  // src/scripts-runtime/credential-broker/store.ts
169178
169136
  function appliesToContext(binding, context) {
169179
169137
  if (binding.scope === "global")
@@ -169221,8 +169179,312 @@ class SwarmConfigCredentialBindingStore {
169221
169179
  return rows.flatMap(parseBindingsFromConfig).filter((binding) => binding.active !== false).filter((binding) => appliesToContext(binding, context)).filter(bindingHasPlaceholder2);
169222
169180
  }
169223
169181
  }
169224
- // src/be/script-connections.ts
169182
+ // src/be/mcp-proxy.ts
169225
169183
  init_db();
169184
+
169185
+ // src/be/db-queries/mcp-oauth.ts
169186
+ init_crypto();
169187
+ init_date_utils();
169188
+ init_db();
169189
+ function encryptOrNull(plaintext) {
169190
+ if (plaintext == null || plaintext === "")
169191
+ return null;
169192
+ return encryptSecret(plaintext, getEncryptionKey());
169193
+ }
169194
+ function decryptOrNull(ciphertext) {
169195
+ if (ciphertext == null || ciphertext === "")
169196
+ return null;
169197
+ return decryptSecret(ciphertext, getEncryptionKey());
169198
+ }
169199
+ function decryptTokenRow(row) {
169200
+ const key = getEncryptionKey();
169201
+ return {
169202
+ ...row,
169203
+ accessToken: decryptSecret(row.accessToken, key),
169204
+ refreshToken: decryptOrNull(row.refreshToken),
169205
+ dcrClientSecret: decryptOrNull(row.dcrClientSecret),
169206
+ createdAt: normalizeDateRequired(row.createdAt),
169207
+ updatedAt: normalizeDateRequired(row.updatedAt)
169208
+ };
169209
+ }
169210
+ function getMcpOAuthToken(mcpServerId, userId = null) {
169211
+ const row = getDb().query(userId == null ? "SELECT * FROM mcp_oauth_tokens WHERE mcpServerId = ? AND userId IS NULL" : "SELECT * FROM mcp_oauth_tokens WHERE mcpServerId = ? AND userId = ?").get(...userId == null ? [mcpServerId] : [mcpServerId, userId]);
169212
+ return row ? decryptTokenRow(row) : null;
169213
+ }
169214
+ function upsertMcpOAuthToken(input) {
169215
+ const userId = input.userId ?? null;
169216
+ const encryptedAccess = encryptSecret(input.accessToken, getEncryptionKey());
169217
+ const encryptedRefresh = encryptOrNull(input.refreshToken);
169218
+ const encryptedClientSecret = encryptOrNull(input.dcrClientSecret);
169219
+ const existing = getMcpOAuthToken(input.mcpServerId, userId);
169220
+ if (!existing) {
169221
+ getDb().query(`INSERT INTO mcp_oauth_tokens (
169222
+ mcpServerId, userId,
169223
+ accessToken, refreshToken, tokenType, expiresAt, scope,
169224
+ resourceUrl, authorizationServerIssuer, authorizeUrl, tokenUrl, revocationUrl,
169225
+ dcrClientId, dcrClientSecret, clientSource,
169226
+ status, lastErrorMessage, lastRefreshedAt, connectedByUserId
169227
+ )
169228
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.mcpServerId, userId, encryptedAccess, encryptedRefresh, input.tokenType ?? "Bearer", input.expiresAt ?? null, input.scope ?? null, input.resourceUrl, input.authorizationServerIssuer, input.authorizeUrl, input.tokenUrl, input.revocationUrl ?? null, input.dcrClientId ?? null, encryptedClientSecret, input.clientSource, input.status ?? "connected", input.lastErrorMessage ?? null, input.lastRefreshedAt ?? null, input.connectedByUserId ?? null);
169229
+ return;
169230
+ }
169231
+ getDb().query(`UPDATE mcp_oauth_tokens SET
169232
+ accessToken = ?,
169233
+ refreshToken = COALESCE(?, refreshToken),
169234
+ tokenType = ?,
169235
+ expiresAt = ?,
169236
+ scope = COALESCE(?, scope),
169237
+ resourceUrl = ?,
169238
+ authorizationServerIssuer = ?,
169239
+ authorizeUrl = ?,
169240
+ tokenUrl = ?,
169241
+ revocationUrl = ?,
169242
+ dcrClientId = COALESCE(?, dcrClientId),
169243
+ dcrClientSecret = COALESCE(?, dcrClientSecret),
169244
+ clientSource = ?,
169245
+ status = ?,
169246
+ lastErrorMessage = ?,
169247
+ lastRefreshedAt = ?,
169248
+ connectedByUserId = COALESCE(?, connectedByUserId),
169249
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
169250
+ WHERE id = ?`).run(encryptedAccess, encryptedRefresh, input.tokenType ?? "Bearer", input.expiresAt ?? null, input.scope ?? null, input.resourceUrl, input.authorizationServerIssuer, input.authorizeUrl, input.tokenUrl, input.revocationUrl ?? null, input.dcrClientId ?? null, encryptedClientSecret, input.clientSource, input.status ?? "connected", input.lastErrorMessage ?? null, input.lastRefreshedAt ?? null, input.connectedByUserId ?? null, existing.id);
169251
+ }
169252
+ function applyMcpOAuthRefresh(id, data) {
169253
+ const key = getEncryptionKey();
169254
+ const encryptedAccess = encryptSecret(data.accessToken, key);
169255
+ const encryptedRefresh = data.refreshToken === undefined ? undefined : data.refreshToken == null ? null : encryptSecret(data.refreshToken, key);
169256
+ if (encryptedRefresh === undefined) {
169257
+ getDb().query(`UPDATE mcp_oauth_tokens
169258
+ SET accessToken = ?,
169259
+ expiresAt = COALESCE(?, expiresAt),
169260
+ scope = COALESCE(?, scope),
169261
+ status = 'connected',
169262
+ lastErrorMessage = NULL,
169263
+ lastRefreshedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
169264
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
169265
+ WHERE id = ?`).run(encryptedAccess, data.expiresAt ?? null, data.scope ?? null, id);
169266
+ } else {
169267
+ getDb().query(`UPDATE mcp_oauth_tokens
169268
+ SET accessToken = ?,
169269
+ refreshToken = ?,
169270
+ expiresAt = COALESCE(?, expiresAt),
169271
+ scope = COALESCE(?, scope),
169272
+ status = 'connected',
169273
+ lastErrorMessage = NULL,
169274
+ lastRefreshedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
169275
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
169276
+ WHERE id = ?`).run(encryptedAccess, encryptedRefresh, data.expiresAt ?? null, data.scope ?? null, id);
169277
+ }
169278
+ }
169279
+ function markMcpOAuthTokenStatus(id, status, errorMessage) {
169280
+ getDb().query(`UPDATE mcp_oauth_tokens
169281
+ SET status = ?,
169282
+ lastErrorMessage = ?,
169283
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
169284
+ WHERE id = ?`).run(status, errorMessage ?? null, id);
169285
+ }
169286
+ function deleteMcpOAuthToken(mcpServerId, userId = null) {
169287
+ const result = getDb().query(userId == null ? "DELETE FROM mcp_oauth_tokens WHERE mcpServerId = ? AND userId IS NULL" : "DELETE FROM mcp_oauth_tokens WHERE mcpServerId = ? AND userId = ?").run(...userId == null ? [mcpServerId] : [mcpServerId, userId]);
169288
+ return result.changes > 0;
169289
+ }
169290
+ function isMcpTokenExpiringSoon(token, bufferMs = 5 * 60 * 1000) {
169291
+ if (!token.expiresAt)
169292
+ return false;
169293
+ const expiresAt = new Date(token.expiresAt).getTime();
169294
+ if (Number.isNaN(expiresAt))
169295
+ return true;
169296
+ return expiresAt - Date.now() < bufferMs;
169297
+ }
169298
+ function insertMcpOAuthPending(input) {
169299
+ const key = getEncryptionKey();
169300
+ getDb().query(`INSERT INTO mcp_oauth_pending (
169301
+ state, mcpServerId, userId,
169302
+ codeVerifier, nonce,
169303
+ resourceUrl, authorizationServerIssuer, authorizeUrl, tokenUrl, revocationUrl,
169304
+ scopes, dcrClientId, dcrClientSecret, redirectUri, finalRedirect
169305
+ )
169306
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.state, input.mcpServerId, input.userId ?? null, encryptSecret(input.codeVerifier, key), input.nonce ?? null, input.resourceUrl, input.authorizationServerIssuer, input.authorizeUrl, input.tokenUrl, input.revocationUrl ?? null, input.scopes ?? null, input.dcrClientId ?? null, encryptOrNull(input.dcrClientSecret), input.redirectUri, input.finalRedirect ?? null);
169307
+ }
169308
+ function consumeMcpOAuthPending(state) {
169309
+ const row = getDb().query("SELECT * FROM mcp_oauth_pending WHERE state = ?").get(state);
169310
+ if (!row)
169311
+ return null;
169312
+ getDb().query("DELETE FROM mcp_oauth_pending WHERE state = ?").run(state);
169313
+ const key = getEncryptionKey();
169314
+ return {
169315
+ ...row,
169316
+ codeVerifier: decryptSecret(row.codeVerifier, key),
169317
+ dcrClientSecret: decryptOrNull(row.dcrClientSecret)
169318
+ };
169319
+ }
169320
+ function gcMcpOAuthPending(olderThanMs = 10 * 60 * 1000) {
169321
+ const cutoff = new Date(Date.now() - olderThanMs).toISOString();
169322
+ const result = getDb().query("DELETE FROM mcp_oauth_pending WHERE createdAt < ?").run(cutoff);
169323
+ return result.changes;
169324
+ }
169325
+ function setMcpServerAuthMethod(mcpServerId, authMethod) {
169326
+ getDb().query("UPDATE mcp_servers SET authMethod = ?, lastUpdatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = ?").run(authMethod, mcpServerId);
169327
+ }
169328
+
169329
+ // src/oauth/ensure-mcp-token.ts
169330
+ var inflight = new Map;
169331
+ async function ensureMcpToken(mcpServerId, opts = {}) {
169332
+ const userId = opts.userId ?? null;
169333
+ const key = `${mcpServerId}::${userId ?? "_"}`;
169334
+ const existing = inflight.get(key);
169335
+ if (existing)
169336
+ return existing;
169337
+ const work = (async () => {
169338
+ const token = getMcpOAuthToken(mcpServerId, userId);
169339
+ if (!token)
169340
+ return null;
169341
+ if (token.status === "revoked")
169342
+ return token;
169343
+ if (!isMcpTokenExpiringSoon(token, opts.bufferMs))
169344
+ return token;
169345
+ if (!token.refreshToken) {
169346
+ markMcpOAuthTokenStatus(token.id, "expired", "No refresh token available; reconnect required.");
169347
+ return { ...token, status: "expired" };
169348
+ }
169349
+ try {
169350
+ const refreshed = await refreshMcpToken({
169351
+ tokenUrl: token.tokenUrl,
169352
+ clientId: token.dcrClientId ?? "",
169353
+ clientSecret: token.dcrClientSecret ?? undefined,
169354
+ refreshToken: token.refreshToken,
169355
+ resource: token.resourceUrl,
169356
+ scopes: token.scope ? token.scope.split(" ").filter(Boolean) : undefined
169357
+ });
169358
+ applyMcpOAuthRefresh(token.id, {
169359
+ accessToken: refreshed.access_token,
169360
+ refreshToken: refreshed.refresh_token ?? undefined,
169361
+ expiresAt: computeExpiresAt(refreshed.expires_in),
169362
+ scope: refreshed.scope ?? null
169363
+ });
169364
+ return getMcpOAuthToken(mcpServerId, userId);
169365
+ } catch (err) {
169366
+ const message = err instanceof Error ? err.message : String(err);
169367
+ markMcpOAuthTokenStatus(token.id, "error", message);
169368
+ console.error(`[mcp-oauth] refresh failed for ${mcpServerId}: ${message}`);
169369
+ return { ...token, status: "error", lastErrorMessage: message };
169370
+ }
169371
+ })();
169372
+ inflight.set(key, work);
169373
+ try {
169374
+ return await work;
169375
+ } finally {
169376
+ inflight.delete(key);
169377
+ }
169378
+ }
169379
+
169380
+ // src/be/mcp-proxy.ts
169381
+ init_secret_scrubber();
169382
+ var MCP_PROXY_TIMEOUT_MS = 30000;
169383
+ function parseHeadersJson(value) {
169384
+ if (!value)
169385
+ return {};
169386
+ try {
169387
+ const parsed = JSON.parse(value);
169388
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
169389
+ return {};
169390
+ return Object.fromEntries(Object.entries(parsed).filter((entry) => typeof entry[1] === "string").map(([key, headerValue]) => [key, headerValue]));
169391
+ } catch {
169392
+ return {};
169393
+ }
169394
+ }
169395
+ function resolveHeaderConfigKeys(headerConfigKeys, context) {
169396
+ if (!headerConfigKeys)
169397
+ return {};
169398
+ let parsed;
169399
+ try {
169400
+ parsed = JSON.parse(headerConfigKeys);
169401
+ } catch {
169402
+ return {};
169403
+ }
169404
+ const configs = getResolvedConfig(context.agentId, context.repoId);
169405
+ const configMap = new Map(configs.map((config) => [config.key, config.value]));
169406
+ const resolved = {};
169407
+ if (Array.isArray(parsed)) {
169408
+ for (const item of parsed) {
169409
+ if (typeof item !== "string")
169410
+ continue;
169411
+ const value = configMap.get(item);
169412
+ if (value === undefined)
169413
+ continue;
169414
+ registerVolatileSecret(value, `mcp-header:${item}`);
169415
+ resolved[item] = value;
169416
+ }
169417
+ return resolved;
169418
+ }
169419
+ if (!parsed || typeof parsed !== "object")
169420
+ return resolved;
169421
+ for (const [headerName, configKey] of Object.entries(parsed)) {
169422
+ if (typeof configKey !== "string")
169423
+ continue;
169424
+ const value = configMap.get(configKey);
169425
+ if (value === undefined)
169426
+ continue;
169427
+ registerVolatileSecret(value, `mcp-header:${configKey}`);
169428
+ resolved[headerName] = value;
169429
+ }
169430
+ return resolved;
169431
+ }
169432
+ async function resolveMcpHeaders(server2, context) {
169433
+ const headers = {
169434
+ ...parseHeadersJson(server2.headers),
169435
+ ...resolveHeaderConfigKeys(server2.headerConfigKeys, context)
169436
+ };
169437
+ if (server2.authMethod !== "oauth")
169438
+ return headers;
169439
+ delete headers.Authorization;
169440
+ delete headers.authorization;
169441
+ const token = await ensureMcpToken(server2.id);
169442
+ if (!token)
169443
+ throw new Error("No OAuth token for this MCP server");
169444
+ if (token.status !== "connected") {
169445
+ throw new Error(token.lastErrorMessage ?? `OAuth status: ${token.status}`);
169446
+ }
169447
+ registerVolatileSecret(token.accessToken, `mcp-oauth:${server2.id}`);
169448
+ const rawType = token.tokenType || "Bearer";
169449
+ const prefix = rawType.toLowerCase() === "bearer" ? "Bearer" : rawType;
169450
+ headers.Authorization = `${prefix} ${token.accessToken}`;
169451
+ return headers;
169452
+ }
169453
+ async function createMcpServerClient(serverId, context = {}) {
169454
+ const server2 = getMcpServerById(serverId);
169455
+ if (!server2)
169456
+ throw new Error("MCP server not found");
169457
+ if (!server2.isEnabled)
169458
+ throw new Error("MCP server is disabled");
169459
+ if (server2.transport === "sse") {
169460
+ throw new Error("SSE MCP servers are not supported yet — use a streamable HTTP server");
169461
+ }
169462
+ if (server2.transport !== "http") {
169463
+ throw new Error("Only streamable HTTP MCP servers are supported by script connections");
169464
+ }
169465
+ if (!server2.url)
169466
+ throw new Error("MCP server URL is required");
169467
+ const client = new McpHttpClient(server2.url, "", "", undefined, {
169468
+ clientInfo: { name: "agent-swarm-script-mcp-proxy", version: "1.0.0" },
169469
+ omitEmptyAuthHeaders: true,
169470
+ timeoutMs: context.timeoutMs ?? MCP_PROXY_TIMEOUT_MS
169471
+ });
169472
+ client.useRawUrl = true;
169473
+ client.customHeaders = await resolveMcpHeaders(server2, context);
169474
+ return client;
169475
+ }
169476
+ async function listMcpServerTools(serverId, context = {}) {
169477
+ const client = await createMcpServerClient(serverId, context);
169478
+ await client.initialize();
169479
+ return client.listTools();
169480
+ }
169481
+ async function callMcpServerTool(serverId, tool, args, context = {}) {
169482
+ const client = await createMcpServerClient(serverId, context);
169483
+ await client.initialize();
169484
+ return client.callToolRaw(tool, args);
169485
+ }
169486
+
169487
+ // src/be/script-connections.ts
169226
169488
  function parseJsonArray(value) {
169227
169489
  try {
169228
169490
  const parsed = JSON.parse(value);
@@ -169241,6 +169503,8 @@ function bindingFromRow(row) {
169241
169503
  scope: row.scope,
169242
169504
  scopeId: row.scope_id,
169243
169505
  active: row.active === 1,
169506
+ authKind: row.auth_kind === "oauth" ? "oauth" : "config",
169507
+ oauthProvider: row.oauth_provider ?? undefined,
169244
169508
  source: row.source,
169245
169509
  createdAt: row.created_at,
169246
169510
  updatedAt: row.updated_at,
@@ -169317,6 +169581,11 @@ function upsertCredentialBinding(data) {
169317
169581
  const scope = data.scope ?? "global";
169318
169582
  const scopeId = scope === "global" ? null : data.scopeId ?? null;
169319
169583
  const active = data.active === false ? 0 : 1;
169584
+ const authKind = data.authKind ?? "config";
169585
+ if (authKind === "oauth" && !data.oauthProvider) {
169586
+ throw new Error("oauthProvider is required for oauth credential bindings");
169587
+ }
169588
+ const oauthProvider = data.oauthProvider ?? null;
169320
169589
  const source = data.source ?? "user";
169321
169590
  const existing = (data.id ? getCredentialBindingById(data.id) : null) ?? findCredentialBindingByIdentity({
169322
169591
  configKey: data.configKey,
@@ -169329,16 +169598,17 @@ function upsertCredentialBinding(data) {
169329
169598
  const targetId = existing.id;
169330
169599
  const row2 = getDb().prepare(`UPDATE script_credential_bindings
169331
169600
  SET config_key = ?, allowed_hosts_json = ?, header_template = ?, query_template = ?,
169332
- scope = ?, scope_id = ?, active = ?, source = ?, updated_by = ?, updated_at = ?
169333
- WHERE id = ? RETURNING *`).get(data.configKey, JSON.stringify(data.allowedHosts), data.headerTemplate ?? null, data.queryTemplate ?? null, scope, scopeId, active, source, data.userId ?? null, now, targetId);
169601
+ scope = ?, scope_id = ?, active = ?, auth_kind = ?, oauth_provider = ?,
169602
+ source = ?, updated_by = ?, updated_at = ?
169603
+ WHERE id = ? RETURNING *`).get(data.configKey, JSON.stringify(data.allowedHosts), data.headerTemplate ?? null, data.queryTemplate ?? null, scope, scopeId, active, authKind, oauthProvider, source, data.userId ?? null, now, targetId);
169334
169604
  if (!row2)
169335
169605
  throw new Error("Failed to update credential binding");
169336
169606
  return bindingFromRow(row2);
169337
169607
  }
169338
169608
  const row = getDb().prepare(`INSERT INTO script_credential_bindings
169339
169609
  (id, config_key, allowed_hosts_json, header_template, query_template, scope, scope_id,
169340
- active, source, created_at, updated_at, created_by, updated_by)
169341
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, data.configKey, JSON.stringify(data.allowedHosts), data.headerTemplate ?? null, data.queryTemplate ?? null, scope, scopeId, active, source, now, now, data.userId ?? null, data.userId ?? null);
169610
+ active, auth_kind, oauth_provider, source, created_at, updated_at, created_by, updated_by)
169611
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, data.configKey, JSON.stringify(data.allowedHosts), data.headerTemplate ?? null, data.queryTemplate ?? null, scope, scopeId, active, authKind, oauthProvider, source, now, now, data.userId ?? null, data.userId ?? null);
169342
169612
  if (!row)
169343
169613
  throw new Error("Failed to create credential binding");
169344
169614
  return bindingFromRow(row);
@@ -169376,7 +169646,11 @@ function importLegacyCredentialBindings(userId) {
169376
169646
  }
169377
169647
  function listScriptConnections(context) {
169378
169648
  const rows = getDb().prepare("SELECT * FROM script_connections ORDER BY slug ASC").all();
169379
- return rows.map(connectionFromRow).filter((connection) => !context?.kind || connection.kind === context.kind).filter((connection) => context?.includeDisabled || connection.enabled).filter((connection) => !context || applies(connection.scope, connection.scopeId, context));
169649
+ return rows.map(connectionFromRow).filter((connection) => !context?.kind || connection.kind === context.kind).filter((connection) => context?.includeDisabled || connection.enabled).filter((connection) => !context || context.allScopes === true || applies(connection.scope, connection.scopeId, context));
169650
+ }
169651
+ function getScriptConnectionById(id) {
169652
+ const row = getDb().prepare("SELECT * FROM script_connections WHERE id = ?").get(id);
169653
+ return row ? connectionFromRow(row) : null;
169380
169654
  }
169381
169655
  function schemaToTs(schema) {
169382
169656
  if (!schema || typeof schema !== "object")
@@ -169470,7 +169744,8 @@ function extractOperations(spec, slug) {
169470
169744
  const responseType = responseContent?.schema ? schemaToTs(responseContent.schema) : "JsonValue";
169471
169745
  const responseSchema = responseContent?.schema ? jsonSchema(responseContent.schema) : {};
169472
169746
  const paramsByPlace = (place) => parameters.filter((param) => param.in === place).map((param) => `${JSON.stringify(param.name)}${param.required ? "" : "?"}: ${param.type}`).join("; ");
169473
- const hasBody = Boolean(jsonContent?.schema || requestBody);
169747
+ const methodAllowsBody = method !== "get" && method !== "head";
169748
+ const hasBody = methodAllowsBody && Boolean(jsonContent?.schema || requestBody);
169474
169749
  const requestType = `export type ${typeBase}Args = {${paramsByPlace("path") ? ` path: { ${paramsByPlace("path")} };` : ""}${paramsByPlace("query") ? ` query?: { ${paramsByPlace("query")} };` : ""}${paramsByPlace("header") ? ` header?: { ${paramsByPlace("header")} };` : ""}${hasBody ? ` body: ${bodyType};` : ""}};`;
169475
169750
  const responseDecl = `export type ${typeBase}Response = ${responseType};`;
169476
169751
  typeBlocks.push(requestType, responseDecl);
@@ -169501,11 +169776,7 @@ function extractOperations(spec, slug) {
169501
169776
  function buildGeneratedArtifacts(input) {
169502
169777
  const slug = normalizeSlug(input.slug);
169503
169778
  const { operations, types } = extractOperations(input.openapiSpec, slug);
169504
- const credential = input.credentialBinding ? {
169505
- configKey: input.credentialBinding.configKey,
169506
- headerTemplate: input.credentialBinding.headerTemplate,
169507
- queryTemplate: input.credentialBinding.queryTemplate
169508
- } : null;
169779
+ const credential = credentialDescriptor(input.credentialBinding);
169509
169780
  const descriptor = {
169510
169781
  slug,
169511
169782
  baseUrl: input.baseUrl,
@@ -169515,24 +169786,203 @@ function buildGeneratedArtifacts(input) {
169515
169786
  const generatedTypes = [
169516
169787
  types,
169517
169788
  `export interface ${pascal(slug)}Api {`,
169518
- ...operations.map((operation) => ` ${operation.name}(args: ${operation.requestType}): Promise<${operation.responseType}>;`),
169789
+ ...operations.flatMap((operation) => [
169790
+ ` ${operation.name}(args: ${operation.requestType}): Promise<${operation.responseType}>;`,
169791
+ ` ${operation.name}(args: ${operation.requestType}, options: ScriptApiRawOptions): Promise<ScriptApiRawResult>;`
169792
+ ]),
169793
+ "}"
169794
+ ].join(`
169795
+ `);
169796
+ return { generatedTypes, generatedRuntimeJson: JSON.stringify(descriptor) };
169797
+ }
169798
+ function credentialDescriptor(binding) {
169799
+ return binding ? {
169800
+ configKey: binding.configKey,
169801
+ headerTemplate: binding.headerTemplate,
169802
+ queryTemplate: binding.queryTemplate
169803
+ } : null;
169804
+ }
169805
+ function buildGraphqlGeneratedArtifacts(input) {
169806
+ const slug = normalizeSlug(input.slug);
169807
+ const descriptor = {
169808
+ slug,
169809
+ kind: "graphql",
169810
+ baseUrl: input.baseUrl,
169811
+ credential: credentialDescriptor(input.credentialBinding)
169812
+ };
169813
+ const generatedTypes = [
169814
+ `export interface ${pascal(slug)}Api {`,
169815
+ " graphql<T = JsonValue>(query: string, variables?: Record<string, JsonValue>): Promise<T>;",
169816
+ "}"
169817
+ ].join(`
169818
+ `);
169819
+ return { generatedTypes, generatedRuntimeJson: JSON.stringify(descriptor) };
169820
+ }
169821
+ var openapiSpecFetchForTesting = null;
169822
+ function openapiSpecUrlOptions() {
169823
+ return publicEndpointSsrfOptions();
169824
+ }
169825
+ function isRedirectStatus(status) {
169826
+ return status >= 300 && status < 400 && status !== 304;
169827
+ }
169828
+ function mcpToolMethodName(name) {
169829
+ return normalizeSlug(name);
169830
+ }
169831
+ function mcpToolMethodNames(tools) {
169832
+ const used = new Set;
169833
+ const baseCounts = new Map;
169834
+ return tools.map((tool) => {
169835
+ const base = mcpToolMethodName(tool.name);
169836
+ const count = baseCounts.get(base) ?? 0;
169837
+ baseCounts.set(base, count + 1);
169838
+ let methodName2 = count === 0 ? base : `${base}${count + 1}`;
169839
+ let suffix = count + 2;
169840
+ while (used.has(methodName2)) {
169841
+ methodName2 = `${base}${suffix}`;
169842
+ suffix += 1;
169843
+ }
169844
+ used.add(methodName2);
169845
+ return { tool, methodName: methodName2 };
169846
+ });
169847
+ }
169848
+ function mcpToolArgsType(inputSchema) {
169849
+ if (!inputSchema || typeof inputSchema !== "object")
169850
+ return "Record<string, JsonValue>";
169851
+ const schema = inputSchema;
169852
+ if (Object.keys(schema).length === 0)
169853
+ return "Record<string, JsonValue>";
169854
+ const type = schemaToTs(schema);
169855
+ return type === "JsonValue" ? "Record<string, JsonValue>" : type;
169856
+ }
169857
+ function buildMcpGeneratedArtifacts(input) {
169858
+ const slug = normalizeSlug(input.slug);
169859
+ const methodNames = mcpToolMethodNames(input.tools);
169860
+ const tools = input.tools.map((tool) => ({
169861
+ name: tool.name,
169862
+ description: tool.description,
169863
+ inputSchema: jsonSchema(tool.inputSchema ?? {})
169864
+ }));
169865
+ const generatedTypes = [
169866
+ `export interface ${pascal(slug)}Mcp {`,
169867
+ ...methodNames.map(({ tool, methodName: methodName2 }) => ` ${methodName2}(args: ${mcpToolArgsType(tool.inputSchema)}): Promise<JsonValue>;`),
169519
169868
  "}"
169520
169869
  ].join(`
169521
169870
  `);
169871
+ const descriptor = {
169872
+ slug,
169873
+ kind: "mcp",
169874
+ connectionId: input.connectionId,
169875
+ tools
169876
+ };
169522
169877
  return { generatedTypes, generatedRuntimeJson: JSON.stringify(descriptor) };
169523
169878
  }
169524
- function upsertScriptConnection(data) {
169879
+ function parseOpenapiSpecBody(body) {
169880
+ try {
169881
+ return JSON.parse(body);
169882
+ } catch (jsonErr) {
169883
+ const yaml = Bun.YAML;
169884
+ if (!yaml) {
169885
+ throw new Error(`OpenAPI spec response was not valid JSON. JSON specs only unless Bun.YAML.parse is available: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`);
169886
+ }
169887
+ try {
169888
+ return yaml.parse(body);
169889
+ } catch (yamlErr) {
169890
+ throw new Error(`OpenAPI spec response was neither valid JSON nor valid YAML: ${yamlErr instanceof Error ? yamlErr.message : String(yamlErr)}`);
169891
+ }
169892
+ }
169893
+ }
169894
+ async function fetchOpenapiSpec(url, opts = {}) {
169895
+ let parsed = assertUrlSafe(url, openapiSpecUrlOptions());
169896
+ const controller = new AbortController;
169897
+ const timeout = setTimeout(() => controller.abort(), 15000);
169898
+ try {
169899
+ const headers = new Headers({
169900
+ Accept: "application/json, application/yaml;q=0.9, text/yaml;q=0.8, */*;q=0.5"
169901
+ });
169902
+ if (opts.etag)
169903
+ headers.set("If-None-Match", opts.etag);
169904
+ const fetchImpl = openapiSpecFetchForTesting ?? Bun.fetch;
169905
+ let response = null;
169906
+ for (let hop = 0;hop <= 5; hop += 1) {
169907
+ response = await fetchImpl(parsed, {
169908
+ headers,
169909
+ signal: controller.signal,
169910
+ redirect: "manual"
169911
+ });
169912
+ if (!isRedirectStatus(response.status))
169913
+ break;
169914
+ const location = response.headers.get("location");
169915
+ if (!location) {
169916
+ throw new Error(`OpenAPI spec redirect missing Location header: HTTP ${response.status}`);
169917
+ }
169918
+ parsed = assertUrlSafe(new URL(location, parsed).toString(), openapiSpecUrlOptions());
169919
+ if (hop === 5)
169920
+ throw new Error("OpenAPI spec fetch exceeded 5 redirects.");
169921
+ }
169922
+ if (!response)
169923
+ throw new Error("OpenAPI spec fetch failed before receiving a response.");
169924
+ const fetchedAt = new Date().toISOString();
169925
+ const etag = response.headers.get("etag");
169926
+ if (response.status === 304) {
169927
+ return { status: "not_modified", etag, fetchedAt };
169928
+ }
169929
+ if (!response.ok) {
169930
+ throw new Error(`Failed to fetch OpenAPI spec: HTTP ${response.status}`);
169931
+ }
169932
+ const spec = parseOpenapiSpecBody(await response.text());
169933
+ return {
169934
+ status: "fetched",
169935
+ spec,
169936
+ specJson: JSON.stringify(spec),
169937
+ etag,
169938
+ fetchedAt
169939
+ };
169940
+ } catch (err) {
169941
+ if (err instanceof DOMException && err.name === "AbortError") {
169942
+ throw new Error("Timed out fetching OpenAPI spec after 15s");
169943
+ }
169944
+ throw err;
169945
+ } finally {
169946
+ clearTimeout(timeout);
169947
+ }
169948
+ }
169949
+ async function upsertScriptConnection(data) {
169525
169950
  const now = new Date().toISOString();
169526
169951
  const id = data.id ?? crypto.randomUUID();
169527
169952
  const scope = data.scope ?? "global";
169528
169953
  const scopeId = scope === "global" ? null : data.scopeId ?? null;
169954
+ const normalizedSlug = normalizeSlug(data.slug);
169529
169955
  let generatedTypes = null;
169530
169956
  let generatedRuntimeJson = null;
169531
169957
  let generationError = null;
169532
169958
  let generatedAt = null;
169959
+ let openapiSpecJson = data.openapiSpecJson ?? null;
169960
+ let openapiSpecSourceKind = data.openapiSpecSourceKind ?? (data.kind === "openapi" ? data.openapiSpecUrl ? "url" : "inline" : null);
169961
+ let openapiSpecSource = data.openapiSpecSource ?? data.openapiSpecUrl ?? null;
169962
+ let openapiSpecEtag = data.openapiSpecEtag ?? null;
169963
+ let openapiSpecFetchedAt = data.openapiSpecFetchedAt ?? null;
169964
+ let openapiSpec;
169965
+ const existing = data.id ? getDb().prepare("SELECT id, version FROM script_connections WHERE id = ?").get(data.id) : getDb().prepare("SELECT id, version FROM script_connections WHERE slug = ? AND scope = ? AND COALESCE(scope_id, '') = COALESCE(?, '')").get(normalizedSlug, scope, scopeId);
169966
+ const connectionId = existing?.id ?? id;
169533
169967
  if (data.kind === "openapi") {
169968
+ if (!openapiSpecJson && data.openapiSpecUrl) {
169969
+ const fetched = await fetchOpenapiSpec(data.openapiSpecUrl);
169970
+ if (fetched.status === "not_modified") {
169971
+ throw new Error("OpenAPI spec URL returned 304 without an existing cached spec.");
169972
+ }
169973
+ openapiSpec = fetched.spec;
169974
+ openapiSpecJson = fetched.specJson;
169975
+ openapiSpecSourceKind = "url";
169976
+ openapiSpecSource = data.openapiSpecUrl;
169977
+ openapiSpecEtag = fetched.etag;
169978
+ openapiSpecFetchedAt = fetched.fetchedAt;
169979
+ }
169534
169980
  try {
169535
- const spec = JSON.parse(data.openapiSpecJson ?? "{}");
169981
+ let spec = openapiSpec;
169982
+ if (!spec) {
169983
+ spec = parseOpenapiSpecBody(openapiSpecJson ?? "{}");
169984
+ openapiSpecJson = JSON.stringify(spec);
169985
+ }
169536
169986
  const binding = data.credentialBindingId ? getCredentialBindingById(data.credentialBindingId) : null;
169537
169987
  const artifacts = buildGeneratedArtifacts({
169538
169988
  slug: data.slug,
@@ -169547,9 +169997,43 @@ function upsertScriptConnection(data) {
169547
169997
  generationError = err instanceof Error ? err.message : String(err);
169548
169998
  }
169549
169999
  }
169550
- const existing = data.id ? getDb().prepare("SELECT id, version FROM script_connections WHERE id = ?").get(data.id) : getDb().prepare("SELECT id, version FROM script_connections WHERE slug = ? AND scope = ? AND COALESCE(scope_id, '') = COALESCE(?, '')").get(normalizeSlug(data.slug), scope, scopeId);
170000
+ if (data.kind === "mcp") {
170001
+ try {
170002
+ if (!data.mcpServerId)
170003
+ throw new Error("mcpServerId is required for MCP connections");
170004
+ const discoveryContext = scope === "agent" && scopeId ? { agentId: scopeId } : scope === "repo" && scopeId ? { agentId: data.agentId, repoId: scopeId } : { agentId: data.agentId };
170005
+ const tools = await listMcpServerTools(data.mcpServerId, discoveryContext);
170006
+ const artifacts = buildMcpGeneratedArtifacts({
170007
+ slug: data.slug,
170008
+ connectionId,
170009
+ tools
170010
+ });
170011
+ generatedTypes = artifacts.generatedTypes;
170012
+ generatedRuntimeJson = artifacts.generatedRuntimeJson;
170013
+ generatedAt = now;
170014
+ } catch (err) {
170015
+ generationError = err instanceof Error ? err.message : String(err);
170016
+ }
170017
+ }
170018
+ if (data.kind === "graphql") {
170019
+ if (!data.baseUrl)
170020
+ throw new Error("baseUrl is required for GraphQL connections");
170021
+ if (!data.allowedHosts?.length) {
170022
+ throw new Error("allowedHosts is required for GraphQL connections");
170023
+ }
170024
+ new URL(data.baseUrl);
170025
+ const binding = data.credentialBindingId ? getCredentialBindingById(data.credentialBindingId) : null;
170026
+ const artifacts = buildGraphqlGeneratedArtifacts({
170027
+ slug: data.slug,
170028
+ baseUrl: data.baseUrl,
170029
+ credentialBinding: binding
170030
+ });
170031
+ generatedTypes = artifacts.generatedTypes;
170032
+ generatedRuntimeJson = artifacts.generatedRuntimeJson;
170033
+ generatedAt = now;
170034
+ }
169551
170035
  const params = [
169552
- normalizeSlug(data.slug),
170036
+ normalizedSlug,
169553
170037
  data.displayName ?? null,
169554
170038
  data.kind,
169555
170039
  scope,
@@ -169557,9 +170041,11 @@ function upsertScriptConnection(data) {
169557
170041
  data.baseUrl ?? null,
169558
170042
  JSON.stringify(data.allowedHosts ?? []),
169559
170043
  data.credentialBindingId ?? null,
169560
- data.openapiSpecSourceKind ?? (data.kind === "openapi" ? "inline" : null),
169561
- data.openapiSpecSource ?? null,
169562
- data.openapiSpecJson ?? null,
170044
+ openapiSpecSourceKind,
170045
+ openapiSpecSource,
170046
+ openapiSpecJson,
170047
+ openapiSpecEtag,
170048
+ openapiSpecFetchedAt,
169563
170049
  data.mcpServerId ?? null,
169564
170050
  generatedTypes,
169565
170051
  generatedRuntimeJson,
@@ -169571,7 +170057,8 @@ function upsertScriptConnection(data) {
169571
170057
  const row2 = getDb().prepare(`UPDATE script_connections SET
169572
170058
  slug = ?, display_name = ?, kind = ?, scope = ?, scope_id = ?, base_url = ?,
169573
170059
  allowed_hosts_json = ?, credential_binding_id = ?, openapi_spec_source_kind = ?,
169574
- openapi_spec_source = ?, openapi_spec_json = ?, mcp_server_id = ?, generated_types = ?,
170060
+ openapi_spec_source = ?, openapi_spec_json = ?, openapi_spec_etag = ?,
170061
+ openapi_spec_fetched_at = ?, mcp_server_id = ?, generated_types = ?,
169575
170062
  generated_runtime_json = ?, generated_at = ?, generation_error = ?, enabled = ?,
169576
170063
  updated_at = ?, updated_by = ?, version = ?
169577
170064
  WHERE id = ? RETURNING *`).get(...params, now, data.userId ?? null, existing.version + 1, existing.id);
@@ -169582,13 +170069,73 @@ function upsertScriptConnection(data) {
169582
170069
  const row = getDb().prepare(`INSERT INTO script_connections
169583
170070
  (id, slug, display_name, kind, scope, scope_id, base_url, allowed_hosts_json,
169584
170071
  credential_binding_id, openapi_spec_source_kind, openapi_spec_source, openapi_spec_json,
169585
- mcp_server_id, generated_types, generated_runtime_json, generated_at, generation_error,
169586
- enabled, created_at, updated_at, created_by, updated_by)
169587
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, ...params, now, now, data.userId ?? null, data.userId ?? null);
170072
+ openapi_spec_etag, openapi_spec_fetched_at, mcp_server_id, generated_types,
170073
+ generated_runtime_json, generated_at, generation_error, enabled, created_at, updated_at,
170074
+ created_by, updated_by)
170075
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, ...params, now, now, data.userId ?? null, data.userId ?? null);
169588
170076
  if (!row)
169589
170077
  throw new Error("Failed to create script connection");
169590
170078
  return connectionFromRow(row);
169591
170079
  }
170080
+ async function refreshScriptConnection(id, userId, callerAgentId) {
170081
+ const row = getDb().prepare("SELECT * FROM script_connections WHERE id = ?").get(id);
170082
+ if (!row)
170083
+ return null;
170084
+ const connection = connectionFromRow(row);
170085
+ if (connection.kind === "mcp") {
170086
+ if (!connection.mcpServerId) {
170087
+ throw new Error("MCP connection has no mcpServerId to refresh from.");
170088
+ }
170089
+ return upsertScriptConnection({
170090
+ id: connection.id,
170091
+ slug: connection.slug,
170092
+ displayName: connection.displayName,
170093
+ kind: "mcp",
170094
+ scope: connection.scope,
170095
+ scopeId: connection.scopeId,
170096
+ allowedHosts: connection.allowedHosts,
170097
+ credentialBindingId: connection.credentialBindingId,
170098
+ mcpServerId: connection.mcpServerId,
170099
+ enabled: connection.enabled,
170100
+ agentId: callerAgentId,
170101
+ userId
170102
+ });
170103
+ }
170104
+ if (connection.kind !== "openapi") {
170105
+ throw new Error("Only OpenAPI and MCP script connections can be refreshed.");
170106
+ }
170107
+ if (connection.openapiSpecSourceKind !== "url" || !connection.openapiSpecSource) {
170108
+ throw new Error("Only OpenAPI script connections registered by URL can be refreshed.");
170109
+ }
170110
+ const fetched = await fetchOpenapiSpec(connection.openapiSpecSource, {
170111
+ etag: connection.openapiSpecEtag
170112
+ });
170113
+ if (fetched.status === "not_modified") {
170114
+ const refreshed = getDb().prepare(`UPDATE script_connections
170115
+ SET openapi_spec_fetched_at = ?
170116
+ WHERE id = ? RETURNING *`).get(fetched.fetchedAt, id);
170117
+ return refreshed ? connectionFromRow(refreshed) : null;
170118
+ }
170119
+ return upsertScriptConnection({
170120
+ id: connection.id,
170121
+ slug: connection.slug,
170122
+ displayName: connection.displayName,
170123
+ kind: "openapi",
170124
+ scope: connection.scope,
170125
+ scopeId: connection.scopeId,
170126
+ baseUrl: connection.baseUrl,
170127
+ allowedHosts: connection.allowedHosts,
170128
+ credentialBindingId: connection.credentialBindingId,
170129
+ openapiSpecSourceKind: "url",
170130
+ openapiSpecSource: connection.openapiSpecSource,
170131
+ openapiSpecJson: fetched.specJson,
170132
+ openapiSpecEtag: fetched.etag,
170133
+ openapiSpecFetchedAt: fetched.fetchedAt,
170134
+ mcpServerId: connection.mcpServerId,
170135
+ enabled: connection.enabled,
170136
+ userId
170137
+ });
170138
+ }
169592
170139
  function setScriptConnectionEnabled(id, enabled, userId) {
169593
170140
  const row = getDb().prepare(`UPDATE script_connections
169594
170141
  SET enabled = ?, updated_at = ?, updated_by = ?, version = version + 1
@@ -169596,7 +170143,18 @@ function setScriptConnectionEnabled(id, enabled, userId) {
169596
170143
  return row ? connectionFromRow(row) : null;
169597
170144
  }
169598
170145
  function getScriptApiConnectionDescriptors(context = {}) {
169599
- return listScriptConnections({ ...context, kind: "openapi" }).map((connection) => {
170146
+ return listScriptConnections(context).filter((connection) => connection.kind === "openapi" || connection.kind === "graphql").map((connection) => {
170147
+ if (!connection.generatedRuntimeJson)
170148
+ return null;
170149
+ try {
170150
+ return JSON.parse(connection.generatedRuntimeJson);
170151
+ } catch {
170152
+ return null;
170153
+ }
170154
+ }).filter((descriptor) => Boolean(descriptor));
170155
+ }
170156
+ function getScriptMcpConnectionDescriptors(context = {}) {
170157
+ return listScriptConnections({ ...context, kind: "mcp" }).map((connection) => {
169600
170158
  if (!connection.generatedRuntimeJson)
169601
170159
  return null;
169602
170160
  try {
@@ -169607,7 +170165,7 @@ function getScriptApiConnectionDescriptors(context = {}) {
169607
170165
  }).filter((descriptor) => Boolean(descriptor));
169608
170166
  }
169609
170167
  function getScriptApiTypes(context = {}) {
169610
- const connections = listScriptConnections({ ...context, kind: "openapi" });
170168
+ const connections = listScriptConnections(context).filter((connection) => connection.kind === "openapi" || connection.kind === "graphql");
169611
170169
  const blocks = connections.filter((connection) => connection.generatedTypes).map((connection) => connection.generatedTypes);
169612
170170
  if (blocks.length === 0)
169613
170171
  return `export interface ScriptApiRegistry {}
@@ -169623,6 +170181,356 @@ ${members.join(`
169623
170181
  }
169624
170182
  `;
169625
170183
  }
170184
+ function getScriptMcpTypes(context = {}) {
170185
+ const connections = listScriptConnections({ ...context, kind: "mcp" });
170186
+ const blocks = connections.filter((connection) => connection.generatedTypes).map((connection) => connection.generatedTypes);
170187
+ if (blocks.length === 0)
170188
+ return `export interface ScriptMcpRegistry {}
170189
+ `;
170190
+ const members = connections.filter((connection) => connection.generatedTypes).map((connection) => ` ${connection.slug}: ${pascal(connection.slug)}Mcp;`);
170191
+ return `${blocks.join(`
170192
+
170193
+ `)}
170194
+
170195
+ export interface ScriptMcpRegistry {
170196
+ ${members.join(`
170197
+ `)}
170198
+ }
170199
+ `;
170200
+ }
170201
+
170202
+ // src/be/scripts/db.ts
170203
+ init_secret_scrubber();
170204
+
170205
+ // src/utils/short-id.ts
170206
+ import { randomBytes } from "node:crypto";
170207
+ var LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
170208
+ var BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
170209
+ function generateShortId(len = 12) {
170210
+ let out = "";
170211
+ while (out.length < len) {
170212
+ for (const byte of randomBytes((len - out.length) * 2)) {
170213
+ if (byte < 208) {
170214
+ out += LETTERS[byte % 52];
170215
+ if (out.length === len)
170216
+ break;
170217
+ }
170218
+ }
170219
+ }
170220
+ return out;
170221
+ }
170222
+ function base62(bytes) {
170223
+ let out = "";
170224
+ for (const byte of bytes)
170225
+ out += BASE62[byte % 62];
170226
+ return out;
170227
+ }
170228
+ function generateBearerToken() {
170229
+ return `xsk_${base62(randomBytes(24))}`;
170230
+ }
170231
+
170232
+ // src/be/scripts/db.ts
170233
+ init_crypto();
170234
+ init_db();
170235
+ function normalizeScopeId(scope, scopeId) {
170236
+ if (scope === "global")
170237
+ return null;
170238
+ if (!scopeId) {
170239
+ throw new Error("scopeId is required for agent-scoped scripts");
170240
+ }
170241
+ return scopeId;
170242
+ }
170243
+ function rowToScript(row) {
170244
+ return {
170245
+ ...row,
170246
+ scopeId: row.scopeId ?? null,
170247
+ isScratch: row.isScratch === 1,
170248
+ typeChecked: row.typeChecked === 1,
170249
+ createdByAgentId: row.createdByAgentId ?? null
170250
+ };
170251
+ }
170252
+ function rowToScriptVersion(row) {
170253
+ return {
170254
+ ...row,
170255
+ changedByAgentId: row.changedByAgentId ?? null,
170256
+ changeReason: row.changeReason ?? null
170257
+ };
170258
+ }
170259
+ function insertScriptVersion(args) {
170260
+ getDb().prepare(`INSERT INTO script_versions (
170261
+ id, scriptId, version, source, description, intent, signatureJson,
170262
+ contentHash, changedByAgentId, changedAt, changeReason
170263
+ )
170264
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(crypto.randomUUID(), args.scriptId, args.version, args.source, args.description, args.intent, args.signatureJson, args.contentHash, args.changedByAgentId ?? null, new Date().toISOString(), args.changeReason ?? null);
170265
+ }
170266
+ function insertScript(args) {
170267
+ const now = new Date().toISOString();
170268
+ const id = crypto.randomUUID();
170269
+ const scopeId = normalizeScopeId(args.scope, args.scopeId);
170270
+ const contentHash = computeContentHash(args.source);
170271
+ const fsMode = args.fsMode ?? "none";
170272
+ const isScratch = args.isScratch ? 1 : 0;
170273
+ const typeChecked = args.typeChecked ? 1 : 0;
170274
+ const txn = getDb().transaction(() => {
170275
+ const row = getDb().prepare(`INSERT INTO scripts (
170276
+ id, name, scope, scopeId, source, description, intent, signatureJson,
170277
+ argsJsonSchema, contentHash, isScratch, typeChecked, fsMode, createdByAgentId, createdAt, updatedAt,
170278
+ created_by, updated_by
170279
+ )
170280
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
170281
+ RETURNING *`).get(id, args.name, args.scope, scopeId, args.source, args.description, args.intent, args.signatureJson, args.argsJsonSchema ?? null, contentHash, isScratch, typeChecked, fsMode, args.agentId ?? null, now, now, args.createdBy ?? null, args.createdBy ?? null);
170282
+ if (!row)
170283
+ throw new Error("Failed to insert script");
170284
+ insertScriptVersion({
170285
+ scriptId: row.id,
170286
+ version: row.version,
170287
+ source: row.source,
170288
+ description: row.description,
170289
+ intent: row.intent,
170290
+ signatureJson: row.signatureJson,
170291
+ contentHash: row.contentHash,
170292
+ changedByAgentId: args.agentId ?? null,
170293
+ changeReason: args.changeReason ?? "Initial creation"
170294
+ });
170295
+ return rowToScript(row);
170296
+ });
170297
+ return txn();
170298
+ }
170299
+ async function upsertScriptByName(args) {
170300
+ const shouldEmbed = args.embeddingMode !== "skip";
170301
+ const existing = getScript(args);
170302
+ if (!existing) {
170303
+ const script2 = insertScript(args);
170304
+ if (!script2.isScratch && shouldEmbed) {
170305
+ await embedScript(script2);
170306
+ }
170307
+ return {
170308
+ script: script2,
170309
+ isNew: true,
170310
+ contentDeduped: false
170311
+ };
170312
+ }
170313
+ const contentHash = computeContentHash(args.source);
170314
+ if (existing.contentHash === contentHash) {
170315
+ const fsMode2 = args.fsMode ?? existing.fsMode;
170316
+ const isScratch2 = args.isScratch ?? existing.isScratch;
170317
+ const typeChecked2 = args.typeChecked ?? existing.typeChecked;
170318
+ const argsJsonSchema2 = args.argsJsonSchema !== undefined ? args.argsJsonSchema : existing.argsJsonSchema;
170319
+ const trackedMetadataChanged = args.description !== existing.description || args.intent !== existing.intent || args.signatureJson !== existing.signatureJson || argsJsonSchema2 !== existing.argsJsonSchema;
170320
+ const promotedFromScratch = existing.isScratch && !isScratch2;
170321
+ if (fsMode2 !== existing.fsMode || isScratch2 !== existing.isScratch || typeChecked2 !== existing.typeChecked || trackedMetadataChanged) {
170322
+ const row = getDb().prepare(`UPDATE scripts
170323
+ SET description = ?, intent = ?, signatureJson = ?, argsJsonSchema = ?,
170324
+ isScratch = ?, typeChecked = ?, fsMode = ?, updatedAt = ?, updated_by = ?
170325
+ WHERE id = ?
170326
+ RETURNING *`).get(args.description, args.intent, args.signatureJson, argsJsonSchema2 ?? null, isScratch2 ? 1 : 0, typeChecked2 ? 1 : 0, fsMode2, new Date().toISOString(), args.createdBy ?? null, existing.id);
170327
+ if (!row)
170328
+ throw new Error("Failed to update script metadata");
170329
+ const script2 = rowToScript(row);
170330
+ if (!script2.isScratch && shouldEmbed && (trackedMetadataChanged || promotedFromScratch)) {
170331
+ await embedScript(script2);
170332
+ }
170333
+ return {
170334
+ script: script2,
170335
+ isNew: false,
170336
+ contentDeduped: true
170337
+ };
170338
+ }
170339
+ return {
170340
+ script: existing,
170341
+ isNew: false,
170342
+ contentDeduped: true
170343
+ };
170344
+ }
170345
+ const now = new Date().toISOString();
170346
+ const newVersion = existing.version + 1;
170347
+ const fsMode = args.fsMode ?? existing.fsMode;
170348
+ const isScratch = args.isScratch ?? existing.isScratch;
170349
+ const typeChecked = args.typeChecked ?? existing.typeChecked;
170350
+ const argsJsonSchema = args.argsJsonSchema !== undefined ? args.argsJsonSchema : existing.argsJsonSchema;
170351
+ const txn = getDb().transaction(() => {
170352
+ const row = getDb().prepare(`UPDATE scripts
170353
+ SET source = ?, description = ?, intent = ?, signatureJson = ?, argsJsonSchema = ?,
170354
+ contentHash = ?, version = ?, isScratch = ?, typeChecked = ?, fsMode = ?, updatedAt = ?, updated_by = ?
170355
+ WHERE id = ?
170356
+ RETURNING *`).get(args.source, args.description, args.intent, args.signatureJson, argsJsonSchema ?? null, contentHash, newVersion, isScratch ? 1 : 0, typeChecked ? 1 : 0, fsMode, now, args.createdBy ?? null, existing.id);
170357
+ if (!row)
170358
+ throw new Error("Failed to update script");
170359
+ insertScriptVersion({
170360
+ scriptId: row.id,
170361
+ version: row.version,
170362
+ source: row.source,
170363
+ description: row.description,
170364
+ intent: row.intent,
170365
+ signatureJson: row.signatureJson,
170366
+ contentHash: row.contentHash,
170367
+ changedByAgentId: args.agentId ?? null,
170368
+ changeReason: args.changeReason ?? null
170369
+ });
170370
+ return rowToScript(row);
170371
+ });
170372
+ const script = txn();
170373
+ if (!script.isScratch && shouldEmbed) {
170374
+ await embedScript(script);
170375
+ }
170376
+ return {
170377
+ script,
170378
+ isNew: false,
170379
+ contentDeduped: false
170380
+ };
170381
+ }
170382
+ function getScript(args) {
170383
+ const scopeId = normalizeScopeId(args.scope, args.scopeId);
170384
+ const row = scopeId === null ? getDb().prepare("SELECT * FROM scripts WHERE name = ? AND scope = ? AND scopeId IS NULL").get(args.name, args.scope) : getDb().prepare("SELECT * FROM scripts WHERE name = ? AND scope = ? AND scopeId = ?").get(args.name, args.scope, scopeId);
170385
+ return row ? rowToScript(row) : null;
170386
+ }
170387
+ function getScriptById(id) {
170388
+ const row = getDb().prepare("SELECT * FROM scripts WHERE id = ?").get(id);
170389
+ return row ? rowToScript(row) : null;
170390
+ }
170391
+ function getScriptVersion(args) {
170392
+ if (args.version === undefined && args.contentHash === undefined) {
170393
+ throw new Error("version or contentHash is required");
170394
+ }
170395
+ const row = args.version !== undefined ? getDb().prepare("SELECT * FROM script_versions WHERE scriptId = ? AND version = ?").get(args.scriptId, args.version) : getDb().prepare("SELECT * FROM script_versions WHERE scriptId = ? AND contentHash = ? ORDER BY version DESC LIMIT 1").get(args.scriptId, args.contentHash);
170396
+ return row ? rowToScriptVersion(row) : null;
170397
+ }
170398
+ function listScripts(args) {
170399
+ const conditions = [];
170400
+ const params = [];
170401
+ if (args?.scope) {
170402
+ conditions.push("scope = ?");
170403
+ params.push(args.scope);
170404
+ if (args.scope === "global") {
170405
+ conditions.push("scopeId IS NULL");
170406
+ } else if (args.scopeId !== undefined) {
170407
+ conditions.push("scopeId = ?");
170408
+ params.push(normalizeScopeId(args.scope, args.scopeId));
170409
+ }
170410
+ } else if (args?.scopeId !== undefined) {
170411
+ conditions.push("scopeId = ?");
170412
+ params.push(args.scopeId ?? "");
170413
+ }
170414
+ if (!args?.includeScratch) {
170415
+ conditions.push("isScratch = 0");
170416
+ }
170417
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
170418
+ return getDb().prepare(`SELECT * FROM scripts ${whereClause} ORDER BY scope ASC, scopeId ASC, name ASC`).all(...params).map(rowToScript);
170419
+ }
170420
+ function listScriptVersions(scriptId) {
170421
+ return getDb().prepare("SELECT * FROM script_versions WHERE scriptId = ? ORDER BY version DESC").all(scriptId).map(rowToScriptVersion);
170422
+ }
170423
+ function deleteScript(args) {
170424
+ const existing = getScript(args);
170425
+ if (!existing)
170426
+ return false;
170427
+ const result = getDb().run("DELETE FROM scripts WHERE id = ?", [existing.id]);
170428
+ return result.changes > 0;
170429
+ }
170430
+ var SCRIPT_API_ID_LEN = 12;
170431
+ function rowToScriptApi(row) {
170432
+ return {
170433
+ id: row.id,
170434
+ scriptId: row.scriptId,
170435
+ agentId: row.agentId,
170436
+ authMode: row.authMode,
170437
+ enabled: row.enabled === 1,
170438
+ label: row.label ?? null,
170439
+ callCount: row.callCount,
170440
+ lastUsedAt: row.lastUsedAt ?? null,
170441
+ createdAt: row.createdAt
170442
+ };
170443
+ }
170444
+ function isUniqueConstraintError(err) {
170445
+ return err instanceof Error && /UNIQUE constraint failed/i.test(err.message);
170446
+ }
170447
+ function createScriptApi(args) {
170448
+ const now = new Date().toISOString();
170449
+ const token = args.authMode === "bearer" ? generateBearerToken() : null;
170450
+ const encrypted = token ? encryptSecret(token, getEncryptionKey()) : null;
170451
+ const createdBy = args.createdBy ?? null;
170452
+ for (let attempt = 0;attempt < 5; attempt++) {
170453
+ const id = generateShortId(SCRIPT_API_ID_LEN);
170454
+ try {
170455
+ const row = getDb().prepare(`INSERT INTO script_apis
170456
+ (id, scriptId, agentId, authMode, bearerTokenEncrypted, enabled, label,
170457
+ callCount, lastUsedAt, createdAt, created_by, updated_by)
170458
+ VALUES (?, ?, ?, ?, ?, 1, ?, 0, NULL, ?, ?, ?)
170459
+ RETURNING *`).get(id, args.scriptId, args.agentId, args.authMode, encrypted, args.label ?? null, now, createdBy, createdBy);
170460
+ if (!row)
170461
+ throw new Error("Failed to create script API endpoint");
170462
+ if (token)
170463
+ registerVolatileSecret(token, `script-api:${id}`);
170464
+ return { ...rowToScriptApi(row), token };
170465
+ } catch (err) {
170466
+ if (isUniqueConstraintError(err) && attempt < 4)
170467
+ continue;
170468
+ throw err;
170469
+ }
170470
+ }
170471
+ throw new Error("Failed to allocate a unique script API endpoint id");
170472
+ }
170473
+ function listScriptApisForScript(scriptId) {
170474
+ return getDb().prepare("SELECT * FROM script_apis WHERE scriptId = ? ORDER BY createdAt DESC").all(scriptId).map(rowToScriptApi);
170475
+ }
170476
+ function getScriptApiById(id) {
170477
+ const row = getDb().prepare("SELECT * FROM script_apis WHERE id = ?").get(id);
170478
+ if (!row)
170479
+ return null;
170480
+ return { ...rowToScriptApi(row), bearerTokenEncrypted: row.bearerTokenEncrypted ?? null };
170481
+ }
170482
+ function getScriptApiSecret(id) {
170483
+ const row = getDb().prepare("SELECT bearerTokenEncrypted FROM script_apis WHERE id = ?").get(id);
170484
+ if (!row?.bearerTokenEncrypted)
170485
+ return null;
170486
+ const token = decryptSecret(row.bearerTokenEncrypted, getEncryptionKey());
170487
+ registerVolatileSecret(token, `script-api:${id}`);
170488
+ return token;
170489
+ }
170490
+ function updateScriptApi(id, args) {
170491
+ const sets = [];
170492
+ const vals = [];
170493
+ if (args.enabled !== undefined) {
170494
+ sets.push("enabled = ?");
170495
+ vals.push(args.enabled ? 1 : 0);
170496
+ }
170497
+ if (args.label !== undefined) {
170498
+ sets.push("label = ?");
170499
+ vals.push(args.label);
170500
+ }
170501
+ if (args.updatedBy !== undefined) {
170502
+ sets.push("updated_by = ?");
170503
+ vals.push(args.updatedBy);
170504
+ }
170505
+ if (sets.length === 0) {
170506
+ const row2 = getDb().prepare("SELECT * FROM script_apis WHERE id = ?").get(id);
170507
+ return row2 ? rowToScriptApi(row2) : null;
170508
+ }
170509
+ vals.push(id);
170510
+ const row = getDb().prepare(`UPDATE script_apis SET ${sets.join(", ")} WHERE id = ? RETURNING *`).get(...vals);
170511
+ return row ? rowToScriptApi(row) : null;
170512
+ }
170513
+ function rotateScriptApiSecret(id, updatedBy) {
170514
+ const existing = getScriptApiById(id);
170515
+ if (!existing || existing.authMode !== "bearer")
170516
+ return null;
170517
+ const token = generateBearerToken();
170518
+ const encrypted = encryptSecret(token, getEncryptionKey());
170519
+ const row = getDb().prepare("UPDATE script_apis SET bearerTokenEncrypted = ?, updated_by = ? WHERE id = ? RETURNING *").get(encrypted, updatedBy ?? null, id);
170520
+ if (!row)
170521
+ return null;
170522
+ registerVolatileSecret(token, `script-api:${id}`);
170523
+ return { ...rowToScriptApi(row), token };
170524
+ }
170525
+ function deleteScriptApi(id) {
170526
+ return getDb().run("DELETE FROM script_apis WHERE id = ?", [id]).changes > 0;
170527
+ }
170528
+ function recordScriptApiUsage(id) {
170529
+ getDb().run("UPDATE script_apis SET callCount = callCount + 1, lastUsedAt = ? WHERE id = ?", [
170530
+ new Date().toISOString(),
170531
+ id
170532
+ ]);
170533
+ }
169626
170534
 
169627
170535
  // src/scripts-runtime/import-allowlist.ts
169628
170536
  var import_typescript = __toESM(require_typescript(), 1);
@@ -169689,4 +170597,4 @@ function validateScriptImports(source) {
169689
170597
  return { ok: false, diagnostic: reason, imports: rejected };
169690
170598
  }
169691
170599
 
169692
- export { CREDENTIAL_BINDINGS_CONFIG_KEY, CredentialBindingSchema, placeholderForConfigKey, CredentialBroker, DEFAULT_CREDENTIAL_BINDINGS, SwarmConfigCredentialBindingStore, listRelationalCredentialBindings, upsertCredentialBinding, disableCredentialBinding, importLegacyCredentialBindings, listScriptConnections, upsertScriptConnection, setScriptConnectionEnabled, getScriptApiConnectionDescriptors, getScriptApiTypes, upsertScriptByName, getScript, getScriptById, getScriptVersion, listScripts, listScriptVersions, deleteScript, createScriptApi, listScriptApisForScript, getScriptApiById, getScriptApiSecret, updateScriptApi, rotateScriptApiSecret, deleteScriptApi, recordScriptApiUsage, require_typescript, validateScriptImports };
170600
+ export { assertUrlSafe, publicEndpointSsrfOptions, discoverProtectedResourceMetadata, discoverAuthorizationServerMetadata, registerClient, buildAuthorizeUrl, exchangeCodeForTokens, refreshMcpToken, revokeMcpToken, computeExpiresAt, CREDENTIAL_BINDINGS_CONFIG_KEY, CredentialBindingSchema, placeholderForConfigKey, CredentialBroker, DEFAULT_CREDENTIAL_BINDINGS, SwarmConfigCredentialBindingStore, getMcpOAuthToken, upsertMcpOAuthToken, applyMcpOAuthRefresh, deleteMcpOAuthToken, insertMcpOAuthPending, consumeMcpOAuthPending, gcMcpOAuthPending, setMcpServerAuthMethod, ensureMcpToken, callMcpServerTool, listRelationalCredentialBindings, upsertCredentialBinding, disableCredentialBinding, importLegacyCredentialBindings, listScriptConnections, getScriptConnectionById, upsertScriptConnection, refreshScriptConnection, setScriptConnectionEnabled, getScriptApiConnectionDescriptors, getScriptMcpConnectionDescriptors, getScriptApiTypes, getScriptMcpTypes, upsertScriptByName, getScript, getScriptById, getScriptVersion, listScripts, listScriptVersions, deleteScript, createScriptApi, listScriptApisForScript, getScriptApiById, getScriptApiSecret, updateScriptApi, rotateScriptApiSecret, deleteScriptApi, recordScriptApiUsage, require_typescript, validateScriptImports };