@desplega.ai/agent-swarm 1.119.3 → 1.119.4

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 (61) hide show
  1. package/dist/{actions-kd321p1j.js → actions-cvcf510b.js} +5 -5
  2. package/dist/{app-p1gqacgd.js → app-05ksvyna.js} +3 -3
  3. package/dist/{assistant-6w0zb089.js → assistant-wqr7nmqv.js} +8 -8
  4. package/dist/{boot-reembed-zktmj8zt.js → boot-reembed-dx3zweeh.js} +4 -4
  5. package/dist/{boot-reembed-7gy85nnp.js → boot-reembed-v0q84hej.js} +3 -3
  6. package/dist/{boot-scrub-logs-d8n8pcgr.js → boot-scrub-logs-3am4xegg.js} +2 -2
  7. package/dist/{cli-gzng1fst.js → cli-0y66mw72.js} +9 -7
  8. package/dist/{cli-527y7yzc.js → cli-1c5zhw9z.js} +3 -3
  9. package/dist/{cli-eb3s7zwh.js → cli-4xtzqzv4.js} +18 -18
  10. package/dist/{cli-yzxg93ma.js → cli-67e8g7bj.js} +4 -4
  11. package/dist/{cli-td2x0s9b.js → cli-758zm6xy.js} +1 -1
  12. package/dist/{cli-y82m27mq.js → cli-7mvmqemc.js} +1 -1
  13. package/dist/{cli-6expbmb3.js → cli-8jjrc3ww.js} +1 -1
  14. package/dist/{cli-p4yn675m.js → cli-bzjncaf6.js} +1 -1
  15. package/dist/{cli-8y1kmyn7.js → cli-chfh8r6f.js} +2 -2
  16. package/dist/{cli-3sew4a9j.js → cli-cs5g429e.js} +1 -1
  17. package/dist/{cli-j5da6jb2.js → cli-fhmhjjh4.js} +17 -29
  18. package/dist/{cli-e5hdn9nx.js → cli-h1bffz9b.js} +1 -1
  19. package/dist/{cli-9y4z25ke.js → cli-jfc1jm13.js} +1 -1
  20. package/dist/{cli-emgzr4ja.js → cli-k0x0fvh5.js} +5 -5
  21. package/dist/{cli-xgefj1nz.js → cli-kmtp1rg9.js} +2 -2
  22. package/dist/{cli-7yfmb4ne.js → cli-pc276yqz.js} +1 -1
  23. package/dist/{cli-j6n9at34.js → cli-pyagab0x.js} +2 -2
  24. package/dist/{cli-g5a31r9y.js → cli-qe43w9a5.js} +2 -2
  25. package/dist/{cli-z612d1nc.js → cli-qfp17rw8.js} +1 -1
  26. package/dist/{cli-9aqtt1dn.js → cli-rk6fkhkk.js} +5 -5
  27. package/dist/{cli-j2vpt4hd.js → cli-ttb3pty1.js} +4 -4
  28. package/dist/{cli-cx30v78p.js → cli-zm2jjaq6.js} +1 -1
  29. package/dist/{cli-get7n46a.js → cli-zrxpz200.js} +1 -1
  30. package/dist/cli.js +9 -9
  31. package/dist/{commands-14gkjns3.js → commands-3ke60h4e.js} +2 -2
  32. package/dist/{db-c9wx8pbf.js → db-a1e1nj43.js} +2 -2
  33. package/dist/{handlers-m9jxp50r.js → handlers-d86a4ypy.js} +8 -8
  34. package/dist/{hook-mm8mj3cp.js → hook-teegt6h5.js} +1 -1
  35. package/dist/{http-hhmh7m51.js → http-64jvxqnz.js} +33 -33
  36. package/dist/{index-vmcp507g.js → index-a95n0ay5.js} +8 -8
  37. package/dist/{index-basky7qw.js → index-bndzxk11.js} +10 -10
  38. package/dist/{index-9h6qre5v.js → index-hb9j1aqr.js} +6 -6
  39. package/dist/{index-ep25fyer.js → index-z39ch6n5.js} +9 -9
  40. package/dist/{keepalive-70mwsyhb.js → keepalive-ab1qpqrm.js} +4 -4
  41. package/dist/{lead-pshn5w58.js → lead-852gtrsa.js} +18 -18
  42. package/dist/{maintenance-rbjav7ey.js → maintenance-fke93wqq.js} +4 -4
  43. package/dist/{oauth-refresh-sweep-dfbd7yw0.js → oauth-refresh-sweep-a139pm5w.js} +3 -3
  44. package/dist/{onboard-g8911158.js → onboard-vcd22m9e.js} +2 -2
  45. package/dist/{otel-impl-qw4pakj6.js → otel-impl-88egk5hc.js} +1 -1
  46. package/dist/{pricing-refresh-7ra0g3az.js → pricing-refresh-bbg7s7qs.js} +4 -4
  47. package/dist/{rbac-roles-ksnmkjzf.js → rbac-roles-e169ndy2.js} +2 -2
  48. package/dist/{rbac-roles-s0esm5k6.js → rbac-roles-kc9t08gp.js} +3 -3
  49. package/dist/{seed-pricing-jd8zj8k7.js → seed-pricing-3zhtw483.js} +3 -3
  50. package/dist/{setup-z2s4vqf3.js → setup-6m99ne8y.js} +2 -2
  51. package/dist/{worker-xse3rpzd.js → worker-pwg46aav.js} +18 -18
  52. package/openapi.json +1 -1
  53. package/package.json +1 -1
  54. package/src/be/script-credential-broker.ts +12 -26
  55. package/src/scripts-runtime/credential-broker/broker.ts +15 -4
  56. package/src/scripts-runtime/credential-broker/index.ts +1 -0
  57. package/src/scripts-runtime/credential-broker/types.ts +1 -0
  58. package/src/scripts-runtime/egress-secrets.ts +2 -2
  59. package/src/scripts-runtime/loader.ts +3 -3
  60. package/src/tests/credential-broker.test.ts +130 -3
  61. package/src/tests/scripts-runtime-secret-egress.test.ts +4 -4
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runAgent
3
- } from "./cli-j2vpt4hd.js";
3
+ } from "./cli-ttb3pty1.js";
4
4
  import"./cli-sznberje.js";
