@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
@@ -4,14 +4,16 @@ import {
4
4
  handleScheduleTrigger,
5
5
  runScript,
6
6
  startWorkflowExecution
7
- } from "./cli-z46pnksz.js";
7
+ } from "./cli-q1rx7g0d.js";
8
8
  import {
9
- getScript
10
- } from "./cli-qwgtacd6.js";
9
+ getScript,
10
+ getScriptApiConnectionDescriptors,
11
+ getScriptMcpConnectionDescriptors
12
+ } from "./cli-r9f6ngrf.js";
11
13
  import {
12
14
  createTaskWithSiblingAwareness,
13
15
  scheduleContextKey
14
- } from "./cli-y4ycrnjc.js";
16
+ } from "./cli-k3y4z3h5.js";
15
17
  import {
16
18
  getDb,
17
19
  getDueScheduledTasks,
@@ -21,7 +23,7 @@ import {
21
23
  init_telemetry,
22
24
  telemetry,
23
25
  updateScheduledTask
24
- } from "./cli-ncfgsqbd.js";
26
+ } from "./cli-15y7qhzp.js";
25
27
  import {
26
28
  __commonJS,
27
29
  __require,
@@ -6870,6 +6872,7 @@ function createStandaloneScheduleTask(schedule, extraTags = []) {
6870
6872
  modelTier: schedule.modelTier,
6871
6873
  scheduleId: schedule.id,
6872
6874
  source: "schedule",
6875
+ requestedByUserId: schedule.createdBy,
6873
6876
  contextKey: scheduleContextKey({ scheduleId: schedule.id })
6874
6877
  });
6875
6878
  }
@@ -6887,7 +6890,9 @@ async function executeScheduleScript(schedule) {
6887
6890
  args: schedule.scriptArgs ?? {},
6888
6891
  fsMode: "none",
6889
6892
  agentId,
6890
- egressSecrets: buildScriptCredentialBindings({ agentId }),
6893
+ egressSecrets: await buildScriptCredentialBindings({ agentId }),
6894
+ apiConnections: getScriptApiConnectionDescriptors({ agentId }),
6895
+ mcpConnections: getScriptMcpConnectionDescriptors({ agentId }),
6891
6896
  timeoutMs: 60000
6892
6897
  });
6893
6898
  if (output.exitCode !== 0 || output.error) {
@@ -6917,7 +6922,8 @@ async function dispatchScheduleTarget(schedule, extraTags = []) {
6917
6922
  firedAt: new Date().toISOString()
6918
6923
  };
6919
6924
  const runId = await startWorkflowExecution(workflow, triggerData, registry, {
6920
- triggerType: "schedule"
6925
+ triggerType: "schedule",
6926
+ requestedByUserId: schedule.createdBy
6921
6927
  });
6922
6928
  console.log(`[Scheduler] Schedule "${schedule.name}" → triggered workflow "${workflow.name}"`);
6923
6929
  return { triggeredWorkflows: true, workflowRunIds: [runId] };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  init_external
3
- } from "./cli-anrj584m.js";
3
+ } from "./cli-xz9aq0rf.js";
4
4
 
5
5
  // node_modules/zod/v4/classic/index.js
6
6
  init_external();
