@desplega.ai/agent-swarm 1.129.0 → 1.131.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 (226) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-kfv2dawe.js → actions-w53mbns9.js} +11 -11
  3. package/dist/{anthropic-messages-3xgme8my.js → anthropic-messages-rjmd2e7n.js} +5 -5
  4. package/dist/{app-q8vjk6e8.js → app-m9gwqsqv.js} +8 -8
  5. package/dist/{artifact-hg4t1nx7.js → artifact-jysj3wrg.js} +2 -2
  6. package/dist/{assistant-g037r345.js → assistant-bp65m6f3.js} +13 -13
  7. package/dist/{azure-openai-responses-xa4sh9f6.js → azure-openai-responses-c0yjmbfd.js} +8 -5
  8. package/dist/{boot-reembed-18pzsrxa.js → boot-reembed-62p0at2k.js} +6 -6
  9. package/dist/{boot-reembed-wpks2cj8.js → boot-reembed-xmfp6wdh.js} +7 -7
  10. package/dist/{boot-scrub-logs-n7szd57r.js → boot-scrub-logs-rr8q2dms.js} +5 -5
  11. package/dist/{claude-adapter-qz4r0ajt.js → claude-adapter-2y47mrkd.js} +9 -9
  12. package/dist/{cli-mhjnbgb6.js → cli-0rcyq6d9.js} +450 -186
  13. package/dist/{cli-ttkmehy0.js → cli-2q72qz2s.js} +274 -25
  14. package/dist/{cli-gztgc3hz.js → cli-2wadx7p2.js} +2 -2
  15. package/dist/cli-3fj60etf.js +153 -0
  16. package/dist/{cli-7ecgm5v0.js → cli-3kf51j15.js} +1 -1
  17. package/dist/{cli-vgexpfsr.js → cli-57khth04.js} +6 -3
  18. package/dist/{cli-2mbe7vk0.js → cli-5dsbcjjw.js} +1 -1
  19. package/dist/{cli-etr62zy6.js → cli-5kx36evn.js} +2 -2
  20. package/dist/{cli-v2tr6amf.js → cli-5n6e5yfn.js} +30 -8
  21. package/dist/{cli-hx02s9zq.js → cli-5v4rw6yd.js} +2 -2
  22. package/dist/{cli-v37yr6mf.js → cli-5ze9rv97.js} +4 -4
  23. package/dist/{cli-ch7t5v7n.js → cli-6map995v.js} +9 -4
  24. package/dist/{cli-h0457a67.js → cli-70xqj0z3.js} +3 -3
  25. package/dist/{cli-kk4f9r17.js → cli-72q8m8gk.js} +8 -6
  26. package/dist/{cli-7padk69a.js → cli-78xwvkhj.js} +2 -2
  27. package/dist/{cli-dxtxacy7.js → cli-9dr5qzc0.js} +45 -8
  28. package/dist/{cli-17zxdgsq.js → cli-a40rnf71.js} +6 -6
  29. package/dist/{cli-t53vnnxz.js → cli-a4jzdpa3.js} +2514 -1055
  30. package/dist/{cli-w10fkrf1.js → cli-akdvwc9v.js} +354 -115
  31. package/dist/{cli-1qkk5er0.js → cli-bmze986d.js} +1 -1
  32. package/dist/{cli-ga12dc1d.js → cli-bqw0mt1n.js} +3 -3
  33. package/dist/{cli-0sf92efp.js → cli-cdeg08wx.js} +1 -1
  34. package/dist/{cli-a8ynhkyj.js → cli-cvgjwnh1.js} +17 -15
  35. package/dist/{cli-9fxg3v5m.js → cli-d2tfkcga.js} +4 -4
  36. package/dist/{cli-nhcd7x43.js → cli-d3x3x1c9.js} +2 -2
  37. package/dist/{cli-6nh2qxvh.js → cli-darcwax3.js} +1 -1
  38. package/dist/{cli-yjbgmysx.js → cli-e7ckcg4v.js} +12 -12
  39. package/dist/{cli-4n2gcjpd.js → cli-efxfnq6q.js} +4 -3
  40. package/dist/{cli-z9xqqhfk.js → cli-er3a4bb0.js} +1 -1
  41. package/dist/{cli-xg2eezyv.js → cli-f1am9szv.js} +1 -1
  42. package/dist/{cli-xy4cwsm9.js → cli-fywms69x.js} +1 -1
  43. package/dist/{cli-33jj76y8.js → cli-g6b8x1h8.js} +1 -1
  44. package/dist/{cli-epsvdckx.js → cli-gm9cbhbq.js} +20 -10
  45. package/dist/{cli-rkndnn90.js → cli-k9qkf1hw.js} +6 -4
  46. package/dist/{cli-zejvt395.js → cli-kh3ba2zf.js} +1 -1
  47. package/dist/{cli-gb25p1h2.js → cli-nm5gtp1x.js} +2 -2
  48. package/dist/{cli-akxnpx17.js → cli-pm6xbh84.js} +1829 -1629
  49. package/dist/{cli-2k0ztbfp.js → cli-psh3c4va.js} +1 -1
  50. package/dist/{cli-39vgrsep.js → cli-qv61hp37.js} +2 -2
  51. package/dist/{cli-hv2fndhm.js → cli-r6kqqbj0.js} +6 -4
  52. package/dist/{cli-1y1cm570.js → cli-rzbpmxdr.js} +1 -1
  53. package/dist/{cli-mmrk00vp.js → cli-t1xze5qh.js} +1 -1
  54. package/dist/{cli-g252a9ky.js → cli-t55yw3a1.js} +2 -2
  55. package/dist/{cli-7bz8dzqa.js → cli-t76vw5db.js} +1184 -1522
  56. package/dist/{cli-4n4mdv21.js → cli-xpxde7rm.js} +7 -3
  57. package/dist/{cli-fpaa5k2q.js → cli-yw8vghx2.js} +54 -18
  58. package/dist/cli-zsspvvm2.js +490 -0
  59. package/dist/cli.js +27 -25
  60. package/dist/{codex-adapter-7v6t7ff6.js → codex-adapter-d24q75wk.js} +7 -7
  61. package/dist/{codex-hook-amndawyb.js → codex-hook-hq54t9ft.js} +2 -2
  62. package/dist/{codex-session-runner-e7j4khe2.js → codex-session-runner-g50vcsex.js} +7 -7
  63. package/dist/{commands-ex0k5tbf.js → commands-c3nrc5hg.js} +5 -5
  64. package/dist/{db-df7jatfc.js → db-nt7fg3tw.js} +5 -5
  65. package/dist/{google-generative-ai-y42hnw9p.js → google-generative-ai-hysrd8qx.js} +7 -5
  66. package/dist/{google-vertex-4sg66gb6.js → google-vertex-p5arrga0.js} +7 -5
  67. package/dist/{handlers-1j2cfqt2.js → handlers-fkr6swat.js} +13 -13
  68. package/dist/{hook-nx9a0mj3.js → hook-77wpvnjc.js} +7 -7
  69. package/dist/{hook-w6yaf6sy.js → hook-g019yehy.js} +8 -8
  70. package/dist/{http-4tz5h8wg.js → http-jewj0syk.js} +2993 -716
  71. package/dist/{index-0bpkzb09.js → index-070v4eyb.js} +3 -3
  72. package/dist/{index-0s67qv7h.js → index-6gm5he8h.js} +11 -11
  73. package/dist/{index-35gtv6gj.js → index-7n2xpbd4.js} +310 -40
  74. package/dist/{index-ker3gccb.js → index-9znhar3x.js} +4 -4
  75. package/dist/{index-5n2c8yay.js → index-hase6zbe.js} +2 -2
  76. package/dist/{index-kcf3tm9h.js → index-hfvggd0z.js} +12 -12
  77. package/dist/{index-7c0jqna0.js → index-hp1kdbjj.js} +13 -13
  78. package/dist/{keepalive-3j4a9168.js → keepalive-j0fjvqfd.js} +7 -7
  79. package/dist/{lead-6kpevymb.js → lead-1n8x30xe.js} +33 -33
  80. package/dist/{maintenance-m1k82ean.js → maintenance-gvev11sa.js} +7 -7
  81. package/dist/{mistral-conversations-bjkdfb6z.js → mistral-conversations-81fwav0j.js} +6 -6
  82. package/dist/{oauth-refresh-sweep-881k8e4q.js → oauth-refresh-sweep-s9ecsvh0.js} +6 -6
  83. package/dist/{onboard-c0ykffjn.js → onboard-pvm5b38w.js} +2 -2
  84. package/dist/{openai-codex-responses-xetseqvt.js → openai-codex-responses-5g27pd6f.js} +43 -23
  85. package/dist/{openai-completions-5r2g5axa.js → openai-completions-3vkmfz2a.js} +55 -11
  86. package/dist/{openai-responses-ce67hd23.js → openai-responses-dg4t7j7a.js} +8 -5
  87. package/dist/{openrouter-images-pzfp22jb.js → openrouter-images-g3gm8931.js} +1 -1
  88. package/dist/{otel-impl-sqagjerv.js → otel-impl-p6jvgd9c.js} +6 -4
  89. package/dist/{otel-3mp81ppd.js → otel-nqa12wf3.js} +2 -2
  90. package/dist/{pi-mono-adapter-k584k16g.js → pi-mono-adapter-t091s27g.js} +17117 -7517
  91. package/dist/{pricing-refresh-y82whbf9.js → pricing-refresh-k49rcyed.js} +7 -7
  92. package/dist/{rbac-roles-nvkqmzyd.js → rbac-roles-30pspvy7.js} +7 -7
  93. package/dist/{rbac-roles-4ht8gegc.js → rbac-roles-cr80g96t.js} +6 -6
  94. package/dist/{render-v2-wvqdgvvp.js → render-v2-xm457ng1.js} +8 -8
  95. package/dist/{seed-pricing-fy3rxhde.js → seed-pricing-yhpv35fr.js} +6 -6
  96. package/dist/{setup-ak5cnc6f.js → setup-nytnmwwr.js} +2 -2
  97. package/dist/{worker-d8769q8a.js → worker-msytr2cw.js} +33 -33
  98. package/openapi.json +34039 -11036
  99. package/package.json +8 -6
  100. package/src/apps/definition.ts +368 -26
  101. package/src/apps/row-store.ts +66 -10
  102. package/src/apps/schema-migrate.ts +95 -1
  103. package/src/apps/store.ts +13 -4
  104. package/src/apps/sync-run-as.ts +18 -0
  105. package/src/apps/sync.ts +934 -0
  106. package/src/apps/version.ts +16 -2
  107. package/src/assets/key.ts +8 -1
  108. package/src/be/asset-key-audit.ts +3 -1
  109. package/src/be/db-queries/mcp-oauth.ts +297 -24
  110. package/src/be/db.ts +42 -1
  111. package/src/be/migrations/129_asset_keys_apps_scripts.sql +161 -0
  112. package/src/be/scripts/db.ts +5 -2
  113. package/src/be/scripts/typecheck.ts +1 -0
  114. package/src/be/seed-scripts/catalog/app-sync-run.ts +32 -0
  115. package/src/be/seed-scripts/catalog/github-issues-pull.ts +102 -0
  116. package/src/be/seed-scripts/index.ts +18 -0
  117. package/src/be/seed-skills/index.ts +7 -0
  118. package/src/http/active-sessions.ts +26 -18
  119. package/src/http/agents.ts +94 -27
  120. package/src/http/api-keys.ts +91 -17
  121. package/src/http/approval-requests.ts +87 -10
  122. package/src/http/apps.ts +361 -53
  123. package/src/http/assets.ts +165 -31
  124. package/src/http/budgets.ts +7 -5
  125. package/src/http/codex-oauth-keep-warm.ts +21 -3
  126. package/src/http/config.ts +46 -16
  127. package/src/http/context.ts +33 -6
  128. package/src/http/ecosystem.ts +20 -6
  129. package/src/http/events.ts +24 -9
  130. package/src/http/favorites.ts +20 -6
  131. package/src/http/fs.ts +60 -17
  132. package/src/http/heartbeat.ts +18 -4
  133. package/src/http/inbox-state.ts +12 -6
  134. package/src/http/integrations.ts +25 -5
  135. package/src/http/kv.ts +36 -15
  136. package/src/http/mcp-bridge.ts +5 -1
  137. package/src/http/mcp-oauth.ts +332 -17
  138. package/src/http/mcp-servers.ts +56 -18
  139. package/src/http/memory.ts +282 -29
  140. package/src/http/metrics.ts +80 -17
  141. package/src/http/oauth-callback.ts +10 -4
  142. package/src/http/oauth-generic.ts +5 -1
  143. package/src/http/oauth-locks.ts +6 -3
  144. package/src/http/openapi.ts +57 -9
  145. package/src/http/page-proxy.ts +25 -5
  146. package/src/http/pages-public.ts +37 -20
  147. package/src/http/pages.ts +93 -28
  148. package/src/http/poll.ts +89 -5
  149. package/src/http/pricing.ts +9 -6
  150. package/src/http/prompt-templates.ts +85 -21
  151. package/src/http/route-def.ts +67 -4
  152. package/src/http/schedules.ts +63 -14
  153. package/src/http/script-connection-proxy.ts +30 -4
  154. package/src/http/script-connections.ts +393 -43
  155. package/src/http/script-runs.ts +73 -19
  156. package/src/http/scripts.ts +257 -39
  157. package/src/http/session-data.ts +87 -14
  158. package/src/http/sessions.ts +80 -5
  159. package/src/http/skills.ts +115 -35
  160. package/src/http/stats.ts +94 -13
  161. package/src/http/task-templates.ts +6 -4
  162. package/src/http/tasks.ts +230 -61
  163. package/src/http/trackers/jira.ts +90 -30
  164. package/src/http/trackers/linear.ts +46 -12
  165. package/src/http/users.ts +162 -25
  166. package/src/http/utils.ts +12 -2
  167. package/src/http/webhooks.ts +30 -16
  168. package/src/http/workflow-events.ts +18 -5
  169. package/src/http/workflows.ts +128 -39
  170. package/src/http/x.ts +37 -6
  171. package/src/oauth/ensure-mcp-token.ts +16 -1
  172. package/src/oauth/mcp-wrapper.ts +281 -26
  173. package/src/providers/pi-mono-adapter.ts +1 -1
  174. package/src/scripts-runtime/credential-broker/README.md +1 -1
  175. package/src/scripts-runtime/credential-broker/fetch-patch.ts +12 -2
  176. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  177. package/src/scripts-runtime/stdlib/fetch.ts +6 -0
  178. package/src/scripts-runtime/types/stdlib.d.ts +1 -0
  179. package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
  180. package/src/server.ts +2 -0
  181. package/src/tests/apps-rbac.test.ts +110 -0
  182. package/src/tests/apps-spike2.test.ts +4 -1
  183. package/src/tests/apps-sync-engine.test.ts +2540 -0
  184. package/src/tests/apps-sync.test.ts +1627 -0
  185. package/src/tests/asset-key-api.test.ts +48 -2
  186. package/src/tests/asset-key-invariants.test.ts +92 -0
  187. package/src/tests/asset-key-migration.test.ts +174 -8
  188. package/src/tests/asset-key-normalization.test.ts +2 -0
  189. package/src/tests/credential-broker.test.ts +27 -2
  190. package/src/tests/mcp-oauth-dcr-reuse.test.ts +743 -0
  191. package/src/tests/mcp-oauth-ensure-token.test.ts +45 -0
  192. package/src/tests/mcp-oauth-manual-client.test.ts +135 -2
  193. package/src/tests/mcp-oauth-queries.test.ts +153 -0
  194. package/src/tests/mcp-oauth-wrapper.test.ts +432 -0
  195. package/src/tests/memory-http-recall-gating.test.ts +2 -0
  196. package/src/tests/openapi-response-contract.test.ts +138 -0
  197. package/src/tests/rbac-admission-e2e.test.ts +68 -0
  198. package/src/tests/script-connections.test.ts +3 -0
  199. package/src/tests/scripts-runtime-fetch.test.ts +121 -0
  200. package/src/tests/scripts-runtime-secret-egress.test.ts +4 -2
  201. package/src/tests/seed-scripts.test.ts +2 -2
  202. package/src/tests/swarm-tool-result-gate.test.ts +32 -0
  203. package/src/tests/task-working-dir.test.ts +8 -4
  204. package/src/tests/whoami-route.test.ts +127 -0
  205. package/src/tests/workflow-engine-v2.test.ts +80 -7
  206. package/src/tests/workflow-swarm-script.test.ts +104 -0
  207. package/src/tools/app-get.ts +10 -2
  208. package/src/tools/app-rollback.ts +1 -0
  209. package/src/tools/app-sync.ts +112 -0
  210. package/src/tools/script-common.ts +20 -0
  211. package/src/tools/tool-config.ts +2 -1
  212. package/src/tools/utils.ts +11 -0
  213. package/src/tools/workflows/create-workflow.ts +4 -1
  214. package/src/types.ts +1394 -1206
  215. package/src/utils/template.ts +8 -1
  216. package/src/utils/zod-openapi.ts +11 -0
  217. package/src/workflows/engine.ts +27 -4
  218. package/src/workflows/executors/swarm-script.ts +28 -0
  219. package/src/workflows/retry-poller.ts +19 -2
  220. package/templates/skills/apps/content.md +94 -5
  221. package/templates/skills/asset-namespaces/content.md +2 -2
  222. package/templates/skills/scheduled-task-resilience/SKILL.md +102 -0
  223. package/templates/skills/scheduled-task-resilience/config.json +14 -0
  224. package/templates/skills/scheduled-task-resilience/content.md +97 -0
  225. package/dist/cli-7wmybrt5.js +0 -115
  226. package/dist/cli-d91pvmq1.js +0 -14