5
5
  import"./cli-tyfxrrdd.js";
6
6
  import"./cli-bc89my0z.js";
@@ -14,7 +14,7 @@ import"./cli-yv9dttkh.js";
14
14
  import"./cli-hx1dcevq.js";
15
15
  import {
16
16
  getEnabledCapabilities
17
- } from "./cli-eb3s7zwh.js";
17
+ } from "./cli-4xtzqzv4.js";
18
18
  import"./cli-1z7048dy.js";
19
19
  import"./cli-gz9sbyk3.js";
20
20
  import"./cli-ftyf58xj.js";
@@ -25,36 +25,36 @@ import"./cli-psrqtw2m.js";
25
25
  import"./cli-zm7njwzd.js";
26
26
  import"./cli-tghre7g6.js";
27
27
  import"./cli-e61fs2hp.js";
28
- import"./cli-yzxg93ma.js";
29
- import"./cli-8y1kmyn7.js";
30
- import"./cli-e5hdn9nx.js";
31
- import"./cli-7yfmb4ne.js";
28
+ import"./cli-67e8g7bj.js";
29
+ import"./cli-chfh8r6f.js";
30
+ import"./cli-h1bffz9b.js";
31
+ import"./cli-pc276yqz.js";
32
32
  import"./cli-86fdnerz.js";
33
- import"./cli-z612d1nc.js";
34
- import"./cli-j5da6jb2.js";
33
+ import"./cli-qfp17rw8.js";
34
+ import"./cli-fhmhjjh4.js";
35
35
  import"./cli-f14fvzag.js";
36
- import"./cli-p4yn675m.js";
37
- import"./cli-gzng1fst.js";
36
+ import"./cli-bzjncaf6.js";
37
+ import"./cli-0y66mw72.js";
38
38
  import"./cli-jkhwmmgg.js";
39
- import"./cli-xgefj1nz.js";
40
- import"./cli-6expbmb3.js";
39
+ import"./cli-kmtp1rg9.js";
40
+ import"./cli-8jjrc3ww.js";
41
41
  import"./cli-q25ejbyn.js";
42
42
  import"./cli-0pygq9r1.js";
43
43
  import"./cli-fad8m16k.js";
44
- import"./cli-9y4z25ke.js";
45
- import"./cli-emgzr4ja.js";
44
+ import"./cli-jfc1jm13.js";
45
+ import"./cli-k0x0fvh5.js";
46
46
  import"./cli-rkndnn90.js";
47
47
  import"./cli-b0p7rfnd.js";
48
- import"./cli-3sew4a9j.js";
49
- import"./cli-td2x0s9b.js";
48
+ import"./cli-cs5g429e.js";
49
+ import"./cli-758zm6xy.js";
50
50
  import"./cli-ma04faw5.js";
