@desplega.ai/agent-swarm 1.165.0 → 1.166.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 (188) hide show
  1. package/dist/{acp-adapter-w6zws6me.js → acp-adapter-yzfxn8vj.js} +4 -4
  2. package/dist/{actions-jna0b3wm.js → actions-qz3m692w.js} +20 -24
  3. package/dist/{amp-adapter-0eve81pz.js → amp-adapter-j1xbmsqz.js} +4 -4
  4. package/dist/{app-75hm5cfa.js → app-43vrdmv8.js} +6 -7
  5. package/dist/{assistant-yzd51j33.js → assistant-ertffmka.js} +21 -25
  6. package/dist/{boot-reembed-z158xq32.js → boot-reembed-998jtpdg.js} +9 -12
  7. package/dist/{boot-reembed-2ex2mcsh.js → boot-reembed-s7k7c6v2.js} +10 -12
  8. package/dist/boot-scrub-sweep-nepsk4t1.js +211 -0
  9. package/dist/{boot-seeds-2bmnh18p.js → boot-seeds-vvzs1zhk.js} +8 -8
  10. package/dist/{claude-adapter-jbj1mycz.js → claude-adapter-2zvmekrp.js} +9 -11
  11. package/dist/{claude-managed-adapter-17ggz09t.js → claude-managed-adapter-s49wbk8t.js} +3 -3
  12. package/dist/{claude-managed-setup-ptm1nzm2.js → claude-managed-setup-cvhqq8qn.js} +4 -4
  13. package/dist/{claude-sdk-session-9re1g7r2.js → claude-sdk-session-kkbxtw5q.js} +2618 -2600
  14. package/dist/{cli-k6xx3pve.js → cli-0nhdwxd5.js} +4 -4
  15. package/dist/{cli-n9ehc28t.js → cli-0qjcsxgz.js} +4 -4
  16. package/dist/{cli-zn2v3t8v.js → cli-3ddybbbx.js} +1 -1
  17. package/dist/{cli-5anb0ev3.js → cli-3qn4r334.js} +71 -14
  18. package/dist/{cli-1kzfvd1z.js → cli-4r1sjfc4.js} +9 -5
  19. package/dist/{cli-ba66wyhy.js → cli-66zh26sp.js} +6 -6
  20. package/dist/{cli-9xb89xca.js → cli-68r1j4sg.js} +6 -6
  21. package/dist/{cli-xnzrzets.js → cli-8kgvmkak.js} +2 -2
  22. package/dist/{cli-28smfvbn.js → cli-8t229cmr.js} +5 -5
  23. package/dist/{cli-9866bd07.js → cli-aqcey741.js} +1 -1
  24. package/dist/{cli-90v942q1.js → cli-cpfmqz3n.js} +8 -8
  25. package/dist/{cli-50eg13rj.js → cli-df56tqdw.js} +459 -282
  26. package/dist/{cli-8gpbwy09.js → cli-dw2q9f7g.js} +5 -5
  27. package/dist/{cli-0n4fasz0.js → cli-ecknp7p1.js} +15 -15
  28. package/dist/{cli-xb3c6aat.js → cli-f2n17zhp.js} +1 -1
  29. package/dist/{cli-ftry7975.js → cli-feh3dkah.js} +1 -1
  30. package/dist/{cli-amwhs8h2.js → cli-fn697bnt.js} +2 -2
  31. package/dist/{cli-prt9emct.js → cli-gt8azyet.js} +3 -3
  32. package/dist/{cli-59jn5twa.js → cli-h3x613ze.js} +2 -2
  33. package/dist/{cli-mmzwwptd.js → cli-hf5s9pgf.js} +1 -1
  34. package/dist/cli-j2jbr8af.js +516 -0
  35. package/dist/{cli-wfajtmf7.js → cli-jhm6hzjn.js} +593 -10
  36. package/dist/{cli-xtgtyafv.js → cli-jvj6a0cv.js} +190 -439
  37. package/dist/{cli-hpryr6em.js → cli-mb75wpxf.js} +1 -1
  38. package/dist/{cli-420bdgw0.js → cli-mv2520nm.js} +1 -1
  39. package/dist/{cli-pjxnr1jn.js → cli-mwegs4af.js} +3 -3
  40. package/dist/{cli-5b2fjjzh.js → cli-p4ztb80h.js} +2 -2
  41. package/dist/{cli-e7t4snpa.js → cli-rggdq8wq.js} +4 -4
  42. package/dist/{cli-bdv39kgj.js → cli-rsy933yz.js} +5 -5
  43. package/dist/{cli-gh5yqzpe.js → cli-v2a8x45d.js} +2 -2
  44. package/dist/{cli-bns5c8k9.js → cli-vzc73wet.js} +43 -47
  45. package/dist/cli-wt7ccy4r.js +2654 -0
  46. package/dist/{cli-j0pdrmwg.js → cli-x7kpr72a.js} +3 -3
  47. package/dist/{cli-x7afk5wm.js → cli-xvc3q9br.js} +1 -1
  48. package/dist/{cli-9s3h836g.js → cli-xvdgasgy.js} +1 -1
  49. package/dist/{cli-zvckv750.js → cli-y59h2pk9.js} +2 -2
  50. package/dist/{cli-qdv8kgz7.js → cli-yshaazs1.js} +13884 -3241
  51. package/dist/{cli-nfygw434.js → cli-ysnyhws9.js} +2 -2
  52. package/dist/{cli-fw6vdcq5.js → cli-zp67qg5x.js} +34 -1
  53. package/dist/{cli-ma3zvh39.js → cli-zy7vwsne.js} +6066 -5829
  54. package/dist/cli.js +24 -20
  55. package/dist/{codex-adapter-3qnxshje.js → codex-adapter-dw1wxqt8.js} +7 -7
  56. package/dist/{codex-login-brkxhhdg.js → codex-login-terbdsqq.js} +2 -2
  57. package/dist/{codex-session-runner-612wnapp.js → codex-session-runner-3cgb8y1g.js} +7 -7
  58. package/dist/{commands-2ybfcxkj.js → commands-mhxam6z2.js} +6 -6
  59. package/dist/{cursor-adapter-p3jgdehv.js → cursor-adapter-2qda3st7.js} +3 -3
  60. package/dist/{db-f4ans0jj.js → db-sjks418k.js} +8 -6
  61. package/dist/{devin-adapter-dhzt01ff.js → devin-adapter-bffjj5n4.js} +2 -2
  62. package/dist/{dispatcher-pk182apd.js → dispatcher-1es759rn.js} +18 -22
  63. package/dist/{dsh-adapter-7twaeps1.js → dsh-adapter-bek4cyw9.js} +3 -3
  64. package/dist/{e2b-y8t3jzyz.js → e2b-nd50eccg.js} +1 -1
  65. package/dist/{event-bus-w7ac4n3c.js → event-bus-vnfthafg.js} +2 -2
  66. package/dist/{grok-adapter-yt9pab3k.js → grok-adapter-ar9cw9jg.js} +5 -5
  67. package/dist/{handlers-g2wnr4t9.js → handlers-1gkbhktr.js} +21 -25
  68. package/dist/{hook-f7jt9vnm.js → hook-ds2zxq9v.js} +14 -16
  69. package/dist/{hook-bbdpnkb4.js → hook-vxa3tcsy.js} +8 -10
  70. package/dist/{http-w40q4w94.js → http-3h21z9md.js} +247 -164
  71. package/dist/{index-056j5kzn.js → index-9b65j1m5.js} +8 -8
  72. package/dist/{index-9bfssf54.js → index-bn6cy8ps.js} +18 -22
  73. package/dist/{index-cj1r9q83.js → index-dkfxpkmm.js} +19 -23
  74. package/dist/{index-7n70jwbb.js → index-f8xnqrye.js} +19 -21
  75. package/dist/{index-fdk1t4d6.js → index-n0rxa4ca.js} +11962 -9900
  76. package/dist/{index-k8xc083e.js → index-n99fycq7.js} +18 -22
  77. package/dist/{keepalive-1tj10tf4.js → keepalive-3xspa9jw.js} +11 -8
  78. package/dist/{lead-st0bg7v4.js → lead-6qphc124.js} +34 -38
  79. package/dist/{maintenance-nz71zats.js → maintenance-0teem1bb.js} +7 -9
  80. package/dist/{node-f0ykebbv.js → node-9sg7ajby.js} +25 -7
  81. package/dist/{oauth-refresh-sweep-aefzwvvq.js → oauth-refresh-sweep-sh9q25xp.js} +10 -7
  82. package/dist/{onboard-kgpr9mbn.js → onboard-54qmnqxe.js} +2 -2
  83. package/dist/{opencode-adapter-0c8e70dk.js → opencode-adapter-wjfa2wvd.js} +2 -2
  84. package/dist/{otel-impl-x8zp4y5a.js → otel-impl-ycqzp2a1.js} +2 -2
  85. package/dist/{pi-mono-adapter-t280ah5c.js → pi-mono-adapter-e77e9zst.js} +4 -4
  86. package/dist/{pricing-refresh-9e0q7cwk.js → pricing-refresh-atnkngqs.js} +7 -7
  87. package/dist/{rbac-roles-kcp6zge2.js → rbac-roles-9pjphh9q.js} +6 -6
  88. package/dist/{rbac-roles-790j3ga8.js → rbac-roles-j78wdksm.js} +6 -6
  89. package/dist/{render-v2-tch4nefj.js → render-v2-pxjs49fx.js} +6 -7
  90. package/dist/{setup-rcm303ns.js → setup-ymej0xtx.js} +2 -2
  91. package/dist/{work-objects-ctg7t70w.js → work-objects-3nh4ymzb.js} +22 -26
  92. package/dist/{worker-5dp0xm55.js → worker-qa9fkz5s.js} +34 -38
  93. package/dist/{x-v3yw66zs.js → x-ma4gnfgy.js} +2 -2
  94. package/openapi.json +5 -2
  95. package/package.json +9 -5
  96. package/src/be/automation-preflight.ts +9 -4
  97. package/src/be/boot-scrub-sweep.ts +302 -0
  98. package/src/be/crypto/key-bootstrap.ts +1 -1
  99. package/src/be/db/approvals.ts +15 -1
  100. package/src/be/db/codex-oauth-device-flows.ts +40 -0
  101. package/src/be/db/config-secrets.ts +31 -0
  102. package/src/be/db/runtime.ts +4 -0
  103. package/src/be/db/script-runs.ts +15 -0
  104. package/src/be/db/tasks/write.ts +1 -1
  105. package/src/be/db-queries/mcp-oauth.ts +5 -0
  106. package/src/be/db-queries/oauth.ts +77 -0
  107. package/src/be/db.ts +162 -59
  108. package/src/be/events.ts +17 -1
  109. package/src/be/memory/index-content.ts +2 -2
  110. package/src/be/memory/providers/sqlite-store.ts +63 -11
  111. package/src/be/memory/types.ts +14 -0
  112. package/src/be/migrations/201_workflow_replay_columns.sql +10 -0
  113. package/src/be/migrations/202_codex_oauth_device_flows.sql +24 -0
  114. package/src/be/scripts/db.ts +28 -4
  115. package/src/be/scripts/source-secrets.ts +37 -0
  116. package/src/be/scrub-json.ts +32 -0
  117. package/src/be/sealed-json.ts +59 -0
  118. package/src/be/secret-registry.ts +131 -0
  119. package/src/be/workflow-replay.ts +77 -0
  120. package/src/http/codex-oauth-device.ts +8 -10
  121. package/src/http/db-query-bounded.ts +3 -0
  122. package/src/http/db-query-shared.ts +107 -15
  123. package/src/http/db-query.ts +10 -2
  124. package/src/http/index.ts +23 -6
  125. package/src/http/script-runs.ts +9 -1
  126. package/src/http/scripts.ts +15 -0
  127. package/src/http/workflows.ts +3 -2
  128. package/src/oauth/mcp-wrapper.ts +1 -1
  129. package/src/providers/acp-adapter.ts +25 -11
  130. package/src/providers/acp-target-catalog.ts +35 -1
  131. package/src/providers/acp-targets.ts +69 -3
  132. package/src/providers/amp-adapter.ts +1 -1
  133. package/src/providers/cursor-adapter.ts +1 -1
  134. package/src/script-workflows/supervisor.ts +11 -2
  135. package/src/scripts-runtime/eval-harness.ts +2 -12
  136. package/src/scripts-runtime/executors/quickjs-bundle.ts +152 -0
  137. package/src/scripts-runtime/executors/quickjs-prelude.ts +285 -0
  138. package/src/scripts-runtime/executors/quickjs-runner.ts +580 -0
  139. package/src/scripts-runtime/executors/quickjs-worker.ts +81 -0
  140. package/src/scripts-runtime/executors/quickjs.ts +196 -0
  141. package/src/scripts-runtime/executors/registry.ts +2 -0
  142. package/src/scripts-runtime/executors/sourcemap.ts +79 -0
  143. package/src/scripts-runtime/script-error-hints.ts +14 -0
  144. package/src/server.ts +3 -1
  145. package/src/tests/acp-adapter.test.ts +127 -2
  146. package/src/tests/boot-scrub-sweep.test.ts +390 -0
  147. package/src/tests/check-text-columns.test.ts +145 -0
  148. package/src/tests/claude-sdk-process.fixture.ts +13 -2
  149. package/src/tests/codex-oauth-device.test.ts +23 -37
  150. package/src/tests/db-query-denylist.test.ts +202 -0
  151. package/src/tests/db-task-read-contract.test.ts +1 -0
  152. package/src/tests/db-task-write-contract.test.ts +1 -0
  153. package/src/tests/entrypoint-acp-target-binary.test.ts +31 -1
  154. package/src/tests/events-scrub.test.ts +88 -0
  155. package/src/tests/fixtures/secret-corpus.ts +320 -0
  156. package/src/tests/script-connections.test.ts +5 -0
  157. package/src/tests/script-executor-conformance.test.ts +3 -0
  158. package/src/tests/script-executor-quickjs.test.ts +442 -0
  159. package/src/tests/script-executor-registry.test.ts +6 -1
  160. package/src/tests/script-runs-http.test.ts +88 -1
  161. package/src/tests/script-runs-scrub.test.ts +275 -0
  162. package/src/tests/scripts-http.test.ts +82 -1
  163. package/src/tests/scrubbed-text-12e.test.ts +289 -0
  164. package/src/tests/sealed-key-guard.test.ts +166 -0
  165. package/src/tests/secret-canary-sweep.test.ts +958 -0
  166. package/src/tests/secret-registry.test.ts +355 -0
  167. package/src/tests/secret-rules.test.ts +230 -0
  168. package/src/tests/secret-scrubber.test.ts +3 -2
  169. package/src/tests/workflow-replay.test.ts +518 -0
  170. package/src/tests/workflow-scrub.test.ts +242 -0
  171. package/src/tools/db-query.ts +1 -1
  172. package/src/tools/inject-learning.ts +1 -1
  173. package/src/tools/workflows/get-workflow-run.ts +3 -2
  174. package/src/types.ts +1 -1
  175. package/src/utils/secret-rules.generated.ts +2055 -0
  176. package/src/utils/secret-scrubber.ts +363 -29
  177. package/src/workflows/engine.ts +39 -4
  178. package/src/workflows/recovery.ts +10 -1
  179. package/src/workflows/resume.ts +62 -3
  180. package/src/workflows/retry-poller.ts +3 -0
  181. package/dist/boot-scrub-logs-246yn076.js +0 -99
  182. package/dist/cli-3a4m1aws.js +0 -572
  183. package/dist/cli-a87b04k8.js +0 -951
  184. package/dist/cli-cvk6yzw5.js +0 -40
  185. package/dist/cli-h1spv8v4.js +0 -427
  186. package/dist/cli-jqt9hrnz.js +0 -168
  187. package/dist/cli-yhmwmv0g.js +0 -9281
  188. package/src/be/boot-scrub-logs.ts +0 -132