@@ -59,7 +59,7 @@ import { resolveScopedResourceId, scopedResourceScopeIdSchema } from "@/utils/sc
59
59
  import { registerVolatileSecret, scrubSecrets } from "@/utils/secret-scrubber";
60
60
  import { staticOAuthCallbackUri } from "./oauth-callback";
61
61
  import { route } from "./route-def";
62
- import { json, jsonError } from "./utils";
62
+ import { jsonError } from "./utils";
63
63
 
64
64
  const providerSchema = z
65
65
  .string()
@@ -163,6 +163,257 @@ const discoverOAuthAppBodySchema = z.object({
163
163
  url: z.string().url(),
164
164
  });
165
165
 
166
+ // ─── Response schemas ────────────────────────────────────────────────────────
167
+ // Local zod mirrors of the response shapes built by this handler. Nothing in
168
+ // src/types.ts models script connections / OAuth apps / credential bindings,
169
+ // so these are defined here rather than reusing a shared entity schema.
170
+
171
+ const oauthBindingTokenStatusSchema = z.enum([
172
+ "ok",
173
+ "expiring",
174
+ "refresh-failed",
175
+ "revoked",
176
+ "missing",
177
+ ]);
178
+
179
+ const oauthAuthorizationStatusSchema = z.enum(["active", "refresh-failed", "expired", "revoked"]);
180
+
181
+ const credentialAuthKindSchema = z.enum(["config", "oauth"]);
182
+
183
+ const connectionAuthTypeResponseSchema = z.enum(["none", "bearer", "header", "query", "oauth"]);
184
+
185
+ // DB CHECK constraint on script_connections.kind also allows the legacy 'raw'
186
+ // value (never written via this handler's upsert path, but readable via the
187
+ // list/get routes for rows written elsewhere) — wider than the HTTP-body-only
188
+ // `connectionKindSchema` above.
189
+ const connectionRecordKindSchema = z.enum(["raw", "openapi", "mcp", "graphql"]);
190
+
191
+ const bindingSummarySchema = z.object({
192
+ id: z.string(),
193
+ configKey: z.string(),
194
+ authKind: credentialAuthKindSchema,
195
+ oauthAuthorizationId: z.string().optional(),
196
+ tokenStatus: oauthBindingTokenStatusSchema.optional(),
197
+ });
198
+
199
+ const connectionAuthSummaryResponseSchema = z.object({
200
+ type: connectionAuthTypeResponseSchema,
201
+ configKey: z.string().optional(),
202
+ authorizationId: z.string().optional(),
203
+ paramName: z.string().optional(),
204
+ status: oauthBindingTokenStatusSchema.optional(),
205
+ });
206
+
207
+ const decoratedConnectionSchema = z.object({
208
+ id: z.string(),
209
+ slug: z.string(),
210
+ displayName: z.string().nullable(),
211
+ kind: connectionRecordKindSchema,
212
+ scope: scopeSchema,
213
+ scopeId: z.string().nullable(),
214
+ baseUrl: z.string().nullable(),
215
+ baseUrlSource: z.enum(["user", "spec"]),
216
+ baseUrlMismatch: z.object({ specUrl: z.string(), effectiveUrl: z.string() }).optional(),
217
+ allowedHosts: z.array(z.string()),
218
+ credentialBindingId: z.string().nullable(),
219
+ authType: connectionAuthTypeResponseSchema,
220
+ authConfigKey: z.string().nullable(),
221
+ authAuthorizationId: z.string().nullable(),
222
+ authParamName: z.string().nullable(),
223
+ authTemplateOverride: z.string().nullable(),
224
+ authHostsOverride: z.array(z.string()).nullable(),
225
+ openapiSpecSourceKind: z.enum(["url", "inline", "agent_fs", "vendored"]).nullable(),
226
+ openapiSpecSource: z.string().nullable(),
227
+ openapiSpecEtag: z.string().nullable(),
228
+ openapiSpecFetchedAt: z.string().nullable(),
229
+ mcpServerId: z.string().nullable(),
230
+ generatedAt: z.string().nullable(),
231
+ generationError: z.string().nullable(),
232
+ enabled: z.boolean(),
233
+ version: z.number(),
234
+ createdAt: z.string(),
235
+ updatedAt: z.string(),
236
+ createdBy: z.string().nullable(),
237
+ updatedBy: z.string().nullable(),
238
+ operationCount: z.number(),
239
+ toolCount: z.number(),
240
+ credentialBinding: bindingSummarySchema.nullable(),
241
+ auth: connectionAuthSummaryResponseSchema,
242
+ });
243
+
244
+ const connectionDetailSchema = decoratedConnectionSchema.extend({
245
+ operations: z.array(
246
+ z.object({
247
+ name: z.string(),
248
+ method: z.string(),
249
+ path: z.string(),
250
+ parameters: z
251
+ .array(
252
+ z.object({
253
+ name: z.string(),
254
+ in: z.string(),
255
+ required: z.boolean(),
256
+ schema: z.unknown().optional(),
257
+ }),
258
+ )
259
+ .optional(),
260
+ hasBody: z.boolean().optional(),
261
+ successStatus: z.string().optional(),
262
+ requestBodySchema: z.unknown().optional(),
263
+ responseSchema: z.unknown().optional(),
264
+ }),
265
+ ),
266
+ tools: z.array(
267
+ z.object({
268
+ name: z.string(),
269
+ description: z.string().optional(),
270
+ inputSchema: z.unknown().optional(),
271
+ }),
272
+ ),
273
+ graphql: z.boolean(),
274
+ generatedTypes: z.string(),
275
+ specSummary: z
276
+ .object({
277
+ title: z.string().optional(),
278
+ version: z.string().optional(),
279
+ pathCount: z.number(),
280
+ })
281
+ .optional(),
282
+ specPreview: z.object({ json: z.string(), truncated: z.boolean() }).optional(),
283
+ });
284
+
285
+ const scriptCredentialBindingRecordSchema = z.object({
286
+ id: z.string(),
287
+ configKey: z.string(),
288
+ allowedHosts: z.array(z.string()),
289
+ headerTemplate: z.string().optional(),
290
+ queryTemplate: z.string().optional(),
291
+ scope: scopeSchema,
292
+ // ScriptCredentialBindingRecord inherits this from the zod-inferred
293
+ // CredentialBinding type (`.nullable().optional()`); bindingFromRow always
294
+ // sets a concrete `string | null` at runtime, but the declared TS type
295
+ // still allows `undefined`.
296
+ scopeId: z.string().nullable().optional(),
297
+ active: z.boolean(),
298
+ authKind: credentialAuthKindSchema,
299
+ oauthAuthorizationId: z.string().optional(),
300
+ source: z.enum(["default", "user", "migration", "connection"]),
301
+ managedByConnectionId: z.string().nullable(),
302
+ createdAt: z.string(),
303
+ updatedAt: z.string(),
304
+ createdBy: z.string().nullable(),
305
+ updatedBy: z.string().nullable(),
306
+ });
307
+
308
+ const decoratedBindingSchema = scriptCredentialBindingRecordSchema.extend({
309
+ tokenStatus: oauthBindingTokenStatusSchema.optional(),
310
+ });
311
+
312
+ const oauthPresetSchema = z.object({
313
+ id: z.string(),
314
+ displayName: z.string(),
315
+ provider: z.string(),
316
+ authorizeUrl: z.string(),
317
+ tokenUrl: z.string(),
318
+ revocationUrl: z.string().optional(),
319
+ userinfoUrl: z.string().optional(),
320
+ scopes: z.array(z.string()),
321
+ scopeSeparator: z.string().optional(),
322
+ tokenAuthStyle: z.enum(["body", "basic"]).optional(),
323
+ tokenBodyFormat: z.enum(["form", "json"]).optional(),
324
+ requiresRefreshTokenRotation: z.boolean().optional(),
325
+ extraParams: z.record(z.string(), z.string()).optional(),
326
+ setupHints: z.array(z.string()),
327
+ });
328
+
329
+ const sanitizedAuthorizationSchema = z.object({
330
+ id: z.string(),
331
+ label: z.string(),
332
+ accountEmail: z.string().nullable(),
333
+ status: oauthAuthorizationStatusSchema,
334
+ expiresAt: z.string().nullable(),
335
+ scope: z.string().nullable(),
336
+ hasRefreshToken: z.boolean(),
337
+ lastErrorMessage: z.string().nullable(),
338
+ lastRefreshedAt: z.string().nullable(),
339
+ createdAt: z.string(),
340
+ updatedAt: z.string(),
341
+ });
342
+
343
+ const sanitizedOAuthAppSchema = z.object({
344
+ id: z.string(),
345
+ provider: z.string(),
346
+ clientId: z.string(),
347
+ authorizeUrl: z.string(),
348
+ tokenUrl: z.string(),
349
+ redirectUri: z.string(),
350
+ scopes: z.array(z.string()),
351
+ extraParams: z.record(z.string(), z.string()).optional(),
352
+ tokenAuthStyle: z.enum(["body", "basic"]),
353
+ tokenBodyFormat: z.enum(["form", "json"]),
354
+ source: z.string(),
355
+ tokenStatus: oauthBindingTokenStatusSchema,
356
+ expiresAt: z.string().nullable(),
357
+ lastRefreshedAt: z.string().nullable(),
358
+ authorizations: z.array(sanitizedAuthorizationSchema),
359
+ createdAt: z.string(),
360
+ updatedAt: z.string(),
361
+ });
362
+
363
+ const integrationsCatalogEntrySchema = z.object({
364
+ id: z.string(),
365
+ kind: z.string(),
366
+ slug: z.string(),
367
+ name: z.string(),
368
+ description: z.string(),
369
+ url: z.string(),
370
+ icon: z.string().nullable(),
371
+ domain: z.string(),
372
+ categories: z.array(z.string()),
373
+ feeds: z.array(z.string()),
374
+ vendoredSlug: z.string().optional(),
375
+ presetId: z.string().optional(),
376
+ });
377
+
378
+ const integrationsCatalogListSchema = z.object({
379
+ entries: z.array(integrationsCatalogEntrySchema),
380
+ cachedAt: z.string(),
381
+ partial: z.boolean(),
382
+ });
383
+
384
+ const integrationsSurfaceMechanicsSchema = z.object({
385
+ in: z.string(),
386
+ headerName: z.string().nullable(),
387
+ scheme: z.string().nullable(),
388
+ });
389
+
390
+ const integrationsSurfaceEntrySchema = z.object({
391
+ type: z.string(),
392
+ name: z.string(),
393
+ url: z.string().nullable(),
394
+ docs: z.string().nullable(),
395
+ spec: z.string().nullable(),
396
+ auth: z.object({
397
+ required: z.boolean(),
398
+ credentialIds: z.array(z.string()),
399
+ mechanics: integrationsSurfaceMechanicsSchema.nullable(),
400
+ }),
401
+ });
402
+
403
+ const integrationsSurfaceCredentialSchema = z.object({
404
+ type: z.string(),
405
+ label: z.string(),
406
+ generateUrl: z.string().nullable(),
407
+ setup: z.string().nullable(),
408
+ });
409
+
410
+ const integrationsSurfacePayloadSchema = z.object({
411
+ domain: z.string(),
412
+ summary: z.string(),
413
+ surfaces: z.array(integrationsSurfaceEntrySchema),
414
+ credentials: z.record(z.string(), integrationsSurfaceCredentialSchema),
415
+ });
416
+
166
417
  const listConnectionsRoute = route({
167
418
  method: "get",
168
419
  path: "/api/script-connections",
@@ -174,7 +425,10 @@ const listConnectionsRoute = route({
174
425
  tags: ["Script Connections"],
175
426
  query: listConnectionsQuerySchema,
176
427
  responses: {
177
- 200: { description: "Script connections" },
428
+ 200: {
429
+ description: "Script connections",
430
+ schema: z.object({ connections: z.array(decoratedConnectionSchema) }),
431
+ },
178
432
  400: { description: "Validation error" },
179
433
  },
180
434
  });
@@ -188,7 +442,10 @@ const getConnectionRoute = route({
188
442
  tags: ["Script Connections"],
189
443
  params: idParamsSchema,
190
444
  responses: {
191
- 200: { description: "Script connection detail" },
445
+ 200: {
446
+ description: "Script connection detail",
447
+ schema: z.object({ connection: connectionDetailSchema }),
448
+ },
192
449
  404: { description: "Script connection not found" },
193
450
  },
194
451
  });
@@ -202,7 +459,10 @@ const upsertConnectionRoute = route({
202
459
  tags: ["Script Connections"],
203
460
  body: upsertConnectionBodySchema,
204
461
  responses: {
205
- 200: { description: "Saved script connection" },
462
+ 200: {
463
+ description: "Saved script connection",
464
+ schema: z.object({ connection: decoratedConnectionSchema }),
465
+ },
206
466
  400: { description: "Validation or generation error" },
207
467
  403: { description: "Only the lead agent can manage script connections" },
208
468
  },
@@ -218,7 +478,10 @@ const refreshConnectionRoute = route({
218
478
  tags: ["Script Connections"],
219
479
  params: idParamsSchema,
220
480
  responses: {
221
- 200: { description: "Refreshed script connection" },
481
+ 200: {
482
+ description: "Refreshed script connection",
483
+ schema: z.object({ connection: decoratedConnectionSchema }),
484
+ },
222
485
  400: { description: "Connection cannot be refreshed" },
223
486
  403: { description: "Only the lead agent can manage script connections" },
224
487
  404: { description: "Script connection not found" },
@@ -236,7 +499,10 @@ const setConnectionEnabledRoute = route({
236
499
  params: idParamsSchema,
237
500
  body: disableConnectionBodySchema,
238
501
  responses: {
239
- 200: { description: "Updated script connection" },
502
+ 200: {
503
+ description: "Updated script connection",
504
+ schema: z.object({ connection: decoratedConnectionSchema }),
505
+ },
240
506
  403: { description: "Only the lead agent can manage script connections" },
241
507
  404: { description: "Script connection not found" },
242
508
  },
@@ -254,7 +520,10 @@ const listCredentialBindingsRoute = route({
254
520
  tags: ["Script Connections"],
255
521
  query: z.object({ includeManaged: z.enum(["true", "false"]).optional() }),
256
522
  responses: {
257
- 200: { description: "Credential bindings" },
523
+ 200: {
524
+ description: "Credential bindings",
525
+ schema: z.object({ bindings: z.array(decoratedBindingSchema) }),
526
+ },
258
527
  },
259
528
  });
260
529
 
@@ -267,7 +536,10 @@ const upsertCredentialBindingRoute = route({
267
536
  tags: ["Script Connections"],
268
537
  body: credentialBindingBodySchema,
269
538
  responses: {
270
- 200: { description: "Saved credential binding" },
539
+ 200: {
540
+ description: "Saved credential binding",
541
+ schema: z.object({ binding: decoratedBindingSchema }),
542
+ },
271
543
  400: { description: "Validation error" },
272
544
  403: { description: "Only the lead agent can manage script connections" },
273
545
  },
@@ -282,7 +554,10 @@ const listOAuthAppsRoute = route({
282
554
  summary: "List OAuth apps for script credential bindings",
283
555
  tags: ["Script Connections"],
284
556
  responses: {
285
- 200: { description: "OAuth apps without client secrets" },
557
+ 200: {
558
+ description: "OAuth apps without client secrets",
559
+ schema: z.object({ oauthApps: z.array(sanitizedOAuthAppSchema) }),
560
+ },
286
561
  },
287
562
  });
288
563
 
@@ -296,7 +571,10 @@ const listOAuthPresetsRoute = route({
296
571
  "Static curated OAuth presets (endpoints, scopes, quirks, and setup hints). Contains no secrets; client credentials are always customer-supplied.",
297
572
  tags: ["Script Connections"],
298
573
  responses: {
299
- 200: { description: "Curated OAuth presets" },
574
+ 200: {
575
+ description: "Curated OAuth presets",
576
+ schema: z.object({ presets: z.array(oauthPresetSchema) }),
577
+ },
300
578
  },
301
579
  });
302
580
 
@@ -309,7 +587,16 @@ const upsertOAuthAppRoute = route({
309
587
  tags: ["Script Connections"],
310
588
  body: oauthAppBodySchema,
311
589
  responses: {
312
- 200: { description: "Saved OAuth app without client secret" },
590
+ 200: {
591
+ description: "Saved OAuth app without client secret",
592
+ schema: z.object({
593
+ // `.find()` after create/update — always resolves in practice, but the
594
+ // lookup is not provably total, so this stays honestly optional.
595
+ oauthApp: sanitizedOAuthAppSchema.optional(),
596
+ redirectUri: z.string(),
597
+ setupHints: z.array(z.string()).optional(),
598
+ }),
599
+ },
313
600
  400: { description: "Validation error" },
314
601
  403: { description: "Only the lead agent can manage script connections" },
315
602
  },
@@ -325,7 +612,15 @@ const discoverOAuthAppRoute = route({
325
612
  tags: ["Script Connections"],
326
613
  body: discoverOAuthAppBodySchema,
327
614
  responses: {
328
- 200: { description: "Discovered OAuth metadata" },
615
+ 200: {
616
+ description: "Discovered OAuth metadata",
617
+ schema: z.object({
618
+ authorizeUrl: z.string(),
619
+ tokenUrl: z.string(),
620
+ scopes: z.array(z.string()),
621
+ sourceUrl: z.string(),
622
+ }),
623
+ },
329
624
  400: { description: "Discovery failed" },
330
625
  403: { description: "Only the lead agent can manage script connections" },
331
626
  },
@@ -341,7 +636,10 @@ const deleteOAuthAppRoute = route({
341
636
  tags: ["Script Connections"],
342
637
  params: providerParamsSchema,
343
638
  responses: {
344
- 200: { description: "OAuth app deleted" },
639
+ 200: {
640
+ description: "OAuth app deleted",
641
+ schema: z.object({ success: z.literal(true), warnings: z.array(z.string()).optional() }),
642
+ },
345
643
  403: { description: "Only the lead agent can manage script connections" },
346
644
  404: { description: "OAuth app not found" },
347
645
  },
@@ -373,7 +671,15 @@ const authorizeUrlRoute = route({
373
671
  params: oauthResourceIdParamsSchema,
374
672
  body: authorizeUrlBodySchema,
375
673
  responses: {
376
- 200: { description: "OAuth authorization URL + state" },
674
+ 200: {
675
+ description: "OAuth authorization URL + state",
676
+ schema: z.object({
677
+ authorizeUrl: z.string(),
678
+ state: z.string(),
679
+ label: z.string(),
680
+ redirectUri: z.string(),
681
+ }),
682
+ },
377
683
  403: { description: "Only the lead agent can manage OAuth authorizations" },
378
684
  404: { description: "OAuth app not found" },
379
685
  },
@@ -389,7 +695,10 @@ const listAuthorizationsRoute = route({
389
695
  tags: ["Script Connections"],
390
696
  params: oauthResourceIdParamsSchema,
391
697
  responses: {
392
- 200: { description: "Authorizations without token material" },
698
+ 200: {
699
+ description: "Authorizations without token material",
700
+ schema: z.object({ authorizations: z.array(sanitizedAuthorizationSchema) }),
701
+ },
393
702
  404: { description: "OAuth app not found" },
394
703
  },
395
704
  });
@@ -403,7 +712,10 @@ const deleteAuthorizationRoute = route({
403
712
  tags: ["Script Connections"],
404
713
  params: oauthResourceIdParamsSchema,
405
714
  responses: {
406
- 200: { description: "Authorization revoked + deleted" },
715
+ 200: {
716
+ description: "Authorization revoked + deleted",
717
+ schema: z.object({ deleted: z.literal(true), revocationAttempted: z.boolean() }),
718
+ },
407
719
  403: { description: "Only the lead agent can manage OAuth authorizations" },
408
720
  404: { description: "Authorization not found" },
409
721
  },
@@ -419,7 +731,14 @@ const refreshAuthorizationRoute = route({
419
731
  tags: ["Script Connections"],
420
732
  params: oauthResourceIdParamsSchema,
421
733
  responses: {
422
- 200: { description: "Refresh result with token status and new expiry" },
734
+ 200: {
735
+ description: "Refresh result with token status and new expiry",
736
+ schema: z.object({
737
+ ok: z.literal(true),
738
+ status: oauthAuthorizationStatusSchema,
739
+ expiresAt: z.string().nullable(),
740
+ }),
741
+ },
423
742
  400: { description: "No refresh token stored" },
424
743
  403: { description: "Only the lead agent can manage OAuth authorizations" },
425
744
  404: { description: "Authorization not found" },
@@ -436,7 +755,7 @@ const integrationsCatalogRoute = route({
436
755
  summary: "Proxy integrations.sh catalog entries",
437
756
  tags: ["Script Connections"],
438
757
  responses: {
439
- 200: { description: "Integrations catalog entries" },
758
+ 200: { description: "Integrations catalog entries", schema: integrationsCatalogListSchema },
440
759
  502: { description: "Catalog upstream unavailable" },
441
760
  },
442
761
  });
@@ -456,7 +775,10 @@ const integrationsSurfaceRoute = route({
456
775
  tags: ["Script Connections"],
457
776
  params: z.object({ domain: surfaceDomainSchema }),
458
777
  responses: {
459
- 200: { description: "Trimmed integration surface details for a domain" },
778
+ 200: {
779
+ description: "Trimmed integration surface details for a domain",
780
+ schema: integrationsSurfacePayloadSchema,
781
+ },
460
782
  404: { description: "No surface data for this domain" },
461
783
  502: { description: "Surface upstream unavailable" },
462
784
  },
@@ -472,7 +794,13 @@ const disconnectOAuthAppRoute = route({
472
794
  tags: ["Script Connections"],
473
795
  params: providerParamsSchema,
474
796
  responses: {
475
- 200: { description: "Disconnect result" },
797
+ 200: {
798
+ description: "Disconnect result",
799
+ schema: z.union([
800
+ z.object({ disconnected: z.literal(false), message: z.string() }),
801
+ z.object({ disconnected: z.literal(true), revocationAttempted: z.boolean() }),
802
+ ]),
803
+ },
476
804
  403: { description: "Only the lead agent can manage script connections" },
477
805
  404: { description: "OAuth app not found" },
478
806
  },
@@ -488,7 +816,14 @@ const refreshOAuthAppTokensRoute = route({
488
816
  tags: ["Script Connections"],
489
817
  params: providerParamsSchema,
490
818
  responses: {
491
- 200: { description: "Refresh result with token status and new expiry" },
819
+ 200: {
820
+ description: "Refresh result with token status and new expiry",
821
+ schema: z.object({
822
+ refreshed: z.literal(true),
823
+ tokenStatus: oauthBindingTokenStatusSchema,
824
+ expiresAt: z.string().nullable(),
825
+ }),
826
+ },
492
827
  400: { description: "No stored tokens or provider does not support refresh" },
493
828
  403: { description: "Only the lead agent can manage script connections" },
494
829
  404: { description: "OAuth app not found" },
@@ -1515,7 +1850,7 @@ export async function handleScriptConnections(
1515
1850
  if (listConnectionsRoute.match(req.method, pathSegments)) {
1516
1851
  const parsed = await listConnectionsRoute.parse(req, res, pathSegments, queryParams);
1517
1852
  if (!parsed) return true;
1518
- json(res, { connections: listConnections(parsed.query) });
1853
+ listConnectionsRoute.respond(res, 200, { connections: listConnections(parsed.query) });
1519
1854
  return true;
1520
1855
  }
1521
1856
 
@@ -1530,7 +1865,7 @@ export async function handleScriptConnections(
1530
1865
  jsonError(res, "Script connection not found.", 404);
1531
1866
  return true;
1532
1867
  }
1533
- json(res, { connection: connectionDetail(connection) });
1868
+ getConnectionRoute.respond(res, 200, { connection: connectionDetail(connection) });
1534
1869
  return true;
1535
1870
  }
1536
1871
 
@@ -1646,7 +1981,9 @@ export async function handleScriptConnections(
1646
1981
  userId,
1647
1982
  });
1648
1983
 
1649
- json(res, { connection: decorateConnections([connection])[0] });
1984
+ upsertConnectionRoute.respond(res, 200, {
1985
+ connection: decorateConnections([connection])[0]!,
1986
+ });
1650
1987
  } catch (err) {
1651
1988
  jsonError(res, err instanceof Error ? err.message : String(err), 400);
1652
1989
  }
@@ -1667,7 +2004,9 @@ export async function handleScriptConnections(
1667
2004
  jsonError(res, "Script connection not found.", 404);
1668
2005
  return true;
1669
2006
  }
1670
- json(res, { connection: decorateConnections([refreshed])[0] });
2007
+ refreshConnectionRoute.respond(res, 200, {
2008
+ connection: decorateConnections([refreshed])[0]!,
2009
+ });
1671
2010
  } catch (err) {
1672
2011
  jsonError(res, err instanceof Error ? err.message : String(err), 400);
1673
2012
  }
@@ -1687,13 +2026,13 @@ export async function handleScriptConnections(
1687
2026
  jsonError(res, "Script connection not found.", 404);
1688
2027
  return true;
1689
2028
  }
1690
- json(res, { connection: decorateConnections([updated])[0] });
2029
+ setConnectionEnabledRoute.respond(res, 200, { connection: decorateConnections([updated])[0]! });
1691
2030
  return true;
1692
2031
  }
1693
2032
 
1694
2033
  if (listCredentialBindingsRoute.match(req.method, pathSegments)) {
1695
2034
  const includeManaged = queryParams.get("includeManaged") === "true";
1696
- json(res, {
2035
+ listCredentialBindingsRoute.respond(res, 200, {
1697
2036
  bindings: listRelationalCredentialBindings({
1698
2037
  includeInactive: true,
1699
2038
  excludeManaged: !includeManaged,
@@ -1748,7 +2087,7 @@ export async function handleScriptConnections(
1748
2087
  oauthAuthorizationId: nextBinding.oauthAuthorizationId ?? null,
1749
2088
  userId: resolveHttpAuditUserId(req, agentId),
1750
2089
  });
1751
- json(res, { binding: decorateBinding(binding) });
2090
+ upsertCredentialBindingRoute.respond(res, 200, { binding: decorateBinding(binding) });
1752
2091
  } catch (err) {
1753
2092
  jsonError(res, err instanceof Error ? err.message : String(err), 400);
1754
2093
  }
@@ -1756,12 +2095,12 @@ export async function handleScriptConnections(
1756
2095
  }
1757
2096
 
1758
2097
  if (listOAuthPresetsRoute.match(req.method, pathSegments)) {
1759
- json(res, { presets: listOAuthPresets() });
2098
+ listOAuthPresetsRoute.respond(res, 200, { presets: listOAuthPresets() });
1760
2099
  return true;
1761
2100
  }
1762
2101
 
1763
2102
  if (listOAuthAppsRoute.match(req.method, pathSegments)) {
1764
- json(res, { oauthApps: listOAuthApps() });
2103
+ listOAuthAppsRoute.respond(res, 200, { oauthApps: listOAuthApps() });
1765
2104
  return true;
1766
2105
  }
1767
2106
 
@@ -1869,7 +2208,7 @@ export async function handleScriptConnections(
1869
2208
  savedId = createOAuthApp(provider, appData);
1870
2209
  }
1871
2210
  const app = listOAuthApps().find((row) => row.id === savedId);
1872
- json(res, {
2211
+ upsertOAuthAppRoute.respond(res, 200, {
1873
2212
  oauthApp: app,
1874
2213
  redirectUri,
1875
2214
  ...(hydrated ? { setupHints: hydrated.setupHints } : {}),
@@ -1886,7 +2225,7 @@ export async function handleScriptConnections(
1886
2225
  if (!ensureOAuthAppAdmin(req, res, agentId)) return true;
1887
2226
  try {
1888
2227
  const discovered = await discoverOAuthApp(parsed.body.url);
1889
- json(res, discovered);
2228
+ discoverOAuthAppRoute.respond(res, 200, discovered);
1890
2229
  } catch (err) {
1891
2230
  jsonError(res, err instanceof Error ? err.message : String(err), 400);
1892
2231
  }
@@ -1902,7 +2241,7 @@ export async function handleScriptConnections(
1902
2241
  jsonError(res, `OAuth app ${parsed.params.provider} not found.`, 404);
1903
2242
  return true;
1904
2243
  }
1905
- json(res, {
2244
+ deleteOAuthAppRoute.respond(res, 200, {
1906
2245
  success: true,
1907
2246
  ...(deletion.warnings.length > 0 ? { warnings: deletion.warnings } : {}),
1908
2247
  });
@@ -1917,7 +2256,7 @@ export async function handleScriptConnections(
1917
2256
  jsonError(res, `OAuth app ${parsed.params.id} not found.`, 404);
1918
2257
  return true;
1919
2258
  }
1920
- json(res, {
2259
+ listAuthorizationsRoute.respond(res, 200, {
1921
2260
  authorizations: listAuthorizationsForApp(app.id).map(sanitizeAuthorization),
1922
2261
  });
1923
2262
  return true;
@@ -1944,7 +2283,7 @@ export async function handleScriptConnections(
1944
2283
  ...(parsed.body?.finalRedirect ? { finalRedirect: parsed.body.finalRedirect } : {}),
1945
2284
  userId: resolveHttpAuditUserId(req, agentId),
1946
2285
  });
1947
- json(res, {
2286
+ authorizeUrlRoute.respond(res, 200, {
1948
2287
  authorizeUrl: result.url,
1949
2288
  state: result.state,
1950
2289
  label,
@@ -1972,7 +2311,7 @@ export async function handleScriptConnections(
1972
2311
  revocationAttempted = await attemptRemoteRevocation(app, authorization.accessToken);
1973
2312
  }
1974
2313
  deleteAuthorizationById(authorization.id);
1975
- json(res, { deleted: true, revocationAttempted });
2314
+ deleteAuthorizationRoute.respond(res, 200, { deleted: true, revocationAttempted });
1976
2315
  return true;
1977
2316
  }
1978
2317
 
@@ -2024,21 +2363,25 @@ export async function handleScriptConnections(
2024
2363
  jsonError(res, `Authorization ${parsed.params.id} not found.`, 404);
2025
2364
  return true;
2026
2365
  }
2027
- json(res, { ok: true, status: refreshed.status, expiresAt: refreshed.expiresAt });
2366
+ refreshAuthorizationRoute.respond(res, 200, {
2367
+ ok: true,
2368
+ status: refreshed.status,
2369
+ expiresAt: refreshed.expiresAt,
2370
+ });
2028
2371
  return true;
2029
2372
  }
2030
2373
 
2031
2374
  if (integrationsCatalogRoute.match(req.method, pathSegments)) {
2032
2375
  try {
2033
2376
  const catalog = await fetchIntegrationsCatalog();
2034
- json(res, {
2377
+ integrationsCatalogRoute.respond(res, 200, {
2035
2378
  ...catalog,
2036
2379
  entries: mergeBlessedCatalogEntries(catalog.entries),
2037
2380
  partial: false,
2038
2381
  });
2039
2382
  } catch (err) {
2040
2383
  if (BLESSED_CATALOG_ENTRIES.length > 0) {
2041
- json(res, {
2384
+ integrationsCatalogRoute.respond(res, 200, {
2042
2385
  entries: BLESSED_CATALOG_ENTRIES,
2043
2386
  cachedAt: new Date().toISOString(),
2044
2387
  partial: true,
@@ -2058,7 +2401,11 @@ export async function handleScriptConnections(
2058
2401
  const parsed = await integrationsSurfaceRoute.parse(req, res, pathSegments, queryParams);
2059
2402
  if (!parsed) return true;
2060
2403
  try {
2061
- json(res, await fetchIntegrationsSurface(parsed.params.domain));
2404
+ integrationsSurfaceRoute.respond(
2405
+ res,
2406
+ 200,
2407
+ await fetchIntegrationsSurface(parsed.params.domain),
2408
+ );
2062
2409
  } catch (err) {
2063
2410
  if (err instanceof SurfaceNotFoundError) {
2064
2411
  jsonError(res, err.message, 404);
@@ -2085,12 +2432,15 @@ export async function handleScriptConnections(
2085
2432
  }
2086
2433
  const tokens = getOAuthTokens(parsed.params.provider);
2087
2434
  if (!tokens) {
2088
- json(res, { disconnected: false, message: "no stored tokens" });
2435
+ disconnectOAuthAppRoute.respond(res, 200, {
2436
+ disconnected: false,
2437
+ message: "no stored tokens",
2438
+ });
2089
2439
  return true;
2090
2440
  }
2091
2441
  const revocationAttempted = await attemptRemoteRevocation(app, tokens.accessToken);
2092
2442
  deleteOAuthTokens(parsed.params.provider);
2093
- json(res, { disconnected: true, revocationAttempted });
2443
+ disconnectOAuthAppRoute.respond(res, 200, { disconnected: true, revocationAttempted });
2094
2444
  return true;
2095
2445
  }
2096
2446
 
@@ -2126,7 +2476,7 @@ export async function handleScriptConnections(
2126
2476
  return true;
2127
2477
  }
2128
2478
 
2129
- json(res, {
2479
+ refreshOAuthAppTokensRoute.respond(res, 200, {
2130
2480
  refreshed: true,
2131
2481
  tokenStatus: getOAuthBindingTokenStatus(tokens.id),
2132
2482
  expiresAt: getOAuthTokens(provider)?.expiresAt ?? null,