51
51
  import"./cli-wspgs9bt.js";
52
52
  import"./cli-dh55d5fg.js";
53
- import"./cli-y82m27mq.js";
53
+ import"./cli-7mvmqemc.js";
54
54
  import"./cli-z2zcxes1.js";
55
55
  import"./cli-bw80ck94.js";
56
56
  import"./cli-xz9aq0rf.js";
57
- import"./cli-cx30v78p.js";
57
+ import"./cli-zm2jjaq6.js";
58
58
  import"./cli-4j2mrzag.js";
59
59
  import"./cli-e15jhafb.js";
60
60
  import"./cli-9aryf9tv.js";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  reembedAllScripts
3
- } from "./cli-xgefj1nz.js";
4
- import"./cli-6expbmb3.js";
3
+ } from "./cli-kmtp1rg9.js";
4
+ import"./cli-8jjrc3ww.js";
5
5
  import"./cli-q25ejbyn.js";
6
- import"./cli-y82m27mq.js";
6
+ import"./cli-7mvmqemc.js";
7
7
  import"./cli-z2zcxes1.js";
8
8
  import"./cli-bw80ck94.js";
9
9
  import"./cli-xz9aq0rf.js";
10
- import"./cli-cx30v78p.js";
10
+ import"./cli-zm2jjaq6.js";
11
11
  import"./cli-4j2mrzag.js";
12
12
  import"./cli-p9swy5t3.js";
13
13
 
@@ -2,13 +2,13 @@ import {
2
2
  forceRefreshTokenOrThrow,
3
3
  getOAuthTokens,
4
4
  listOAuthTokenSweepRows
5
- } from "./cli-p4yn675m.js";
5
+ } from "./cli-bzjncaf6.js";
6
6
  import"./cli-0pygq9r1.js";
7
- import"./cli-y82m27mq.js";
7
+ import"./cli-7mvmqemc.js";
8
8
  import"./cli-z2zcxes1.js";
9
9
  import"./cli-bw80ck94.js";
10
10
  import"./cli-xz9aq0rf.js";
11
- import"./cli-cx30v78p.js";
11
+ import"./cli-zm2jjaq6.js";
12
12
  import {
13
13
  init_secret_scrubber,
14
14
  scrubSecrets
@@ -3,7 +3,7 @@ import {
3
3
  createDefaultMcpJson,
4
4
  createDefaultSettingsLocal,
5
5
  createHooksConfig
6
- } from "./cli-get7n46a.js";
6
+ } from "./cli-zrxpz200.js";
7
7
  import {
8
8
  require_jsx_dev_runtime
9
9
  } from "./cli-wx82tw1k.js";
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  init_package,
15
15
  package_default
16
- } from "./cli-cx30v78p.js";
16
+ } from "./cli-zm2jjaq6.js";
17
17
  import {
18
18
  Box_default,
19
19
  Select,
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  init_package,
8
8
  package_default
9
- } from "./cli-cx30v78p.js";
9
+ } from "./cli-zm2jjaq6.js";
10
10
  import {
11
11
  init_secret_scrubber,
12
12
  scrubSecrets
@@ -2,13 +2,13 @@ import {
2
2
  PRICING_REFRESH_INTERVAL_MS,
3
3
  refreshPricingFromModelsDev,
4
4
  startPricingRefreshLoop
5
- } from "./cli-8y1kmyn7.js";
6
- import"./cli-e5hdn9nx.js";
7
- import"./cli-y82m27mq.js";
5
+ } from "./cli-chfh8r6f.js";
6
+ import"./cli-h1bffz9b.js";
7
+ import"./cli-7mvmqemc.js";
8
8
  import"./cli-z2zcxes1.js";
9
9
  import"./cli-bw80ck94.js";
10
10
  import"./cli-xz9aq0rf.js";
11
- import"./cli-cx30v78p.js";
11
+ import"./cli-zm2jjaq6.js";
12
12
  import"./cli-4j2mrzag.js";
13
13
  import"./cli-p9swy5t3.js";