@@ -0,0 +1,393 @@
1
+ import {
2
+ calculatePKCECodeChallenge,
3
+ generateRandomCodeVerifier,
4
+ generateRandomState
5
+ } from "./cli-0pygq9r1.js";
6
+ import {
7
+ getDb,
8
+ init_date_utils,
9
+ init_db,
10
+ normalizeDateRequired
11
+ } from "./cli-15y7qhzp.js";
12
+
13
+ // src/be/db-queries/oauth.ts
14
+ init_date_utils();
15
+ init_db();
16
+ function normalizeOAuthApp(row) {
17
+ return {
18
+ ...row,
19
+ createdAt: normalizeDateRequired(row.createdAt),
20
+ updatedAt: normalizeDateRequired(row.updatedAt)
21
+ };
22
+ }
23
+ function normalizeOAuthTokens(row) {
24
+ return {
25
+ ...row,
26
+ createdAt: normalizeDateRequired(row.createdAt),
27
+ updatedAt: normalizeDateRequired(row.updatedAt)
28
+ };
29
+ }
30
+ function getOAuthApp(provider) {
31
+ const row = getDb().query("SELECT * FROM oauth_apps WHERE provider = ?").get(provider);
32
+ return row ? normalizeOAuthApp(row) : null;
33
+ }
34
+ function upsertOAuthApp(provider, data) {
35
+ const metadataProvided = data.metadata !== undefined;
36
+ const sql = metadataProvided ? `INSERT INTO oauth_apps (provider, clientId, clientSecret, authorizeUrl, tokenUrl, redirectUri, scopes, metadata)
37
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
38
+ ON CONFLICT(provider) DO UPDATE SET
39
+ clientId = excluded.clientId,
40
+ clientSecret = excluded.clientSecret,
41
+ authorizeUrl = excluded.authorizeUrl,
42
+ tokenUrl = excluded.tokenUrl,
43
+ redirectUri = excluded.redirectUri,
44
+ scopes = excluded.scopes,
45
+ metadata = excluded.metadata,
46
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')` : `INSERT INTO oauth_apps (provider, clientId, clientSecret, authorizeUrl, tokenUrl, redirectUri, scopes, metadata)
47
+ VALUES (?, ?, ?, ?, ?, ?, ?, '{}')
48
+ ON CONFLICT(provider) DO UPDATE SET
49
+ clientId = excluded.clientId,
50
+ clientSecret = excluded.clientSecret,
51
+ authorizeUrl = excluded.authorizeUrl,
52
+ tokenUrl = excluded.tokenUrl,
53
+ redirectUri = excluded.redirectUri,
54
+ scopes = excluded.scopes,
55
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;
56
+ if (metadataProvided) {
57
+ getDb().query(sql).run(provider, data.clientId, data.clientSecret, data.authorizeUrl, data.tokenUrl, data.redirectUri, data.scopes, data.metadata);
58
+ } else {
59
+ getDb().query(sql).run(provider, data.clientId, data.clientSecret, data.authorizeUrl, data.tokenUrl, data.redirectUri, data.scopes);
60
+ }
61
+ }
62
+ function getOAuthTokens(provider) {
63
+ const row = getDb().query("SELECT * FROM oauth_tokens WHERE provider = ?").get(provider);
64
+ return row ? normalizeOAuthTokens(row) : null;
65
+ }
66
+ function storeOAuthTokens(provider, data) {
67
+ getDb().query(`INSERT INTO oauth_tokens (provider, accessToken, refreshToken, expiresAt, scope)
68
+ VALUES (?, ?, ?, ?, ?)
69
+ ON CONFLICT(provider) DO UPDATE SET
70
+ accessToken = excluded.accessToken,
71
+ refreshToken = COALESCE(excluded.refreshToken, oauth_tokens.refreshToken),
72
+ expiresAt = excluded.expiresAt,
73
+ scope = COALESCE(excluded.scope, oauth_tokens.scope),
74
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`).run(provider, data.accessToken, data.refreshToken ?? null, data.expiresAt, data.scope ?? null);
75
+ }
76
+ function updateOAuthTokensAfterRefresh(provider, expectedRefreshToken, data) {
77
+ const result = getDb().query(`UPDATE oauth_tokens
78
+ SET accessToken = ?,
79
+ refreshToken = ?,
80
+ expiresAt = ?,
81
+ scope = COALESCE(?, scope),
82
+ updatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
83
+ WHERE provider = ? AND refreshToken = ?`).run(data.accessToken, data.refreshToken, data.expiresAt, data.scope ?? null, provider, expectedRefreshToken);
84
+ if (result.changes === 1)
85
+ return;
86
+ const current = getOAuthTokens(provider);
87
+ if (!current) {
88
+ throw new Error(`OAuth token refresh persistence failed for ${provider}: token row missing`);
89
+ }
90
+ if (current.refreshToken !== expectedRefreshToken) {
91
+ throw new Error(`OAuth token refresh persistence failed for ${provider}: stored refresh token changed during refresh`);
92
+ }
93
+ throw new Error(`OAuth token refresh persistence failed for ${provider}: no rows updated`);
94
+ }
95
+ function listOAuthTokenSweepRows() {
96
+ const rows = getDb().query(`SELECT t.provider,
97
+ CASE WHEN a.provider IS NOT NULL THEN 1 ELSE 0 END AS hasApp,
98
+ CASE WHEN t.refreshToken IS NOT NULL AND t.refreshToken != '' THEN 1 ELSE 0 END AS hasRefreshToken,
99
+ t.expiresAt,
100
+ t.updatedAt
101
+ FROM oauth_tokens t
102
+ LEFT JOIN oauth_apps a ON a.provider = t.provider
103
+ ORDER BY t.provider ASC`).all();
104
+ return rows.map((row) => ({
105
+ provider: row.provider,
106
+ hasApp: row.hasApp === 1,
107
+ hasRefreshToken: row.hasRefreshToken === 1,
108
+ expiresAt: normalizeDateRequired(row.expiresAt),
109
+ updatedAt: normalizeDateRequired(row.updatedAt)
110
+ }));
111
+ }
112
+ function deleteOAuthTokens(provider) {
113
+ getDb().query("DELETE FROM oauth_tokens WHERE provider = ?").run(provider);
114
+ }
115
+ function isTokenExpiringSoon(provider, bufferMs = 5 * 60 * 1000) {
116
+ const tokens = getOAuthTokens(provider);
117
+ if (!tokens)
118
+ return true;
119
+ const expiresAt = new Date(tokens.expiresAt).getTime();
120
+ return expiresAt - Date.now() < bufferMs;
121
+ }
122
+ function acquireOAuthRefreshLock(provider, ttlMs) {
123
+ const owner = crypto.randomUUID();
124
+ const now = Date.now();
125
+ const expiresAt = new Date(now + ttlMs).toISOString();
126
+ const nowIso = new Date(now).toISOString();
127
+ getDb().query(`INSERT INTO oauth_refresh_locks (provider, owner, expiresAt, createdAt, updatedAt)
128
+ VALUES (?, ?, ?, ?, ?)
129
+ ON CONFLICT(provider) DO UPDATE SET
130
+ owner = excluded.owner,
131
+ expiresAt = excluded.expiresAt,
132
+ updatedAt = excluded.updatedAt
133
+ WHERE oauth_refresh_locks.expiresAt <= ?`).run(provider, owner, expiresAt, nowIso, nowIso, nowIso);
134
+ const row = getDb().query("SELECT owner FROM oauth_refresh_locks WHERE provider = ?").get(provider);
135
+ return row?.owner === owner ? owner : null;
136
+ }
137
+ function releaseOAuthRefreshLock(provider, owner) {
138
+ getDb().query("DELETE FROM oauth_refresh_locks WHERE provider = ? AND owner = ?").run(provider, owner);
139
+ }
140
+
141
+ // src/oauth/wrapper.ts
142
+ var STATE_TTL_MS = 10 * 60 * 1000;
143
+ var pendingStates = new Map;
144
+ function cleanupExpiredStates() {
145
+ const now = Date.now();
146
+ for (const [key, entry] of pendingStates) {
147
+ if (now - entry.createdAt > STATE_TTL_MS) {
148
+ pendingStates.delete(key);
149
+ }
150
+ }
151
+ }
152
+ async function buildAuthorizationUrl(config) {
153
+ cleanupExpiredStates();
154
+ const state = generateRandomState();
155
+ const codeVerifier = generateRandomCodeVerifier();
156
+ const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
157
+ pendingStates.set(state, {
158
+ codeVerifier,
159
+ config,
160
+ createdAt: Date.now()
161
+ });
162
+ const url = new URL(config.authorizeUrl);
163
+ url.searchParams.set("client_id", config.clientId);
164
+ url.searchParams.set("redirect_uri", config.redirectUri);
165
+ url.searchParams.set("response_type", "code");
166
+ url.searchParams.set("scope", config.scopes.join(config.scopeSeparator ?? ","));
167
+ url.searchParams.set("state", state);
168
+ url.searchParams.set("code_challenge", codeChallenge);
169
+ url.searchParams.set("code_challenge_method", "S256");
170
+ if (config.extraParams) {
171
+ for (const [key, value] of Object.entries(config.extraParams)) {
172
+ url.searchParams.set(key, value);
173
+ }
174
+ }
175
+ return { url: url.toString(), state, codeVerifier };
176
+ }
177
+ function tokenRequestInit(config, params) {
178
+ const useBasic = config.tokenAuthStyle === "basic";
179
+ const bodyParams = useBasic ? params : { ...params, client_id: config.clientId, client_secret: config.clientSecret };
180
+ const headers = {};
181
+ if (useBasic) {
182
+ headers.Authorization = `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`;
183
+ }
184
+ if (config.tokenBodyFormat === "json") {
185
+ headers["Content-Type"] = "application/json";
186
+ return { headers, body: JSON.stringify(bodyParams) };
187
+ }
188
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
189
+ return { headers, body: new URLSearchParams(bodyParams).toString() };
190
+ }
191
+ async function exchangeCode(config, code, state) {
192
+ const pending = pendingStates.get(state);
193
+ if (!pending) {
194
+ throw new Error("Invalid or expired OAuth state");
195
+ }
196
+ pendingStates.delete(state);
197
+ const { codeVerifier } = pending;
198
+ const response = await fetch(config.tokenUrl, {
199
+ method: "POST",
200
+ ...tokenRequestInit(config, {
201
+ grant_type: "authorization_code",
202
+ redirect_uri: config.redirectUri,
203
+ code,
204
+ code_verifier: codeVerifier
205
+ })
206
+ });
207
+ if (!response.ok) {
208
+ const errorText = await response.text();
209
+ throw new Error(`Token exchange failed (${response.status}): ${errorText}`);
210
+ }
211
+ const data = await response.json();
212
+ const expiresAt = data.expires_in ? new Date(Date.now() + data.expires_in * 1000).toISOString() : new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
213
+ storeOAuthTokens(config.provider, {
214
+ accessToken: data.access_token,
215
+ refreshToken: data.refresh_token ?? null,
216
+ expiresAt,
217
+ scope: data.scope ?? null
218
+ });
219
+ return {
220
+ accessToken: data.access_token,
221
+ refreshToken: data.refresh_token,
222
+ expiresIn: data.expires_in,
223
+ scope: data.scope
224
+ };
225
+ }
226
+ async function refreshAccessToken(config, refreshToken) {
227
+ const response = await fetch(config.tokenUrl, {
228
+ method: "POST",
229
+ ...tokenRequestInit(config, {
230
+ grant_type: "refresh_token",
231
+ refresh_token: refreshToken
232
+ })
233
+ });
234
+ if (!response.ok) {
235
+ const errorText = await response.text();
236
+ throw new Error(`Token refresh failed (${response.status}): ${errorText}`);
237
+ }
238
+ const data = await response.json();
239
+ if (typeof data.access_token !== "string" || data.access_token.length === 0) {
240
+ throw new Error(`Token refresh failed: ${config.provider} response missing access_token`);
241
+ }
242
+ if (config.requiresRefreshTokenRotation && (typeof data.refresh_token !== "string" || data.refresh_token.length === 0)) {
243
+ throw new Error(`Token refresh failed: ${config.provider} response did not include a rotated refresh_token`);
244
+ }
245
+ const expiresAt = data.expires_in ? new Date(Date.now() + data.expires_in * 1000).toISOString() : new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
246
+ const nextRefreshToken = data.refresh_token ?? refreshToken;
247
+ try {
248
+ updateOAuthTokensAfterRefresh(config.provider, refreshToken, {
249
+ accessToken: data.access_token,
250
+ refreshToken: nextRefreshToken,
251
+ expiresAt,
252
+ scope: data.scope ?? null
253
+ });
254
+ } catch (err) {
255
+ const message = err instanceof Error ? err.message : String(err);
256
+ console.warn(`[OAuth] Refusing to use refreshed ${config.provider} access token because persistence failed: ${message}`);
257
+ throw err;
258
+ }
259
+ return {
260
+ accessToken: data.access_token,
261
+ refreshToken: data.refresh_token,
262
+ expiresIn: data.expires_in,
263
+ scope: data.scope
264
+ };
265
+ }
266
+
267
+ // src/oauth/ensure-token.ts
268
+ function parseMetadata(metadataJson) {
269
+ try {
270
+ const parsed = JSON.parse(metadataJson || "{}");
271
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
272
+ } catch {
273
+ return {};
274
+ }
275
+ }
276
+ function stringRecord(value) {
277
+ if (!value || typeof value !== "object" || Array.isArray(value))
278
+ return;
279
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, String(item)]));
280
+ }
281
+ function oauthAppRowToProviderConfig(app) {
282
+ const metadata = parseMetadata(app.metadata);
283
+ const extraParams = stringRecord(metadata.extraParams) ?? (typeof metadata.actor === "string" ? { actor: metadata.actor } : undefined);
284
+ return {
285
+ provider: app.provider,
286
+ clientId: app.clientId,
287
+ clientSecret: app.clientSecret,
288
+ authorizeUrl: app.authorizeUrl,
289
+ tokenUrl: app.tokenUrl,
290
+ redirectUri: app.redirectUri,
291
+ scopes: app.scopes.split(",").map((scope) => scope.trim()).filter(Boolean),
292
+ extraParams,
293
+ scopeSeparator: " ",
294
+ ...metadata.tokenAuthStyle === "basic" ? { tokenAuthStyle: "basic" } : {},
295
+ ...metadata.tokenBodyFormat === "json" ? { tokenBodyFormat: "json" } : {},
296
+ requiresRefreshTokenRotation: app.provider === "jira"
297
+ };
298
+ }
299
+ var refreshLocks = new Map;
300
+ var REFRESH_LOCK_TTL_MS = 2 * 60 * 1000;
301
+ var REFRESH_LOCK_WAIT_MS = 30 * 1000;
302
+ var REFRESH_LOCK_POLL_MS = 250;
303
+ function getOAuthConfig(provider) {
304
+ const app = getOAuthApp(provider);
305
+ return app ? oauthAppRowToProviderConfig(app) : null;
306
+ }
307
+ async function withProviderRefreshLock(provider, fn) {
308
+ const previous = refreshLocks.get(provider) ?? Promise.resolve();
309
+ let release;
310
+ const current = new Promise((resolve) => {
311
+ release = resolve;
312
+ });
313
+ const next = previous.catch(() => {
314
+ return;
315
+ }).then(() => current);
316
+ refreshLocks.set(provider, next);
317
+ await previous.catch(() => {
318
+ return;
319
+ });
320
+ try {
321
+ return await fn();
322
+ } finally {
323
+ release();
324
+ if (refreshLocks.get(provider) === next) {
325
+ refreshLocks.delete(provider);
326
+ }
327
+ }
328
+ }
329
+ function sleep(ms) {
330
+ return new Promise((resolve) => setTimeout(resolve, ms));
331
+ }
332
+ function tokenRowChanged(current, observed) {
333
+ if (!observed)
334
+ return current !== null;
335
+ if (!current)
336
+ return true;
337
+ return current.accessToken !== observed.accessToken || current.refreshToken !== observed.refreshToken || current.expiresAt !== observed.expiresAt;
338
+ }
339
+ async function ensureToken(provider, bufferMs) {
340
+ try {
341
+ await ensureTokenOrThrow(provider, bufferMs);
342
+ } catch (err) {
343
+ console.error(`[OAuth] Failed to refresh ${provider} token:`, err instanceof Error ? err.message : err);
344
+ }
345
+ }
346
+ var FORCE_REFRESH_BUFFER_MS = 1000 * 60 * 60 * 24 * 365 * 100;
347
+ async function forceRefreshTokenOrThrow(provider) {
348
+ await ensureTokenOrThrow(provider, FORCE_REFRESH_BUFFER_MS);
349
+ }
350
+ async function ensureTokenOrThrow(provider, bufferMs) {
351
+ if (!isTokenExpiringSoon(provider, bufferMs))
352
+ return;
353
+ const observedTokens = getOAuthTokens(provider);
354
+ await withProviderRefreshLock(provider, async () => {
355
+ const waitStartedAt = Date.now();
356
+ while (isTokenExpiringSoon(provider, bufferMs)) {
357
+ const tokens = getOAuthTokens(provider);
358
+ if (tokenRowChanged(tokens, observedTokens))
359
+ return;
360
+ const config = getOAuthConfig(provider);
361
+ if (!config || !tokens?.refreshToken) {
362
+ console.warn(`[OAuth] ${provider} token expiring but cannot refresh (missing config or refresh token)`);
363
+ return;
364
+ }
365
+ const lockOwner = acquireOAuthRefreshLock(provider, REFRESH_LOCK_TTL_MS);
366
+ if (!lockOwner) {
367
+ if (Date.now() - waitStartedAt > REFRESH_LOCK_WAIT_MS) {
368
+ throw new Error(`Timed out waiting for ${provider} OAuth token refresh lock`);
369
+ }
370
+ await sleep(REFRESH_LOCK_POLL_MS);
371
+ continue;
372
+ }
373
+ try {
374
+ const lockedTokens = getOAuthTokens(provider);
375
+ if (!isTokenExpiringSoon(provider, bufferMs) || tokenRowChanged(lockedTokens, observedTokens)) {
376
+ return;
377
+ }
378
+ const lockedConfig = getOAuthConfig(provider);
379
+ if (!lockedConfig || !lockedTokens?.refreshToken) {
380
+ console.warn(`[OAuth] ${provider} token expiring but cannot refresh (missing config or refresh token)`);
381
+ return;
382
+ }
383
+ await refreshAccessToken(lockedConfig, lockedTokens.refreshToken);
384
+ console.log(`[OAuth] ${provider} token refreshed successfully`);
385
+ return;
386
+ } finally {
387
+ releaseOAuthRefreshLock(provider, lockOwner);
388
+ }
389
+ }
390
+ });
391
+ }
392
+
393
+ export { getOAuthApp, upsertOAuthApp, getOAuthTokens, listOAuthTokenSweepRows, deleteOAuthTokens, isTokenExpiringSoon, acquireOAuthRefreshLock, releaseOAuthRefreshLock, buildAuthorizationUrl, exchangeCode, oauthAppRowToProviderConfig, ensureToken, forceRefreshTokenOrThrow, ensureTokenOrThrow };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getDb,
3
3
  init_db
4
- } from "./cli-ncfgsqbd.js";
4
+ } from "./cli-15y7qhzp.js";
5
5
 
6
6
  // src/be/seed-pricing.ts
7
7
  init_db();
@@ -3,7 +3,7 @@ import {
3
3
  getAgentWorkingOnThread,
4
4
  getAllAgents,
5
5
  init_db
6
- } from "./cli-ncfgsqbd.js";
6
+ } from "./cli-15y7qhzp.js";
7
7
 
8
8
  // src/slack/router.ts
9
9
  init_db();