@@ -1,951 +0,0 @@
1
- import {
2
- decryptSecret,
3
- encryptSecret,
4
- getDbClient,
5
- getEncryptionKey,
6
- init_crypto,
7
- init_date_utils,
8
- init_db,
9
- normalizeDateRequired
10
- } from "./cli-qdv8kgz7.js";
11
- import {
12
- init_secret_scrubber,
13
- registerVolatileSecret,
14
- scrubSecrets
15
- } from "./cli-h1spv8v4.js";
16
-
17
- // src/be/db-queries/oauth.ts
18
- init_crypto();
19
- init_date_utils();
20
- init_db();
21
- function parseScopeList(value) {
22
- try {
23
- const parsed = JSON.parse(value);
24
- if (Array.isArray(parsed)) {
25
- return parsed.filter((item) => typeof item === "string");
26
- }
27
- } catch {}
28
- return value.split(",").map((scope) => scope.trim()).filter(Boolean);
29
- }
30
- function storeScopeList(value) {
31
- return JSON.stringify(Array.isArray(value) ? value : parseScopeList(value));
32
- }
33
- function parseObject(value) {
34
- try {
35
- const parsed = JSON.parse(value || "{}");
36
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
37
- } catch {
38
- return {};
39
- }
40
- }
41
- function storageMetadata(metadata) {
42
- const parsed = parseObject(metadata);
43
- const extraParams = parsed.extraParams && typeof parsed.extraParams === "object" && !Array.isArray(parsed.extraParams) ? parsed.extraParams : null;
44
- const tokenAuthStyle = parsed.tokenAuthStyle === "basic" ? "basic" : "body";
45
- const tokenBodyFormat = parsed.tokenBodyFormat === "json" ? "json" : "form";
46
- const revocationUrl = typeof parsed.revocationUrl === "string" ? parsed.revocationUrl : null;
47
- const hasLiftedFields = [
48
- "extraParams",
49
- "tokenAuthStyle",
50
- "tokenBodyFormat",
51
- "revocationUrl"
52
- ].some((key) => Object.hasOwn(parsed, key));
53
- if (hasLiftedFields) {
54
- delete parsed.extraParams;
55
- delete parsed.tokenAuthStyle;
56
- delete parsed.tokenBodyFormat;
57
- delete parsed.revocationUrl;
58
- }
59
- return {
60
- metadata: hasLiftedFields ? JSON.stringify(parsed) : metadata,
61
- extraParamsJson: extraParams ? JSON.stringify(extraParams) : null,
62
- tokenAuthStyle,
63
- tokenBodyFormat,
64
- revocationUrl
65
- };
66
- }
67
- function normalizeOAuthApp(row) {
68
- const encrypted = row.clientSecretEncrypted === 1;
69
- return {
70
- ...row,
71
- clientSecret: row.clientSecret == null ? "" : encrypted ? decryptSecret(row.clientSecret, getEncryptionKey()) : row.clientSecret,
72
- clientSecretEncrypted: encrypted,
73
- scopes: parseScopeList(row.scopes).join(","),
74
- requiresRefreshTokenRotation: row.requiresRefreshTokenRotation === 1,
75
- createdAt: normalizeDateRequired(row.createdAt),
76
- updatedAt: normalizeDateRequired(row.updatedAt)
77
- };
78
- }
79
- function normalizeAuthorization(row) {
80
- const encrypted = row.tokensEncrypted === 1;
81
- const key = encrypted ? getEncryptionKey() : null;
82
- return {
83
- ...row,
84
- accessToken: key ? decryptSecret(row.accessToken, key) : row.accessToken,
85
- refreshToken: row.refreshToken == null ? null : key ? decryptSecret(row.refreshToken, key) : row.refreshToken,
86
- tokensEncrypted: encrypted,
87
- createdAt: normalizeDateRequired(row.createdAt),
88
- updatedAt: normalizeDateRequired(row.updatedAt)
89
- };
90
- }
91
- async function rawOAuthAppByProvider(provider) {
92
- return await getDbClient().get(`SELECT * FROM oauth_apps
93
- WHERE provider = ? AND mcpServerId IS NULL
94
- ORDER BY createdAt ASC, id ASC
95
- LIMIT 1`, [provider]);
96
- }
97
- async function rawOAuthAppById(id) {
98
- return await getDbClient().get("SELECT * FROM oauth_apps WHERE id = ? AND mcpServerId IS NULL", [id]);
99
- }
100
- async function rawDefaultAuthorizationForApp(appId) {
101
- return await getDbClient().get("SELECT * FROM oauth_authorizations WHERE appId = ? AND label = 'default'", [appId]);
102
- }
103
- async function rawDefaultAuthorizationForProvider(provider) {
104
- return await getDbClient().get(`SELECT z.*
105
- FROM oauth_authorizations z
106
- JOIN oauth_apps a ON a.id = z.appId
107
- WHERE a.provider = ? AND a.mcpServerId IS NULL AND z.label = 'default'
108
- ORDER BY a.createdAt ASC, a.id ASC
109
- LIMIT 1`, [provider]);
110
- }
111
- async function getDefaultAuthorizationIdForProvider(provider) {
112
- return (await rawDefaultAuthorizationForProvider(provider))?.id ?? null;
113
- }
114
- async function getOAuthApp(provider) {
115
- const row = await rawOAuthAppByProvider(provider);
116
- return row ? normalizeOAuthApp(row) : null;
117
- }
118
- async function getOAuthAppById(id) {
119
- const row = await getDbClient().get("SELECT * FROM oauth_apps WHERE id = ?", [id]);
120
- return row ? normalizeOAuthApp(row) : null;
121
- }
122
- async function getOAuthAppIdByProvider(provider) {
123
- return (await rawOAuthAppByProvider(provider))?.id ?? null;
124
- }
125
- async function writeOAuthApp(existing, provider, data) {
126
- const metadataProvided = data.metadata !== undefined;
127
- const lifted = metadataProvided ? storageMetadata(data.metadata) : null;
128
- const encryptedSecret = encryptSecret(data.clientSecret, getEncryptionKey());
129
- const scopeSeparator = data.scopeSeparator ?? existing?.scopeSeparator ?? (provider === "linear" ? "," : " ");
130
- const rotation = data.requiresRefreshTokenRotation ?? (existing ? existing.requiresRefreshTokenRotation === 1 : provider === "jira");
131
- const tokenAuthStyle = data.tokenAuthStyle ?? lifted?.tokenAuthStyle ?? existing?.tokenAuthStyle ?? "body";
132
- const tokenBodyFormat = data.tokenBodyFormat ?? lifted?.tokenBodyFormat ?? existing?.tokenBodyFormat ?? "form";
133
- const extraParamsJson = data.extraParams !== undefined ? data.extraParams == null ? null : JSON.stringify(data.extraParams) : lifted ? lifted.extraParamsJson : existing?.extraParamsJson ?? null;
134
- const revocationUrl = data.revocationUrl !== undefined ? data.revocationUrl : lifted ? lifted.revocationUrl : existing?.revocationUrl ?? null;
135
- if (existing) {
136
- await getDbClient().run(`UPDATE oauth_apps SET
137
- displayName = ?, clientId = ?, clientSecret = ?, clientSecretEncrypted = 1,
138
- authorizeUrl = ?, tokenUrl = ?, revocationUrl = ?, userinfoUrl = ?,
139
- redirectUri = ?, scopes = ?, scopeSeparator = ?, tokenAuthStyle = ?,
140
- tokenBodyFormat = ?, requiresRefreshTokenRotation = ?, extraParamsJson = ?,
141
- source = ?, metadata = ?, updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
142
- WHERE id = ?`, [
143
- data.displayName !== undefined ? data.displayName : existing.displayName,
144
- data.clientId,
145
- encryptedSecret,
146
- data.authorizeUrl,
147
- data.tokenUrl,
148
- revocationUrl,
149
- data.userinfoUrl !== undefined ? data.userinfoUrl : existing.userinfoUrl,
150
- data.redirectUri,
151
- storeScopeList(data.scopes),
152
- scopeSeparator,
153
- tokenAuthStyle,
154
- tokenBodyFormat,
155
- rotation ? 1 : 0,
156
- extraParamsJson,
157
- data.source ?? existing.source,
158
- lifted?.metadata ?? existing.metadata,
159
- existing.id
160
- ]);
161
- return existing.id;
162
- }
163
- const inserted = await getDbClient().get(`INSERT INTO oauth_apps (
164
- provider, displayName, clientId, clientSecret, clientSecretEncrypted,
165
- authorizeUrl, tokenUrl, revocationUrl, userinfoUrl, redirectUri, scopes,
166
- scopeSeparator, tokenAuthStyle, tokenBodyFormat,
167
- requiresRefreshTokenRotation, extraParamsJson, source, metadata
168
- ) VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
169
- RETURNING id`, [
170
- provider,
171
- data.displayName ?? null,
172
- data.clientId,
173
- encryptedSecret,
174
- data.authorizeUrl,
175
- data.tokenUrl,
176
- revocationUrl,
177
- data.userinfoUrl ?? null,
178
- data.redirectUri,
179
- storeScopeList(data.scopes),
180
- scopeSeparator,
181
- tokenAuthStyle,
182
- tokenBodyFormat,
183
- rotation ? 1 : 0,
184
- extraParamsJson,
185
- data.source ?? "manual",
186
- lifted?.metadata ?? "{}"
187
- ]);
188
- return inserted.id;
189
- }
190
- async function createOAuthApp(provider, data) {
191
- return await writeOAuthApp(null, provider, data);
192
- }
193
- async function updateOAuthAppById(id, data) {
194
- const existing = await rawOAuthAppById(id);
195
- if (!existing) {
196
- throw new Error(`OAuth app ${id} not found.`);
197
- }
198
- await writeOAuthApp(existing, existing.provider, data);
199
- }
200
- async function upsertOAuthApp(provider, data) {
201
- await writeOAuthApp(await rawOAuthAppByProvider(provider), provider, data);
202
- }
203
- async function listAuthorizationsForApp(appId) {
204
- const rows = await getDbClient().query("SELECT * FROM oauth_authorizations WHERE appId = ? ORDER BY createdAt ASC, id ASC", [appId]);
205
- return rows.map(normalizeAuthorization);
206
- }
207
- async function getAuthorizationById(id) {
208
- const row = await getDbClient().get("SELECT * FROM oauth_authorizations WHERE id = ?", [id]);
209
- return row ? normalizeAuthorization(row) : null;
210
- }
211
- async function upsertAuthorization(data) {
212
- const label = data.label ?? "default";
213
- const existing = data.id ? await getAuthorizationById(data.id) : (await listAuthorizationsForApp(data.appId)).find((row) => row.label === label);
214
- const key = getEncryptionKey();
215
- const accessToken = encryptSecret(data.accessToken, key);
216
- const refreshToken = data.refreshToken === undefined ? undefined : data.refreshToken == null ? null : encryptSecret(data.refreshToken, key);
217
- if (existing) {
218
- await getDbClient().run(`UPDATE oauth_authorizations SET
219
- userId = ?, accountEmail = ?, identityJson = ?, accessToken = ?,
220
- refreshToken = ?, tokenType = ?, expiresAt = ?, scope = ?,
221
- tokensEncrypted = 1, tokenVersion = tokenVersion + 1, status = ?,
222
- lastErrorMessage = ?, lastRefreshedAt = ?, connectedByUserId = ?,
223
- updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
224
- WHERE id = ?`, [
225
- data.userId !== undefined ? data.userId : existing.userId,
226
- data.accountEmail !== undefined ? data.accountEmail : existing.accountEmail,
227
- data.identityJson !== undefined ? data.identityJson : existing.identityJson,
228
- accessToken,
229
- refreshToken === undefined ? existing.refreshToken == null ? null : encryptSecret(existing.refreshToken, key) : refreshToken,
230
- data.tokenType ?? existing.tokenType,
231
- data.expiresAt !== undefined ? data.expiresAt : existing.expiresAt,
232
- data.scope !== undefined ? data.scope : existing.scope,
233
- data.status ?? existing.status,
234
- data.lastErrorMessage !== undefined ? data.lastErrorMessage : existing.lastErrorMessage,
235
- data.lastRefreshedAt !== undefined ? data.lastRefreshedAt : existing.lastRefreshedAt,
236
- data.connectedByUserId !== undefined ? data.connectedByUserId : existing.connectedByUserId,
237
- existing.id
238
- ]);
239
- return await getAuthorizationById(existing.id);
240
- }
241
- const id = data.id ?? crypto.randomUUID();
242
- await getDbClient().run(`INSERT INTO oauth_authorizations (
243
- id, appId, label, userId, accountEmail, identityJson,
244
- accessToken, refreshToken, tokenType, expiresAt, scope,
245
- tokensEncrypted, tokenVersion, status, lastErrorMessage,
246
- lastRefreshedAt, connectedByUserId
247
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 1, ?, ?, ?, ?)`, [
248
- id,
249
- data.appId,
250
- label,
251
- data.userId ?? null,
252
- data.accountEmail ?? null,
253
- data.identityJson ?? null,
254
- accessToken,
255
- refreshToken ?? null,
256
- data.tokenType ?? "Bearer",
257
- data.expiresAt ?? null,
258
- data.scope ?? null,
259
- data.status ?? "active",
260
- data.lastErrorMessage ?? null,
261
- data.lastRefreshedAt ?? null,
262
- data.connectedByUserId ?? null
263
- ]);
264
- return await getAuthorizationById(id);
265
- }
266
- async function updateAuthorizationTokens(id, data) {
267
- const existing = await getAuthorizationById(id);
268
- if (!existing)
269
- return null;
270
- const expectedVersion = data.expectedTokenVersion ?? existing.tokenVersion;
271
- const key = getEncryptionKey();
272
- const encryptedRefresh = data.refreshToken === undefined ? existing.refreshToken == null ? null : encryptSecret(existing.refreshToken, key) : data.refreshToken == null ? null : encryptSecret(data.refreshToken, key);
273
- const result = await getDbClient().run(`UPDATE oauth_authorizations SET
274
- accessToken = ?, refreshToken = ?, expiresAt = ?, scope = ?,
275
- tokensEncrypted = 1, tokenVersion = tokenVersion + 1,
276
- status = 'active', lastErrorMessage = NULL,
277
- lastRefreshedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
278
- updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
279
- WHERE id = ? AND tokenVersion = ?`, [
280
- encryptSecret(data.accessToken, key),
281
- encryptedRefresh,
282
- data.expiresAt !== undefined ? data.expiresAt : existing.expiresAt,
283
- data.scope !== undefined ? data.scope : existing.scope,
284
- id,
285
- expectedVersion
286
- ]);
287
- return result.changes === 1 ? await getAuthorizationById(id) : null;
288
- }
289
- async function updateAuthorizationIdentity(id, data) {
290
- await getDbClient().run(`UPDATE oauth_authorizations SET
291
- accountEmail = ?, identityJson = ?,
292
- updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
293
- WHERE id = ?`, [data.accountEmail ?? null, data.identityJson ?? null, id]);
294
- }
295
- async function deleteAuthorizationById(id) {
296
- const result = await getDbClient().run("DELETE FROM oauth_authorizations WHERE id = ?", [id]);
297
- return result.changes > 0;
298
- }
299
- async function deleteAuthorizationsForApp(appId) {
300
- return (await getDbClient().run("DELETE FROM oauth_authorizations WHERE appId = ?", [appId])).changes;
301
- }
302
- var OAUTH_PENDING_TTL_MS = 10 * 60 * 1000;
303
- async function createOAuthPending(input) {
304
- await getDbClient().run(`INSERT INTO oauth_pending (
305
- state, appId, label, flow, codeVerifier, nonce,
306
- redirectUri, finalRedirect, userId, contextJson
307
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
308
- input.state,
309
- input.appId,
310
- input.label ?? "default",
311
- input.flow ?? "generic",
312
- encryptSecret(input.codeVerifier, getEncryptionKey()),
313
- input.nonce ?? null,
314
- input.redirectUri,
315
- input.finalRedirect ?? null,
316
- input.userId ?? null,
317
- input.contextJson ?? "{}"
318
- ]);
319
- }
320
- async function consumeOAuthPending(state) {
321
- return await getDbClient().transaction(async (tx) => {
322
- const row = await tx.get(`SELECT state, appId, label, flow, codeVerifier, nonce, redirectUri,
323
- finalRedirect, userId, contextJson, createdAt
324
- FROM oauth_pending
325
- WHERE state = ? AND flow IN ('generic', 'tracker')`, [state]);
326
- if (!row)
327
- return null;
328
- await tx.run("DELETE FROM oauth_pending WHERE state = ?", [state]);
329
- const createdAt = normalizeDateRequired(row.createdAt);
330
- const createdMs = new Date(createdAt).getTime();
331
- if (Number.isNaN(createdMs) || Date.now() - createdMs > OAUTH_PENDING_TTL_MS) {
332
- return null;
333
- }
334
- return {
335
- ...row,
336
- codeVerifier: decryptSecret(row.codeVerifier, getEncryptionKey()),
337
- createdAt
338
- };
339
- });
340
- }
341
- async function gcOAuthPending(olderThanMs = 10 * 60 * 1000) {
342
- const cutoff = new Date(Date.now() - olderThanMs).toISOString();
343
- const result = await getDbClient().run("DELETE FROM oauth_pending WHERE flow IN ('generic', 'tracker') AND createdAt < ?", [cutoff]);
344
- return result.changes;
345
- }
346
- async function markAuthorizationRefreshFailed(id, message) {
347
- const result = await getDbClient().run(`UPDATE oauth_authorizations SET
348
- status = 'refresh-failed', lastErrorMessage = ?,
349
- updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
350
- WHERE id = ? AND status != 'revoked'`, [message, id]);
351
- return result.changes === 1 ? await getAuthorizationById(id) : null;
352
- }
353
- async function getOAuthTokens(provider) {
354
- const row = await rawDefaultAuthorizationForProvider(provider);
355
- if (!row)
356
- return null;
357
- const authorization = normalizeAuthorization(row);
358
- if (authorization.status === "revoked")
359
- return null;
360
- return {
361
- id: authorization.id,
362
- provider,
363
- accessToken: authorization.accessToken,
364
- refreshToken: authorization.refreshToken,
365
- expiresAt: authorization.expiresAt ?? "",
366
- scope: authorization.scope,
367
- tokenVersion: authorization.tokenVersion,
368
- createdAt: authorization.createdAt,
369
- updatedAt: authorization.updatedAt
370
- };
371
- }
372
- async function listAuthorizationSweepRows() {
373
- const rows = await getDbClient().query(`SELECT z.id AS authorizationId,
374
- z.appId AS appId,
375
- a.provider AS provider,
376
- z.label AS label,
377
- z.status AS status,
378
- CASE WHEN z.refreshToken IS NOT NULL AND z.refreshToken != '' THEN 1 ELSE 0 END AS hasRefreshToken,
379
- COALESCE(z.expiresAt, '') AS expiresAt,
380
- z.updatedAt AS updatedAt
381
- FROM oauth_authorizations z
382
- JOIN oauth_apps a ON a.id = z.appId
383
- WHERE a.mcpServerId IS NULL
384
- ORDER BY a.provider ASC, z.label ASC`);
385
- return rows.map((row) => ({
386
- authorizationId: row.authorizationId,
387
- appId: row.appId,
388
- provider: row.provider,
389
- label: row.label,
390
- status: row.status,
391
- hasRefreshToken: row.hasRefreshToken === 1,
392
- expiresAt: normalizeDateRequired(row.expiresAt),
393
- updatedAt: normalizeDateRequired(row.updatedAt)
394
- }));
395
- }
396
- async function listKeepAliveAuthorizations() {
397
- const rows = await getDbClient().query(`SELECT z.id AS authorizationId,
398
- z.appId AS appId,
399
- a.provider AS provider,
400
- z.label AS label,
401
- a.displayName AS displayName
402
- FROM oauth_authorizations z
403
- JOIN oauth_apps a ON a.id = z.appId
404
- WHERE a.mcpServerId IS NULL
405
- AND z.status = 'active'
406
- AND z.refreshToken IS NOT NULL AND z.refreshToken != ''
407
- AND (
408
- a.requiresRefreshTokenRotation = 1
409
- OR (json_valid(a.metadata) = 1 AND json_extract(a.metadata, '$.keepAlive') IN (1, 'true'))
410
- )
411
- ORDER BY a.provider ASC, z.label ASC`);
412
- return rows.map((row) => ({
413
- authorizationId: row.authorizationId,
414
- appId: row.appId,
415
- provider: row.provider,
416
- label: row.label,
417
- displayName: row.displayName
418
- }));
419
- }
420
- async function deleteOAuthTokens(provider) {
421
- const app = await rawOAuthAppByProvider(provider);
422
- if (!app)
423
- return;
424
- const existing = await rawDefaultAuthorizationForApp(app.id);
425
- if (!existing)
426
- return;
427
- await getDbClient().run(`UPDATE oauth_authorizations SET
428
- accessToken = ?, refreshToken = NULL, expiresAt = NULL, scope = NULL,
429
- tokensEncrypted = 1, tokenVersion = tokenVersion + 1,
430
- status = 'revoked', lastErrorMessage = NULL, lastRefreshedAt = NULL,
431
- updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
432
- WHERE id = ?`, [encryptSecret("", getEncryptionKey()), existing.id]);
433
- }
434
- async function acquireOAuthRefreshLock(lockKey, ttlMs) {
435
- const owner = crypto.randomUUID();
436
- const now = Date.now();
437
- const expiresAt = new Date(now + ttlMs).toISOString();
438
- const nowIso = new Date(now).toISOString();
439
- await getDbClient().run(`INSERT INTO oauth_refresh_locks (lockKey, owner, expiresAt, createdAt, updatedAt)
440
- VALUES (?, ?, ?, ?, ?)
441
- ON CONFLICT(lockKey) DO UPDATE SET
442
- owner = excluded.owner,
443
- expiresAt = excluded.expiresAt,
444
- updatedAt = excluded.updatedAt
445
- WHERE oauth_refresh_locks.expiresAt <= ?`, [lockKey, owner, expiresAt, nowIso, nowIso, nowIso]);
446
- const row = await getDbClient().get("SELECT owner FROM oauth_refresh_locks WHERE lockKey = ?", [lockKey]);
447
- return row?.owner === owner ? owner : null;
448
- }
449
- async function releaseOAuthRefreshLock(lockKey, owner) {
450
- await getDbClient().run("DELETE FROM oauth_refresh_locks WHERE lockKey = ? AND owner = ?", [
451
- lockKey,
452
- owner
453
- ]);
454
- }
455
-
456
- // src/oauth/ensure-token.ts
457
- init_secret_scrubber();
458
-
459
- // node_modules/oauth4webapi/build/index.js
460
- var USER_AGENT;
461
- if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
462
- const NAME = "oauth4webapi";
463
- const VERSION = "v3.8.5";
464
- USER_AGENT = `${NAME}/${VERSION}`;
465
- }
466
- var ERR_INVALID_ARG_VALUE = "ERR_INVALID_ARG_VALUE";
467
- var ERR_INVALID_ARG_TYPE = "ERR_INVALID_ARG_TYPE";
468
- function CodedTypeError(message, code, cause) {
469
- const err = new TypeError(message, { cause });
470
- Object.assign(err, { code });
471
- return err;
472
- }
473
- var allowInsecureRequests = Symbol();
474
- var clockSkew = Symbol();
475
- var clockTolerance = Symbol();
476
- var customFetch = Symbol();
477
- var modifyAssertion = Symbol();
478
- var jweDecrypt = Symbol();
479
- var jwksCache = Symbol();
480
- var encoder = new TextEncoder;
481
- var decoder = new TextDecoder;
482
- function buf(input) {
483
- if (typeof input === "string") {
484
- return encoder.encode(input);
485
- }
486
- return decoder.decode(input);
487
- }
488
- var encodeBase64Url;
489
- if (Uint8Array.prototype.toBase64) {
490
- encodeBase64Url = (input) => {
491
- if (input instanceof ArrayBuffer) {
492
- input = new Uint8Array(input);
493
- }
494
- return input.toBase64({ alphabet: "base64url", omitPadding: true });
495
- };
496
- } else {
497
- const CHUNK_SIZE = 32768;
498
- encodeBase64Url = (input) => {
499
- if (input instanceof ArrayBuffer) {
500
- input = new Uint8Array(input);
501
- }
502
- const arr = [];
503
- for (let i = 0;i < input.byteLength; i += CHUNK_SIZE) {
504
- arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
505
- }
506
- return btoa(arr.join("")).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
507
- };
508
- }
509
- var decodeBase64Url;
510
- if (Uint8Array.fromBase64) {
511
- decodeBase64Url = (input) => {
512
- try {
513
- return Uint8Array.fromBase64(input, { alphabet: "base64url" });
514
- } catch (cause) {
515
- throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
516
- }
517
- };
518
- } else {
519
- decodeBase64Url = (input) => {
520
- try {
521
- const binary = atob(input.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""));
522
- const bytes = new Uint8Array(binary.length);
523
- for (let i = 0;i < binary.length; i++) {
524
- bytes[i] = binary.charCodeAt(i);
525
- }
526
- return bytes;
527
- } catch (cause) {
528
- throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
529
- }
530
- };
531
- }
532
- function b64u(input) {
533
- if (typeof input === "string") {
534
- return decodeBase64Url(input);
535
- }
536
- return encodeBase64Url(input);
537
- }
538
- class OperationProcessingError extends Error {
539
- code;
540
- constructor(message, options) {
541
- super(message, options);
542
- this.name = this.constructor.name;
543
- if (options?.code) {
544
- this.code = options?.code;
545
- }
546
- Error.captureStackTrace?.(this, this.constructor);
547
- }
548
- }
549
- function OPE(message, code, cause) {
550
- return new OperationProcessingError(message, { code, cause });
551
- }
552
- function assertString(input, it, code, cause) {
553
- try {
554
- if (typeof input !== "string") {
555
- throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE, cause);
556
- }
557
- if (input.length === 0) {
558
- throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE, cause);
559
- }
560
- } catch (err) {
561
- if (code) {
562
- throw OPE(err.message, code, cause);
563
- }
564
- throw err;
565
- }
566
- }
567
- function randomBytes() {
568
- return b64u(crypto.getRandomValues(new Uint8Array(32)));
569
- }
570
- function generateRandomCodeVerifier() {
571
- return randomBytes();
572
- }
573
- function generateRandomState() {
574
- return randomBytes();
575
- }
576
- async function calculatePKCECodeChallenge(codeVerifier) {
577
- assertString(codeVerifier, "codeVerifier");
578
- return b64u(await crypto.subtle.digest("SHA-256", buf(codeVerifier)));
579
- }
580
- var URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) => {
581
- try {
582
- return new URL(url, base);
583
- } catch {
584
- return null;
585
- }
586
- };
587
- var tokenMatch = "[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+";
588
- var token68Match = "[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}";
589
- var quotedMatch = '"((?:[^"\\\\]|\\\\[\\s\\S])*)"';
590
- var quotedParamMatcher = "(" + tokenMatch + ")\\s*=\\s*" + quotedMatch;
591
- var paramMatcher = "(" + tokenMatch + ")\\s*=\\s*(" + tokenMatch + ")";
592
- var schemeRE = new RegExp("^[,\\s]*(" + tokenMatch + ")");
593
- var quotedParamRE = new RegExp("^[,\\s]*" + quotedParamMatcher + "[,\\s]*(.*)");
594
- var unquotedParamRE = new RegExp("^[,\\s]*" + paramMatcher + "[,\\s]*(.*)");
595
- var token68ParamRE = new RegExp("^(" + token68Match + ")(?:$|[,\\s])(.*)");
596
- var skipSubjectCheck = Symbol();
597
- var idTokenClaims = new WeakMap;
598
- var jwtRefs = new WeakMap;
599
- var branded = new WeakSet;
600
- var nopkce = Symbol();
601
- var expectNoNonce = Symbol();
602
- var skipAuthTimeCheck = Symbol();
603
- var skipStateCheck = Symbol();
604
- var expectNoState = Symbol();
605
- var _nodiscoverycheck = Symbol();
606
- var _expectedIssuer = Symbol();
607
-
608
- // src/oauth/wrapper.ts
609
- async function buildAuthorizationUrl(config, options = {}) {
610
- const state = generateRandomState();
611
- const codeVerifier = generateRandomCodeVerifier();
612
- const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
613
- const appId = options.appId ?? await getOAuthAppIdByProvider(config.provider);
614
- if (!appId) {
615
- throw new Error(`OAuth app ${config.provider} is not configured`);
616
- }
617
- await createOAuthPending({
618
- state,
619
- appId,
620
- label: options.label ?? "default",
621
- flow: options.flow ?? "generic",
622
- codeVerifier,
623
- nonce: options.nonce ?? null,
624
- redirectUri: config.redirectUri,
625
- finalRedirect: options.finalRedirect ?? null,
626
- userId: options.userId ?? null
627
- });
628
- const url = new URL(config.authorizeUrl);
629
- url.searchParams.set("client_id", config.clientId);
630
- url.searchParams.set("redirect_uri", config.redirectUri);
631
- url.searchParams.set("response_type", "code");
632
- url.searchParams.set("scope", config.scopes.join(config.scopeSeparator ?? ","));
633
- url.searchParams.set("state", state);
634
- url.searchParams.set("code_challenge", codeChallenge);
635
- url.searchParams.set("code_challenge_method", "S256");
636
- if (config.extraParams) {
637
- for (const [key, value] of Object.entries(config.extraParams)) {
638
- url.searchParams.set(key, value);
639
- }
640
- }
641
- return { url: url.toString(), state, codeVerifier };
642
- }
643
- function tokenRequestInit(config, params) {
644
- const useBasic = config.tokenAuthStyle === "basic";
645
- const bodyParams = useBasic ? params : { ...params, client_id: config.clientId, client_secret: config.clientSecret };
646
- const headers = { Accept: "application/json" };
647
- if (useBasic) {
648
- headers.Authorization = `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`;
649
- }
650
- if (config.tokenBodyFormat === "json") {
651
- headers["Content-Type"] = "application/json";
652
- return { headers, body: JSON.stringify(bodyParams) };
653
- }
654
- headers["Content-Type"] = "application/x-www-form-urlencoded";
655
- return { headers, body: new URLSearchParams(bodyParams).toString() };
656
- }
657
- async function exchangeAuthorizationCode(config, params) {
658
- const response = await fetch(config.tokenUrl, {
659
- method: "POST",
660
- ...tokenRequestInit(config, {
661
- grant_type: "authorization_code",
662
- redirect_uri: params.redirectUri,
663
- code: params.code,
664
- code_verifier: params.codeVerifier
665
- })
666
- });
667
- if (!response.ok) {
668
- const errorText = await response.text();
669
- throw new Error(`Token exchange failed (${response.status}): ${errorText}`);
670
- }
671
- const data = await response.json();
672
- return {
673
- accessToken: data.access_token,
674
- refreshToken: data.refresh_token,
675
- expiresIn: data.expires_in,
676
- scope: data.scope,
677
- tokenType: data.token_type,
678
- idToken: data.id_token
679
- };
680
- }
681
- async function performTokenRefreshRequest(config, refreshToken) {
682
- const response = await fetch(config.tokenUrl, {
683
- method: "POST",
684
- ...tokenRequestInit(config, {
685
- grant_type: "refresh_token",
686
- refresh_token: refreshToken
687
- })
688
- });
689
- if (!response.ok) {
690
- const errorText = await response.text();
691
- throw new Error(`Token refresh failed (${response.status}): ${errorText}`);
692
- }
693
- const data = await response.json();
694
- if (typeof data.access_token !== "string" || data.access_token.length === 0) {
695
- throw new Error(`Token refresh failed: ${config.provider} response missing access_token`);
696
- }
697
- if (config.requiresRefreshTokenRotation && (typeof data.refresh_token !== "string" || data.refresh_token.length === 0)) {
698
- throw new Error(`Token refresh failed: ${config.provider} response did not include a rotated refresh_token`);
699
- }
700
- const expiresAt = data.expires_in ? new Date(Date.now() + data.expires_in * 1000).toISOString() : null;
701
- return {
702
- accessToken: data.access_token,
703
- refreshToken: data.refresh_token,
704
- expiresIn: data.expires_in,
705
- scope: data.scope,
706
- expiresAt
707
- };
708
- }
709
-
710
- // src/oauth/ensure-token.ts
711
- function parseMetadata(metadataJson) {
712
- try {
713
- const parsed = JSON.parse(metadataJson || "{}");
714
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
715
- } catch {
716
- return {};
717
- }
718
- }
719
- function stringRecord(value) {
720
- if (!value || typeof value !== "object" || Array.isArray(value))
721
- return;
722
- return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, String(item)]));
723
- }
724
- function oauthAppRowToProviderConfig(app) {
725
- const metadata = parseMetadata(app.metadata);
726
- const liftedExtraParams = stringRecord(parseMetadata(app.extraParamsJson));
727
- const extraParams = {
728
- ...typeof metadata.actor === "string" ? { actor: metadata.actor } : {},
729
- ...liftedExtraParams
730
- };
731
- return {
732
- provider: app.provider,
733
- clientId: app.clientId,
734
- clientSecret: app.clientSecret,
735
- authorizeUrl: app.authorizeUrl,
736
- tokenUrl: app.tokenUrl,
737
- redirectUri: app.redirectUri,
738
- scopes: app.scopes.split(",").map((scope) => scope.trim()).filter(Boolean),
739
- extraParams: Object.keys(extraParams).length > 0 ? extraParams : undefined,
740
- scopeSeparator: app.scopeSeparator,
741
- tokenAuthStyle: app.tokenAuthStyle,
742
- tokenBodyFormat: app.tokenBodyFormat,
743
- requiresRefreshTokenRotation: app.requiresRefreshTokenRotation
744
- };
745
- }
746
-
747
- class OAuthRefreshError extends Error {
748
- authorizationId;
749
- appId;
750
- reason;
751
- authorizationLabel;
752
- constructor(authorizationId, appId, reason, authorizationLabel, message) {
753
- super(message ?? `OAuth authorization '${authorizationLabel}' refresh failed (${reason})`);
754
- this.authorizationId = authorizationId;
755
- this.appId = appId;
756
- this.reason = reason;
757
- this.authorizationLabel = authorizationLabel;
758
- this.name = "OAuthRefreshError";
759
- }
760
- }
761
- function authorizationLabelFor(app, authorization) {
762
- const base = app.displayName?.trim() || app.provider;
763
- return authorization.label && authorization.label !== "default" ? `${base} (${authorization.label})` : base;
764
- }
765
- var refreshLocks = new Map;
766
- var REFRESH_LOCK_TTL_MS = 2 * 60 * 1000;
767
- var REFRESH_LOCK_WAIT_MS = 30 * 1000;
768
- var REFRESH_LOCK_POLL_MS = 250;
769
- var DEFAULT_REFRESH_BUFFER_MS = 5 * 60 * 1000;
770
- async function withAuthorizationRefreshLock(authorizationId, fn) {
771
- const previous = refreshLocks.get(authorizationId) ?? Promise.resolve();
772
- let release;
773
- const current = new Promise((resolve) => {
774
- release = resolve;
775
- });
776
- const next = previous.catch(() => {
777
- return;
778
- }).then(() => current);
779
- refreshLocks.set(authorizationId, next);
780
- await previous.catch(() => {
781
- return;
782
- });
783
- try {
784
- return await fn();
785
- } finally {
786
- release();
787
- if (refreshLocks.get(authorizationId) === next) {
788
- refreshLocks.delete(authorizationId);
789
- }
790
- }
791
- }
792
- function sleep(ms) {
793
- return new Promise((resolve) => setTimeout(resolve, ms));
794
- }
795
- function authorizationNeedsRefresh(authorization, bufferMs = DEFAULT_REFRESH_BUFFER_MS, force = false) {
796
- if (authorization.status === "refresh-failed" || authorization.status === "expired")
797
- return true;
798
- if (force)
799
- return true;
800
- if (!authorization.expiresAt)
801
- return false;
802
- const expiresAt = new Date(authorization.expiresAt).getTime();
803
- if (Number.isNaN(expiresAt))
804
- return true;
805
- return expiresAt - Date.now() < bufferMs;
806
- }
807
- var authorizationRefreshedListeners = new Set;
808
- function onAuthorizationRefreshed(listener) {
809
- authorizationRefreshedListeners.add(listener);
810
- return () => {
811
- authorizationRefreshedListeners.delete(listener);
812
- };
813
- }
814
- function notifyAuthorizationRefreshed(event) {
815
- for (const listener of authorizationRefreshedListeners) {
816
- try {
817
- listener(event);
818
- } catch (err) {
819
- console.error(scrubSecrets(`[OAuth] authorization-refreshed listener failed for ${event.provider}: ${err instanceof Error ? err.message : String(err)}`));
820
- }
821
- }
822
- }
823
- async function ensureAuthorizationTokenOrThrow(authorizationId, bufferMs, force = false) {
824
- const initial = await getAuthorizationById(authorizationId);
825
- if (!initial || initial.status === "revoked")
826
- return;
827
- if (!authorizationNeedsRefresh(initial, bufferMs, force))
828
- return;
829
- await withAuthorizationRefreshLock(authorizationId, async () => {
830
- const waitStartedAt = Date.now();
831
- while (true) {
832
- const current = await getAuthorizationById(authorizationId);
833
- if (!current || current.status === "revoked")
834
- return;
835
- if (current.tokenVersion !== initial.tokenVersion)
836
- return;
837
- if (!authorizationNeedsRefresh(current, bufferMs, force))
838
- return;
839
- const app = await getOAuthAppById(current.appId);
840
- if (!app || app.mcpServerId !== null)
841
- return;
842
- if (!current.refreshToken) {
843
- const label = authorizationLabelFor(app, current);
844
- const message = `OAuth authorization '${label}' cannot refresh: no refresh token stored`;
845
- await markAuthorizationRefreshFailed(authorizationId, message);
846
- throw new OAuthRefreshError(authorizationId, app.id, "no_refresh_token", label, message);
847
- }
848
- const lockOwner = await acquireOAuthRefreshLock(`authz:${authorizationId}`, REFRESH_LOCK_TTL_MS);
849
- if (!lockOwner) {
850
- if (Date.now() - waitStartedAt > REFRESH_LOCK_WAIT_MS) {
851
- const label = authorizationLabelFor(app, current);
852
- const message = `Timed out waiting for OAuth refresh lock for '${label}'`;
853
- await markAuthorizationRefreshFailed(authorizationId, message);
854
- throw new OAuthRefreshError(authorizationId, app.id, "lock_timeout", label, message);
855
- }
856
- await sleep(REFRESH_LOCK_POLL_MS);
857
- continue;
858
- }
859
- try {
860
- const locked = await getAuthorizationById(authorizationId);
861
- if (!locked || locked.status === "revoked")
862
- return;
863
- if (locked.tokenVersion !== initial.tokenVersion)
864
- return;
865
- if (!authorizationNeedsRefresh(locked, bufferMs, force))
866
- return;
867
- const lockedApp = await getOAuthAppById(locked.appId);
868
- if (!lockedApp || lockedApp.mcpServerId !== null)
869
- return;
870
- if (!locked.refreshToken) {
871
- const label = authorizationLabelFor(lockedApp, locked);
872
- const message = `OAuth authorization '${label}' cannot refresh: no refresh token stored`;
873
- await markAuthorizationRefreshFailed(authorizationId, message);
874
- throw new OAuthRefreshError(authorizationId, lockedApp.id, "no_refresh_token", label, message);
875
- }
876
- const config = oauthAppRowToProviderConfig(lockedApp);
877
- try {
878
- const refreshed = await performTokenRefreshRequest(config, locked.refreshToken);
879
- const persisted = await updateAuthorizationTokens(authorizationId, {
880
- accessToken: refreshed.accessToken,
881
- refreshToken: refreshed.refreshToken ?? locked.refreshToken,
882
- expiresAt: refreshed.expiresAt,
883
- scope: refreshed.scope ?? null,
884
- expectedTokenVersion: locked.tokenVersion
885
- });
886
- if (!persisted) {
887
- continue;
888
- }
889
- notifyAuthorizationRefreshed({
890
- authorizationId,
891
- appId: lockedApp.id,
892
- provider: lockedApp.provider
893
- });
894
- return;
895
- } catch (err) {
896
- const label = authorizationLabelFor(lockedApp, locked);
897
- if (locked.refreshToken)
898
- registerVolatileSecret(locked.refreshToken, "oauth-refresh-token");
899
- if (locked.accessToken)
900
- registerVolatileSecret(locked.accessToken, "oauth-access-token");
901
- if (lockedApp.clientSecret)
902
- registerVolatileSecret(lockedApp.clientSecret, "oauth-client-secret");
903
- const message = scrubSecrets(err instanceof Error ? err.message : String(err));
904
- await markAuthorizationRefreshFailed(authorizationId, message);
905
- if (err instanceof OAuthRefreshError)
906
- throw err;
907
- throw new OAuthRefreshError(authorizationId, lockedApp.id, "refresh_rejected", label, message);
908
- }
909
- } finally {
910
- await releaseOAuthRefreshLock(`authz:${authorizationId}`, lockOwner);
911
- }
912
- }
913
- });
914
- }
915
- async function forceRefreshAuthorizationOrThrow(authorizationId) {
916
- await ensureAuthorizationTokenOrThrow(authorizationId, undefined, true);
917
- }
918
- async function ensureToken(provider, bufferMs) {
919
- try {
920
- await ensureTokenOrThrow(provider, bufferMs);
921
- } catch (err) {
922
- console.error(`[OAuth] Failed to refresh ${provider} token:`, err instanceof Error ? err.message : err);
923
- }
924
- }
925
- async function ensureTokenOrThrow(provider, bufferMs) {
926
- const authorizationId = await getDefaultAuthorizationIdForProvider(provider);
927
- if (!authorizationId)
928
- return;
929
- try {
930
- await ensureAuthorizationTokenOrThrow(authorizationId, bufferMs);
931
- } catch (err) {
932
- if (err instanceof OAuthRefreshError && err.reason === "no_refresh_token") {
933
- return;
934
- }
935
- throw err;
936
- }
937
- }
938
- async function forceRefreshTokenOrThrow(provider) {
939
- const authorizationId = await getDefaultAuthorizationIdForProvider(provider);
940
- if (!authorizationId)
941
- return;
942
- try {
943
- await forceRefreshAuthorizationOrThrow(authorizationId);
944
- } catch (err) {
945
- if (err instanceof OAuthRefreshError && err.reason === "no_refresh_token")
946
- return;
947
- throw err;
948
- }
949
- }
950
-
951
- export { getDefaultAuthorizationIdForProvider, getOAuthApp, getOAuthAppById, getOAuthAppIdByProvider, createOAuthApp, updateOAuthAppById, upsertOAuthApp, listAuthorizationsForApp, getAuthorizationById, upsertAuthorization, updateAuthorizationTokens, updateAuthorizationIdentity, deleteAuthorizationById, deleteAuthorizationsForApp, consumeOAuthPending, gcOAuthPending, getOAuthTokens, listAuthorizationSweepRows, listKeepAliveAuthorizations, deleteOAuthTokens, acquireOAuthRefreshLock, releaseOAuthRefreshLock, generateRandomCodeVerifier, generateRandomState, calculatePKCECodeChallenge, buildAuthorizationUrl, exchangeAuthorizationCode, oauthAppRowToProviderConfig, onAuthorizationRefreshed, ensureAuthorizationTokenOrThrow, forceRefreshAuthorizationOrThrow, ensureToken, ensureTokenOrThrow, forceRefreshTokenOrThrow };