14
14
  export {
@@ -4,11 +4,11 @@ import {
4
4
  import {
5
5
  getDb,
6
6
  init_db
7
- } from "./cli-y82m27mq.js";
7
+ } from "./cli-7mvmqemc.js";
8
8
  import"./cli-z2zcxes1.js";
9
9
  import"./cli-bw80ck94.js";
10
10
  import"./cli-xz9aq0rf.js";
11
- import"./cli-cx30v78p.js";
11
+ import"./cli-zm2jjaq6.js";
12
12
  import"./cli-4j2mrzag.js";
13
13
  import"./cli-p9swy5t3.js";
14
14
 
@@ -7,13 +7,13 @@ import {
7
7
  getUserGrant,
8
8
  listUserRoles,
9
9
  runRbacCliCommand
10
- } from "./cli-7yfmb4ne.js";
10
+ } from "./cli-pc276yqz.js";
11
11
  import"./cli-86fdnerz.js";
12
- import"./cli-y82m27mq.js";
12
+ import"./cli-7mvmqemc.js";
13
13
  import"./cli-z2zcxes1.js";
14
14
  import"./cli-bw80ck94.js";
15
15
  import"./cli-xz9aq0rf.js";
16
- import"./cli-cx30v78p.js";
16
+ import"./cli-zm2jjaq6.js";
17
17
  import"./cli-4j2mrzag.js";
18
18
  import"./cli-p9swy5t3.js";
19
19
  export {
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  buildModelsDevSeedRows,
3
3
  seedPricingFromModelsDev
4
- } from "./cli-e5hdn9nx.js";
5
- import"./cli-y82m27mq.js";
4
+ } from "./cli-h1bffz9b.js";
5
+ import"./cli-7mvmqemc.js";
6
6
  import"./cli-z2zcxes1.js";
7
7
  import"./cli-bw80ck94.js";
8
8
  import"./cli-xz9aq0rf.js";
9
- import"./cli-cx30v78p.js";
9
+ import"./cli-zm2jjaq6.js";
10
10
  import"./cli-4j2mrzag.js";
11
11
  import"./cli-p9swy5t3.js";
12
12
  export {
@@ -4,14 +4,14 @@ import {
4
4
  createDefaultMcpJson,
5
5
  createDefaultSettingsLocal,
6
6
  createHooksConfig
7
- } from "./cli-get7n46a.js";
7
+ } from "./cli-zrxpz200.js";
8
8
  import {
9
9
  require_jsx_dev_runtime
10
10
  } from "./cli-wx82tw1k.js";
11
11
  import {
12
12
  getApiKey
13
13
  } from "./cli-f14fvzag.js";
14
- import"./cli-cx30v78p.js";
14
+ import"./cli-zm2jjaq6.js";
15
15
  import {
16
16
  Box_default,
17
17
  Spinner,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runAgent
3
- } from "./cli-j2vpt4hd.js";
3
+ } from "./cli-ttb3pty1.js";
4
4
  import"./cli-sznberje.js";
5
5
  import"./cli-tyfxrrdd.js";
6
6
  import"./cli-bc89my0z.js";
@@ -14,7 +14,7 @@ import"./cli-yv9dttkh.js";
14
14
  import"./cli-hx1dcevq.js";
15
15
  import {
16
16
  getEnabledCapabilities
17
- } from "./cli-eb3s7zwh.js";
17
+ } from "./cli-4xtzqzv4.js";
18
18
  import"./cli-1z7048dy.js";
19
19
  import"./cli-gz9sbyk3.js";
20
20
  import"./cli-ftyf58xj.js";
@@ -25,36 +25,36 @@ import"./cli-psrqtw2m.js";
25
25
  import"./cli-zm7njwzd.js";
26
26
  import"./cli-tghre7g6.js";
27
27
  import"./cli-e61fs2hp.js";
28
- import"./cli-yzxg93ma.js";
29
- import"./cli-8y1kmyn7.js";
30
- import"./cli-e5hdn9nx.js";
31
- import"./cli-7yfmb4ne.js";
28
+ import"./cli-67e8g7bj.js";
29
+ import"./cli-chfh8r6f.js";
30
+ import"./cli-h1bffz9b.js";
31
+ import"./cli-pc276yqz.js";
32
32
  import"./cli-86fdnerz.js";
33
- import"./cli-z612d1nc.js";
34
- import"./cli-j5da6jb2.js";
33
+ import"./cli-qfp17rw8.js";
34
+ import"./cli-fhmhjjh4.js";
35
35
  import"./cli-f14fvzag.js";
36
- import"./cli-p4yn675m.js";
37
- import"./cli-gzng1fst.js";
36
+ import"./cli-bzjncaf6.js";
37
+ import"./cli-0y66mw72.js";
38
38
  import"./cli-jkhwmmgg.js";
39
- import"./cli-xgefj1nz.js";
40
- import"./cli-6expbmb3.js";
39
+ import"./cli-kmtp1rg9.js";
40
+ import"./cli-8jjrc3ww.js";
41
41
  import"./cli-q25ejbyn.js";
42
42
  import"./cli-0pygq9r1.js";
43
43
  import"./cli-fad8m16k.js";
44
- import"./cli-9y4z25ke.js";
45
- import"./cli-emgzr4ja.js";
44
+ import"./cli-jfc1jm13.js";
45
+ import"./cli-k0x0fvh5.js";
46
46
  import"./cli-rkndnn90.js";
47
47
  import"./cli-b0p7rfnd.js";
48
- import"./cli-3sew4a9j.js";
49
- import"./cli-td2x0s9b.js";
48
+ import"./cli-cs5g429e.js";
49
+ import"./cli-758zm6xy.js";
50
50
  import"./cli-ma04faw5.js";
51
51
  import"./cli-wspgs9bt.js";
52
52
  import"./cli-dh55d5fg.js";
53
- import"./cli-y82m27mq.js";
53
+ import"./cli-7mvmqemc.js";
54
54
  import"./cli-z2zcxes1.js";
55
55
  import"./cli-bw80ck94.js";
56
56
  import"./cli-xz9aq0rf.js";
57
- import"./cli-cx30v78p.js";
57
+ import"./cli-zm2jjaq6.js";
58
58
  import"./cli-4j2mrzag.js";
59
59
  import"./cli-e15jhafb.js";
60
60
  import"./cli-9aryf9tv.js";
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Swarm API",
5
- "version": "1.119.3",
5
+ "version": "1.119.4",
6
6
  "description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
7
7
  },
8
8
  "servers": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.119.3",
3
+ "version": "1.119.4",
4
4
  "description": "Multi-agent orchestration for Claude Code, Codex, Gemini CLI, and other AI coding assistants",
5
5
  "license": "MIT",
6
6
  "author": "desplega.sh <contact@desplega.sh>",
@@ -25,38 +25,24 @@ export async function buildScriptCredentialBindings(input: {
25
25
  }): Promise<EgressSecretEntry[]> {
26
26
  const resolvedConfigs = getResolvedConfig(input.agentId, input.repoId);
27
27
  const configMap = new Map(resolvedConfigs.map((config) => [config.key, config.value]));
28
- const relationalBindings = listRelationalCredentialBindings(input);
29
- for (const binding of relationalBindings) {
30
- if (binding.authKind !== "oauth") continue;
31
- configMap.set(binding.configKey, "");
32
- if (!binding.oauthProvider) continue;
33
-
34
- let accessToken: string | undefined;
35
- try {
36
- accessToken = await resolveOAuthBindingToken(binding.oauthProvider);
37
- } catch (err) {
38
- // A stale/broken provider must not take down unrelated script runs —
39
- // skip just this binding (the "" shadow above keeps the placeholder
40
- // unresolved instead of falling through to a same-named config value).
41
- const message = err instanceof Error ? err.message : String(err);
42
- console.warn(
43
- `[script-credential-broker] skipping OAuth binding ${binding.configKey}: token refresh for provider ${binding.oauthProvider} failed: ${scrubSecrets(message)}`,
44
- );
45
- continue;
46
- }
47
- if (!accessToken) continue;
48
-
49
- configMap.set(binding.configKey, accessToken);
50
- registerVolatileSecret(accessToken, binding.configKey);
51
- }
52
-
53
28
  const broker = new CredentialBroker(
54
29
  new RelationalCredentialBindingStore((filters) => getSwarmConfigs(filters)),
55
30
  (configKey) => configMap.get(configKey) ?? process.env[configKey],
56
31
  DEFAULT_CREDENTIAL_BINDINGS,
32
+ async (provider) => {
33
+ try {
34
+ return await resolveOAuthBindingToken(provider);
35
+ } catch (err) {
36
+ const message = err instanceof Error ? err.message : String(err);
37
+ console.warn(
38
+ `[script-credential-broker] skipping OAuth provider ${provider}: ${scrubSecrets(message)}`,
39
+ );
40
+ return undefined;
41
+ }
42
+ },
57
43
  );
58
44
 
59
- const bindings = broker.resolveBindings({ agentId: input.agentId, repoId: input.repoId });
45
+ const bindings = await broker.resolveBindings({ agentId: input.agentId, repoId: input.repoId });
60
46
  for (const binding of bindings) {
61
47
  registerVolatileSecret(binding.value, binding.configKey);
62
48
  }
@@ -1,4 +1,9 @@
1
- import type { CredentialBinding, CredentialBindingStore, CredentialResolver } from "./types";
1
+ import type {
2
+ CredentialBinding,
3
+ CredentialBindingStore,
4
+ CredentialResolver,
5
+ OAuthCredentialResolver,
6
+ } from "./types";
2
7
  import { placeholderForConfigKey, type ResolvedCredentialBinding } from "./types";
3
8
 
4
9
  function bindingHasPlaceholder(binding: CredentialBinding) {
@@ -12,11 +17,12 @@ function bindingHasPlaceholder(binding: CredentialBinding) {
12
17
  export class CredentialBroker {
13
18
  constructor(
14
19
  private readonly store: CredentialBindingStore,
15
- private readonly resolveCredential: CredentialResolver,
20
+ private readonly resolveConfigCredential: CredentialResolver,
16
21
  private readonly defaults: CredentialBinding[] = [],
22
+ private readonly resolveOAuthCredential?: OAuthCredentialResolver,
17
23
  ) {}
18
24
 
19
- resolveBindings(context: Parameters<CredentialBindingStore["listActiveBindings"]>[0]) {
25
+ async resolveBindings(context: Parameters<CredentialBindingStore["listActiveBindings"]>[0]) {
20
26
  const merged = [...this.defaults, ...this.store.listActiveBindings(context)];
21
27
  const resolved: ResolvedCredentialBinding[] = [];
22
28
  const seen = new Set<string>();
@@ -25,7 +31,12 @@ export class CredentialBroker {
25
31
  if (binding.active === false) continue;
26
32
  if (!bindingHasPlaceholder(binding)) continue;
27
33
 
28
- const value = this.resolveCredential(binding.configKey);
34
+ const value =
35
+ binding.authKind === "oauth"
36
+ ? binding.oauthProvider
37
+ ? await this.resolveOAuthCredential?.(binding.oauthProvider)
38
+ : undefined
39
+ : this.resolveConfigCredential(binding.configKey);
29
40
  if (!value) continue;
30
41
 
31
42
  const dedupeKey = [
@@ -12,6 +12,7 @@ export {
12
12
  CredentialBindingsDocumentSchema,
13
13
  type CredentialResolver,
14
14
  normalizeCredentialBindingsDocument,
15
+ type OAuthCredentialResolver,
15
16
  placeholderForConfigKey,
16
17
  type ResolvedCredentialBinding,
17
18
  } from "./types";
@@ -51,6 +51,7 @@ export interface CredentialBindingStore {
51
51
  }
52
52
 
53
53
  export type CredentialResolver = (configKey: string) => string | undefined;
54
+ export type OAuthCredentialResolver = (oauthProvider: string) => Promise<string | undefined>;
54
55
 
55
56
  export function placeholderForConfigKey(configKey: string): string {
56
57
  return `${REDACTED_PLACEHOLDER_PREFIX}${configKey}]`;
@@ -7,13 +7,13 @@ import {
7
7
 
8
8
  export type EgressSecretEntry = ResolvedCredentialBinding;
9
9
 
10
- export function buildEgressSecrets(): EgressSecretEntry[] {
10
+ export async function buildEgressSecrets(): Promise<EgressSecretEntry[]> {
11
11
  const broker = new CredentialBroker(
12
12
  { listActiveBindings: () => [] },
13
13
  (configKey) => process.env[configKey],
14
14
  DEFAULT_CREDENTIAL_BINDINGS,
15
15
  );
16
- return broker.resolveBindings({});
16
+ return await broker.resolveBindings({});
17
17
  }
18
18
 
19
19
  export function patchFetchWithEgressSubstitution(secrets: EgressSecretEntry[]): void {
@@ -36,7 +36,7 @@ export type RunScriptOutput = Omit<ExecutorOutput, "result" | "stdout" | "stderr
36
36
 
37
37
  export type { ScriptRuntimeError, ScriptStackFrame } from "./executors/types";
38
38
 
39
- function buildConfigPayload(input: RunScriptInput): SwarmConfigPayload {
39
+ async function buildConfigPayload(input: RunScriptInput): Promise<SwarmConfigPayload> {
40
40
  const apiKey = getApiKey();
41
41
  if (!apiKey) throw new Error("Swarm API key is required to run scripts");
42
42
 
@@ -50,7 +50,7 @@ function buildConfigPayload(input: RunScriptInput): SwarmConfigPayload {
50
50
  },
51
51
  },
52
52
  user: input.userConfig ?? {},
53
- egressSecrets: input.egressSecrets ?? buildEgressSecrets(),
53
+ egressSecrets: input.egressSecrets ?? (await buildEgressSecrets()),
54
54
  apiConnections: input.apiConnections ?? [],
55
55
  mcpConnections: input.mcpConnections ?? [],
56
56
  };
@@ -91,7 +91,7 @@ export async function runScript(input: RunScriptInput): Promise<RunScriptOutput>
91
91
  const output = await getScriptExecutor().run({
92
92
  source: input.source,
93
93
  args: input.args ?? null,
94
- configPayload: buildConfigPayload(input),
94
+ configPayload: await buildConfigPayload(input),
95
95
  resources,
96
96
  fsMode: input.fsMode ?? "none",
97
97
  network: "open",
@@ -1,6 +1,7 @@
1
- import { afterEach, describe, expect, test } from "bun:test";
1
+ import { afterEach, describe, expect, mock, test } from "bun:test";
2
2
  import { deleteSwarmConfig, upsertSwarmConfig } from "../be/db";
3
3
  import { buildScriptCredentialBindings } from "../be/script-credential-broker";
4
+ import { createApiRegistryClient } from "../scripts-runtime/api-client";
4
5
  import {
5
6
  CREDENTIAL_BINDINGS_CONFIG_KEY,
6
7
  type CredentialBindingStore,
@@ -119,7 +120,7 @@ describe("credential broker", () => {
119
120
  );
120
121
  });
121
122
 
122
- test("resolves seeded GITHUB_TOKEN binding", () => {
123
+ test("resolves seeded GITHUB_TOKEN binding", async () => {
123
124
  const emptyStore: CredentialBindingStore = { listActiveBindings: () => [] };
124
125
  const broker = new CredentialBroker(
125
126
  emptyStore,
@@ -127,7 +128,7 @@ describe("credential broker", () => {
127
128
  DEFAULT_CREDENTIAL_BINDINGS,
128
129
  );
129
130
 
130
- expect(broker.resolveBindings({})).toEqual([
131
+ expect(await broker.resolveBindings({})).toEqual([
131
132
  {
132
133
  configKey: "GITHUB_TOKEN",
133
134
  allowedHosts: ["api.github.com"],
@@ -142,6 +143,132 @@ describe("credential broker", () => {
142
143
  ]);
143
144
  });
144
145
 
146
+ test("resolves OAuth bindings with the OAuth resolver and substitutes their header", async () => {
147
+ const oauthResolver = mock(async (provider: string) =>
148
+ provider === "gmailSupport" ? "gmail-access-token" : undefined,
149
+ );
150
+ const broker = new CredentialBroker(
151
+ {
152
+ listActiveBindings: () => [
153
+ {
154
+ configKey: "GMAIL_SUPPORT_OAUTH_BINDING",
155
+ allowedHosts: ["gmail.googleapis.com"],
156
+ headerTemplate: "Authorization: Bearer [REDACTED:GMAIL_SUPPORT_OAUTH_BINDING]",
157
+ scope: "global",
158
+ scopeId: null,
159
+ active: true,
160
+ authKind: "oauth",
161
+ oauthProvider: "gmailSupport",
162
+ },
163
+ ],
164
+ },
165
+ () => {
166
+ throw new Error("config resolver must not be used for OAuth bindings");
167
+ },
168
+ [],
169
+ oauthResolver,
170
+ );
171
+ const bindings = await broker.resolveBindings({});
172
+
173
+ expect(oauthResolver).toHaveBeenCalledWith("gmailSupport");
174
+ let authorization: string | null = null;
175
+ globalThis.fetch = (async (_input: string | URL | Request, init?: RequestInit) => {
176
+ authorization = new Headers(init?.headers).get("authorization");
177
+ return Response.json({ ok: true });
178
+ }) as typeof fetch;
179
+ patchFetchWithCredentialBroker(bindings);
180
+
181
+ await fetch("https://gmail.googleapis.com/gmail/v1/users/me/profile", {
182
+ headers: { Authorization: "Bearer [REDACTED:GMAIL_SUPPORT_OAUTH_BINDING]" },
183
+ });
184
+
185
+ expect(authorization).toBe("Bearer gmail-access-token");
186
+ });
187
+
188
+ test("resolved OAuth bindings also authenticate ctx.api clients", async () => {
189
+ let authorization: string | null = null;
190
+ globalThis.fetch = (async (_input: string | URL | Request, init?: RequestInit) => {
191
+ authorization = new Headers(init?.headers).get("authorization");
192
+ return Response.json({ data: { ok: true } });
193
+ }) as typeof fetch;
194
+ const broker = new CredentialBroker(
195
+ {
196
+ listActiveBindings: () => [
197
+ {
198
+ configKey: "GMAIL_SUPPORT_OAUTH_BINDING",
199
+ allowedHosts: ["gmail.googleapis.com"],
200
+ headerTemplate: "Authorization: Bearer [REDACTED:GMAIL_SUPPORT_OAUTH_BINDING]",
201
+ scope: "global",
202
+ scopeId: null,
203
+ active: true,
204
+ authKind: "oauth",
205
+ oauthProvider: "gmailSupport",
206
+ },
207
+ ],
208
+ },
209
+ () => undefined,
210
+ [],
211
+ async () => "gmail-access-token",
212
+ );
213
+ patchFetchWithCredentialBroker(await broker.resolveBindings({}));
214
+ const api = createApiRegistryClient([
215
+ {
216
+ slug: "gmailSupport",
217
+ kind: "graphql",
218
+ baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me/profile",
219
+ credential: {
220
+ configKey: "GMAIL_SUPPORT_OAUTH_BINDING",
221
+ headerTemplate: "Authorization: Bearer [REDACTED:GMAIL_SUPPORT_OAUTH_BINDING]",
222
+ },
223
+ },
224
+ ]);
225
+
226
+ await api.gmailSupport.graphql("query { me { id } }");
227
+
228
+ expect(authorization).toBe("Bearer gmail-access-token");
229
+ });
230
+
231
+ test("resolves config bindings via the config resolver even when oauthProvider metadata is present", async () => {
232
+ const oauthResolver = mock(async () => "should-not-be-used");
233
+ const broker = new CredentialBroker(
234
+ {
235
+ listActiveBindings: () => [
236
+ {
237
+ configKey: "VENDOR_CONFIG_KEY",
238
+ allowedHosts: ["api.vendor.test"],
239
+ headerTemplate: "Authorization: Bearer [REDACTED:VENDOR_CONFIG_KEY]",
240
+ scope: "global",
241
+ scopeId: null,
242
+ active: true,
243
+ authKind: "config",
244
+ oauthProvider: "vendorProvider",
245
+ },
246
+ ],
247
+ },
248
+ (key) => (key === "VENDOR_CONFIG_KEY" ? "vendor-config-value" : undefined),
249
+ [],
250
+ oauthResolver,
251
+ );
252
+
253
+ const bindings = await broker.resolveBindings({});
254
+
255
+ expect(oauthResolver).not.toHaveBeenCalled();
256
+ expect(bindings).toEqual([
257
+ {
258
+ configKey: "VENDOR_CONFIG_KEY",
259
+ allowedHosts: ["api.vendor.test"],
260
+ headerTemplate: "Authorization: Bearer [REDACTED:VENDOR_CONFIG_KEY]",
261
+ scope: "global",
262
+ scopeId: null,
263
+ active: true,
264
+ authKind: "config",
265
+ oauthProvider: "vendorProvider",
266
+ placeholder: "[REDACTED:VENDOR_CONFIG_KEY]",
267
+ value: "vendor-config-value",
268
+ },
269
+ ]);
270
+ });
271
+
145
272
  test("registers resolved broker config values with the scrubber", async () => {
146
273
  const bindingsConfig = upsertSwarmConfig({
147
274
  scope: "global",
@@ -49,9 +49,9 @@ describe("runtime secret egress", () => {
49
49
 
50
50
  describe("egress-substitution", () => {
51
51
  describe("buildEgressSecrets", () => {
52
- test("includes GITHUB_TOKEN when set in env", () => {
52
+ test("includes GITHUB_TOKEN when set in env", async () => {
53
53
  process.env.GITHUB_TOKEN = "ghp_test1234567890abcdef";
54
- const secrets = buildEgressSecrets();
54
+ const secrets = await buildEgressSecrets();
55
55
  expect(secrets).toEqual([
56
56
  {
57
57
  configKey: "GITHUB_TOKEN",
@@ -67,9 +67,9 @@ describe("egress-substitution", () => {
67
67
  ]);
68
68
  });
69
69
 
70
- test("returns empty array when GITHUB_TOKEN not set", () => {
70
+ test("returns empty array when GITHUB_TOKEN not set", async () => {
71
71
  delete process.env.GITHUB_TOKEN;
72
- const secrets = buildEgressSecrets();
72
+ const secrets = await buildEgressSecrets();
73
73
  expect(secrets).toEqual([]);
74
74
  });
75
75
  });