@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.
- package/README.md +2 -2
- package/dist/{actions-kfv2dawe.js → actions-w53mbns9.js} +11 -11
- package/dist/{anthropic-messages-3xgme8my.js → anthropic-messages-rjmd2e7n.js} +5 -5
- package/dist/{app-q8vjk6e8.js → app-m9gwqsqv.js} +8 -8
- package/dist/{artifact-hg4t1nx7.js → artifact-jysj3wrg.js} +2 -2
- package/dist/{assistant-g037r345.js → assistant-bp65m6f3.js} +13 -13
- package/dist/{azure-openai-responses-xa4sh9f6.js → azure-openai-responses-c0yjmbfd.js} +8 -5
- package/dist/{boot-reembed-18pzsrxa.js → boot-reembed-62p0at2k.js} +6 -6
- package/dist/{boot-reembed-wpks2cj8.js → boot-reembed-xmfp6wdh.js} +7 -7
- package/dist/{boot-scrub-logs-n7szd57r.js → boot-scrub-logs-rr8q2dms.js} +5 -5
- package/dist/{claude-adapter-qz4r0ajt.js → claude-adapter-2y47mrkd.js} +9 -9
- package/dist/{cli-mhjnbgb6.js → cli-0rcyq6d9.js} +450 -186
- package/dist/{cli-ttkmehy0.js → cli-2q72qz2s.js} +274 -25
- package/dist/{cli-gztgc3hz.js → cli-2wadx7p2.js} +2 -2
- package/dist/cli-3fj60etf.js +153 -0
- package/dist/{cli-7ecgm5v0.js → cli-3kf51j15.js} +1 -1
- package/dist/{cli-vgexpfsr.js → cli-57khth04.js} +6 -3
- package/dist/{cli-2mbe7vk0.js → cli-5dsbcjjw.js} +1 -1
- package/dist/{cli-etr62zy6.js → cli-5kx36evn.js} +2 -2
- package/dist/{cli-v2tr6amf.js → cli-5n6e5yfn.js} +30 -8
- package/dist/{cli-hx02s9zq.js → cli-5v4rw6yd.js} +2 -2
- package/dist/{cli-v37yr6mf.js → cli-5ze9rv97.js} +4 -4
- package/dist/{cli-ch7t5v7n.js → cli-6map995v.js} +9 -4
- package/dist/{cli-h0457a67.js → cli-70xqj0z3.js} +3 -3
- package/dist/{cli-kk4f9r17.js → cli-72q8m8gk.js} +8 -6
- package/dist/{cli-7padk69a.js → cli-78xwvkhj.js} +2 -2
- package/dist/{cli-dxtxacy7.js → cli-9dr5qzc0.js} +45 -8
- package/dist/{cli-17zxdgsq.js → cli-a40rnf71.js} +6 -6
- package/dist/{cli-t53vnnxz.js → cli-a4jzdpa3.js} +2514 -1055
- package/dist/{cli-w10fkrf1.js → cli-akdvwc9v.js} +354 -115
- package/dist/{cli-1qkk5er0.js → cli-bmze986d.js} +1 -1
- package/dist/{cli-ga12dc1d.js → cli-bqw0mt1n.js} +3 -3
- package/dist/{cli-0sf92efp.js → cli-cdeg08wx.js} +1 -1
- package/dist/{cli-a8ynhkyj.js → cli-cvgjwnh1.js} +17 -15
- package/dist/{cli-9fxg3v5m.js → cli-d2tfkcga.js} +4 -4
- package/dist/{cli-nhcd7x43.js → cli-d3x3x1c9.js} +2 -2
- package/dist/{cli-6nh2qxvh.js → cli-darcwax3.js} +1 -1
- package/dist/{cli-yjbgmysx.js → cli-e7ckcg4v.js} +12 -12
- package/dist/{cli-4n2gcjpd.js → cli-efxfnq6q.js} +4 -3
- package/dist/{cli-z9xqqhfk.js → cli-er3a4bb0.js} +1 -1
- package/dist/{cli-xg2eezyv.js → cli-f1am9szv.js} +1 -1
- package/dist/{cli-xy4cwsm9.js → cli-fywms69x.js} +1 -1
- package/dist/{cli-33jj76y8.js → cli-g6b8x1h8.js} +1 -1
- package/dist/{cli-epsvdckx.js → cli-gm9cbhbq.js} +20 -10
- package/dist/{cli-rkndnn90.js → cli-k9qkf1hw.js} +6 -4
- package/dist/{cli-zejvt395.js → cli-kh3ba2zf.js} +1 -1
- package/dist/{cli-gb25p1h2.js → cli-nm5gtp1x.js} +2 -2
- package/dist/{cli-akxnpx17.js → cli-pm6xbh84.js} +1829 -1629
- package/dist/{cli-2k0ztbfp.js → cli-psh3c4va.js} +1 -1
- package/dist/{cli-39vgrsep.js → cli-qv61hp37.js} +2 -2
- package/dist/{cli-hv2fndhm.js → cli-r6kqqbj0.js} +6 -4
- package/dist/{cli-1y1cm570.js → cli-rzbpmxdr.js} +1 -1
- package/dist/{cli-mmrk00vp.js → cli-t1xze5qh.js} +1 -1
- package/dist/{cli-g252a9ky.js → cli-t55yw3a1.js} +2 -2
- package/dist/{cli-7bz8dzqa.js → cli-t76vw5db.js} +1184 -1522
- package/dist/{cli-4n4mdv21.js → cli-xpxde7rm.js} +7 -3
- package/dist/{cli-fpaa5k2q.js → cli-yw8vghx2.js} +54 -18
- package/dist/cli-zsspvvm2.js +490 -0
- package/dist/cli.js +27 -25
- package/dist/{codex-adapter-7v6t7ff6.js → codex-adapter-d24q75wk.js} +7 -7
- package/dist/{codex-hook-amndawyb.js → codex-hook-hq54t9ft.js} +2 -2
- package/dist/{codex-session-runner-e7j4khe2.js → codex-session-runner-g50vcsex.js} +7 -7
- package/dist/{commands-ex0k5tbf.js → commands-c3nrc5hg.js} +5 -5
- package/dist/{db-df7jatfc.js → db-nt7fg3tw.js} +5 -5
- package/dist/{google-generative-ai-y42hnw9p.js → google-generative-ai-hysrd8qx.js} +7 -5
- package/dist/{google-vertex-4sg66gb6.js → google-vertex-p5arrga0.js} +7 -5
- package/dist/{handlers-1j2cfqt2.js → handlers-fkr6swat.js} +13 -13
- package/dist/{hook-nx9a0mj3.js → hook-77wpvnjc.js} +7 -7
- package/dist/{hook-w6yaf6sy.js → hook-g019yehy.js} +8 -8
- package/dist/{http-4tz5h8wg.js → http-jewj0syk.js} +2993 -716
- package/dist/{index-0bpkzb09.js → index-070v4eyb.js} +3 -3
- package/dist/{index-0s67qv7h.js → index-6gm5he8h.js} +11 -11
- package/dist/{index-35gtv6gj.js → index-7n2xpbd4.js} +310 -40
- package/dist/{index-ker3gccb.js → index-9znhar3x.js} +4 -4
- package/dist/{index-5n2c8yay.js → index-hase6zbe.js} +2 -2
- package/dist/{index-kcf3tm9h.js → index-hfvggd0z.js} +12 -12
- package/dist/{index-7c0jqna0.js → index-hp1kdbjj.js} +13 -13
- package/dist/{keepalive-3j4a9168.js → keepalive-j0fjvqfd.js} +7 -7
- package/dist/{lead-6kpevymb.js → lead-1n8x30xe.js} +33 -33
- package/dist/{maintenance-m1k82ean.js → maintenance-gvev11sa.js} +7 -7
- package/dist/{mistral-conversations-bjkdfb6z.js → mistral-conversations-81fwav0j.js} +6 -6
- package/dist/{oauth-refresh-sweep-881k8e4q.js → oauth-refresh-sweep-s9ecsvh0.js} +6 -6
- package/dist/{onboard-c0ykffjn.js → onboard-pvm5b38w.js} +2 -2
- package/dist/{openai-codex-responses-xetseqvt.js → openai-codex-responses-5g27pd6f.js} +43 -23
- package/dist/{openai-completions-5r2g5axa.js → openai-completions-3vkmfz2a.js} +55 -11
- package/dist/{openai-responses-ce67hd23.js → openai-responses-dg4t7j7a.js} +8 -5
- package/dist/{openrouter-images-pzfp22jb.js → openrouter-images-g3gm8931.js} +1 -1
- package/dist/{otel-impl-sqagjerv.js → otel-impl-p6jvgd9c.js} +6 -4
- package/dist/{otel-3mp81ppd.js → otel-nqa12wf3.js} +2 -2
- package/dist/{pi-mono-adapter-k584k16g.js → pi-mono-adapter-t091s27g.js} +17117 -7517
- package/dist/{pricing-refresh-y82whbf9.js → pricing-refresh-k49rcyed.js} +7 -7
- package/dist/{rbac-roles-nvkqmzyd.js → rbac-roles-30pspvy7.js} +7 -7
- package/dist/{rbac-roles-4ht8gegc.js → rbac-roles-cr80g96t.js} +6 -6
- package/dist/{render-v2-wvqdgvvp.js → render-v2-xm457ng1.js} +8 -8
- package/dist/{seed-pricing-fy3rxhde.js → seed-pricing-yhpv35fr.js} +6 -6
- package/dist/{setup-ak5cnc6f.js → setup-nytnmwwr.js} +2 -2
- package/dist/{worker-d8769q8a.js → worker-msytr2cw.js} +33 -33
- package/openapi.json +34039 -11036
- package/package.json +8 -6
- package/src/apps/definition.ts +368 -26
- package/src/apps/row-store.ts +66 -10
- package/src/apps/schema-migrate.ts +95 -1
- package/src/apps/store.ts +13 -4
- package/src/apps/sync-run-as.ts +18 -0
- package/src/apps/sync.ts +934 -0
- package/src/apps/version.ts +16 -2
- package/src/assets/key.ts +8 -1
- package/src/be/asset-key-audit.ts +3 -1
- package/src/be/db-queries/mcp-oauth.ts +297 -24
- package/src/be/db.ts +42 -1
- package/src/be/migrations/129_asset_keys_apps_scripts.sql +161 -0
- package/src/be/scripts/db.ts +5 -2
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed-scripts/catalog/app-sync-run.ts +32 -0
- package/src/be/seed-scripts/catalog/github-issues-pull.ts +102 -0
- package/src/be/seed-scripts/index.ts +18 -0
- package/src/be/seed-skills/index.ts +7 -0
- package/src/http/active-sessions.ts +26 -18
- package/src/http/agents.ts +94 -27
- package/src/http/api-keys.ts +91 -17
- package/src/http/approval-requests.ts +87 -10
- package/src/http/apps.ts +361 -53
- package/src/http/assets.ts +165 -31
- package/src/http/budgets.ts +7 -5
- package/src/http/codex-oauth-keep-warm.ts +21 -3
- package/src/http/config.ts +46 -16
- package/src/http/context.ts +33 -6
- package/src/http/ecosystem.ts +20 -6
- package/src/http/events.ts +24 -9
- package/src/http/favorites.ts +20 -6
- package/src/http/fs.ts +60 -17
- package/src/http/heartbeat.ts +18 -4
- package/src/http/inbox-state.ts +12 -6
- package/src/http/integrations.ts +25 -5
- package/src/http/kv.ts +36 -15
- package/src/http/mcp-bridge.ts +5 -1
- package/src/http/mcp-oauth.ts +332 -17
- package/src/http/mcp-servers.ts +56 -18
- package/src/http/memory.ts +282 -29
- package/src/http/metrics.ts +80 -17
- package/src/http/oauth-callback.ts +10 -4
- package/src/http/oauth-generic.ts +5 -1
- package/src/http/oauth-locks.ts +6 -3
- package/src/http/openapi.ts +57 -9
- package/src/http/page-proxy.ts +25 -5
- package/src/http/pages-public.ts +37 -20
- package/src/http/pages.ts +93 -28
- package/src/http/poll.ts +89 -5
- package/src/http/pricing.ts +9 -6
- package/src/http/prompt-templates.ts +85 -21
- package/src/http/route-def.ts +67 -4
- package/src/http/schedules.ts +63 -14
- package/src/http/script-connection-proxy.ts +30 -4
- package/src/http/script-connections.ts +393 -43
- package/src/http/script-runs.ts +73 -19
- package/src/http/scripts.ts +257 -39
- package/src/http/session-data.ts +87 -14
- package/src/http/sessions.ts +80 -5
- package/src/http/skills.ts +115 -35
- package/src/http/stats.ts +94 -13
- package/src/http/task-templates.ts +6 -4
- package/src/http/tasks.ts +230 -61
- package/src/http/trackers/jira.ts +90 -30
- package/src/http/trackers/linear.ts +46 -12
- package/src/http/users.ts +162 -25
- package/src/http/utils.ts +12 -2
- package/src/http/webhooks.ts +30 -16
- package/src/http/workflow-events.ts +18 -5
- package/src/http/workflows.ts +128 -39
- package/src/http/x.ts +37 -6
- package/src/oauth/ensure-mcp-token.ts +16 -1
- package/src/oauth/mcp-wrapper.ts +281 -26
- package/src/providers/pi-mono-adapter.ts +1 -1
- package/src/scripts-runtime/credential-broker/README.md +1 -1
- package/src/scripts-runtime/credential-broker/fetch-patch.ts +12 -2
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/stdlib/fetch.ts +6 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +2 -0
- package/src/tests/apps-rbac.test.ts +110 -0
- package/src/tests/apps-spike2.test.ts +4 -1
- package/src/tests/apps-sync-engine.test.ts +2540 -0
- package/src/tests/apps-sync.test.ts +1627 -0
- package/src/tests/asset-key-api.test.ts +48 -2
- package/src/tests/asset-key-invariants.test.ts +92 -0
- package/src/tests/asset-key-migration.test.ts +174 -8
- package/src/tests/asset-key-normalization.test.ts +2 -0
- package/src/tests/credential-broker.test.ts +27 -2
- package/src/tests/mcp-oauth-dcr-reuse.test.ts +743 -0
- package/src/tests/mcp-oauth-ensure-token.test.ts +45 -0
- package/src/tests/mcp-oauth-manual-client.test.ts +135 -2
- package/src/tests/mcp-oauth-queries.test.ts +153 -0
- package/src/tests/mcp-oauth-wrapper.test.ts +432 -0
- package/src/tests/memory-http-recall-gating.test.ts +2 -0
- package/src/tests/openapi-response-contract.test.ts +138 -0
- package/src/tests/rbac-admission-e2e.test.ts +68 -0
- package/src/tests/script-connections.test.ts +3 -0
- package/src/tests/scripts-runtime-fetch.test.ts +121 -0
- package/src/tests/scripts-runtime-secret-egress.test.ts +4 -2
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/swarm-tool-result-gate.test.ts +32 -0
- package/src/tests/task-working-dir.test.ts +8 -4
- package/src/tests/whoami-route.test.ts +127 -0
- package/src/tests/workflow-engine-v2.test.ts +80 -7
- package/src/tests/workflow-swarm-script.test.ts +104 -0
- package/src/tools/app-get.ts +10 -2
- package/src/tools/app-rollback.ts +1 -0
- package/src/tools/app-sync.ts +112 -0
- package/src/tools/script-common.ts +20 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/utils.ts +11 -0
- package/src/tools/workflows/create-workflow.ts +4 -1
- package/src/types.ts +1394 -1206
- package/src/utils/template.ts +8 -1
- package/src/utils/zod-openapi.ts +11 -0
- package/src/workflows/engine.ts +27 -4
- package/src/workflows/executors/swarm-script.ts +28 -0
- package/src/workflows/retry-poller.ts +19 -2
- package/templates/skills/apps/content.md +94 -5
- package/templates/skills/asset-namespaces/content.md +2 -2
- package/templates/skills/scheduled-task-resilience/SKILL.md +102 -0
- package/templates/skills/scheduled-task-resilience/config.json +14 -0
- package/templates/skills/scheduled-task-resilience/content.md +97 -0
- package/dist/cli-7wmybrt5.js +0 -115
- package/dist/cli-d91pvmq1.js +0 -14
package/src/http/scripts.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { type AppValidationIssue, collectScriptReferences } from "../apps/definition";
|
|
3
4
|
import { getScriptAppTypes } from "../apps/script-types";
|
|
5
|
+
import { listAppRecords } from "../apps/store";
|
|
4
6
|
import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
5
7
|
import { getAgentById, recordInlineScriptRun, upsertKv } from "../be/db";
|
|
6
8
|
import { createEvent } from "../be/events";
|
|
@@ -34,16 +36,19 @@ import {
|
|
|
34
36
|
typecheckScript,
|
|
35
37
|
} from "../be/scripts/typecheck";
|
|
36
38
|
import { can } from "../rbac";
|
|
37
|
-
import { extractScriptSignature } from "../scripts-runtime/extract-signature";
|
|
39
|
+
import { extractScriptSignature, type ScriptSignature } from "../scripts-runtime/extract-signature";
|
|
38
40
|
import { runScript } from "../scripts-runtime/loader";
|
|
39
41
|
import {
|
|
40
42
|
ScriptApiAuthModeSchema,
|
|
43
|
+
ScriptApiRecordSchema,
|
|
41
44
|
type ScriptDetail,
|
|
42
45
|
ScriptFsModeSchema,
|
|
43
46
|
type ScriptListItem,
|
|
44
47
|
type ScriptRecord,
|
|
48
|
+
ScriptRecordSchema,
|
|
45
49
|
type ScriptScope,
|
|
46
50
|
ScriptScopeSchema,
|
|
51
|
+
ScriptVersionRecordSchema,
|
|
47
52
|
} from "../types";
|
|
48
53
|
import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
|
|
49
54
|
import { route } from "./route-def";
|
|
@@ -89,6 +94,121 @@ const listScriptsQuerySchema = z.object({
|
|
|
89
94
|
includeScratch: z.enum(["true", "false"]).optional(),
|
|
90
95
|
});
|
|
91
96
|
|
|
97
|
+
// ─── Response schemas ──────────────────────────────────────────────────────
|
|
98
|
+
// `signatureJson` is always produced by `signatureJsonFor` (below) /
|
|
99
|
+
// `extractScriptSignature`, so `JSON.parse(script.signatureJson)` is always
|
|
100
|
+
// exactly this shape.
|
|
101
|
+
const scriptSignatureSchema = z.object({
|
|
102
|
+
argsType: z.string(),
|
|
103
|
+
resultType: z.string(),
|
|
104
|
+
description: z.string(),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// `argsJsonSchema` is an arbitrary JSON Schema blob produced by
|
|
108
|
+
// `extractArgsJsonSchema` (always `zod.toJSONSchema()` output, i.e. a JSON
|
|
109
|
+
// object) or `null` when the script exports no `argsSchema`. Modelled as an
|
|
110
|
+
// open object rather than `z.unknown()` so the property stays REQUIRED in the
|
|
111
|
+
// emitted spec — every route below always writes the key.
|
|
112
|
+
const argsJsonSchemaValueSchema = z.record(z.string(), z.unknown()).nullable();
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* `rowToScript` / `rowToScriptVersion` (src/be/scripts/db.ts) build their
|
|
116
|
+
* records by spreading a `SELECT *` row, so the audit columns added by
|
|
117
|
+
* migration 082 ride along on the wire for every route that serves a mapped
|
|
118
|
+
* row verbatim. Declared (optional — the TS row types don't model them, so
|
|
119
|
+
* `respond()` can't require them at the call site) so the spec stops omitting
|
|
120
|
+
* fields that are actually sent. The tidier fix is projecting them out in the
|
|
121
|
+
* row mappers, which lives outside this file.
|
|
122
|
+
*/
|
|
123
|
+
const rowAuditColumnsShape = {
|
|
124
|
+
created_by: z.string().nullable().optional(),
|
|
125
|
+
updated_by: z.string().nullable().optional(),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** Lean projection served by `GET /api/scripts` — mirrors `ScriptListItem`. */
|
|
129
|
+
const scriptListItemSchema = ScriptRecordSchema.omit({
|
|
130
|
+
source: true,
|
|
131
|
+
signatureJson: true,
|
|
132
|
+
argsJsonSchema: true,
|
|
133
|
+
contentHash: true,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Full record served by `GET /api/scripts/{id}` — mirrors `ScriptDetail`.
|
|
138
|
+
* `ScriptDetail` widens `signature`/`argsJsonSchema` to `unknown`, but the
|
|
139
|
+
* wire values are exactly what `typesRoute`/`searchRoute` below serve, and both
|
|
140
|
+
* keys are always written — so they are declared precisely (and required) here
|
|
141
|
+
* too rather than as an untyped, optional blob.
|
|
142
|
+
*/
|
|
143
|
+
const scriptDetailSchema = ScriptRecordSchema.omit({ argsJsonSchema: true }).extend({
|
|
144
|
+
signature: scriptSignatureSchema,
|
|
145
|
+
argsJsonSchema: argsJsonSchemaValueSchema,
|
|
146
|
+
...rowAuditColumnsShape,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/** Mirrors `ScriptApiWithSecret` — a `ScriptApiRecord` plus the plaintext token. */
|
|
150
|
+
const scriptApiWithSecretSchema = ScriptApiRecordSchema.extend({
|
|
151
|
+
token: z.string().nullable(),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Mirrors `RunScriptOutput` (scripts-runtime/loader.ts), scrubbed via
|
|
155
|
+
// `scrubObject`, plus the two optional persistence-side-effect markers this
|
|
156
|
+
// route adds.
|
|
157
|
+
const scriptRunResponseSchema = z.object({
|
|
158
|
+
result: z.unknown().optional(),
|
|
159
|
+
autoSaved: z.object({ slug: z.string(), reason: z.string() }).optional(),
|
|
160
|
+
kvSaved: z.object({ namespace: z.string(), key: z.string() }).optional(),
|
|
161
|
+
truncated: z.object({ stdout: z.boolean(), stderr: z.boolean() }),
|
|
162
|
+
durationMs: z.number(),
|
|
163
|
+
stdout: z.string(),
|
|
164
|
+
stderr: z.string(),
|
|
165
|
+
exitCode: z.number(),
|
|
166
|
+
error: z
|
|
167
|
+
.enum(["timeout", "oom", "killed", "import_violation", "eval_error", "executor_error"])
|
|
168
|
+
.optional(),
|
|
169
|
+
runtimeError: z
|
|
170
|
+
.object({
|
|
171
|
+
name: z.string(),
|
|
172
|
+
message: z.string(),
|
|
173
|
+
stack: z.string(),
|
|
174
|
+
userFrames: z.array(
|
|
175
|
+
z.object({
|
|
176
|
+
file: z.string(),
|
|
177
|
+
line: z.number(),
|
|
178
|
+
column: z.number(),
|
|
179
|
+
raw: z.string(),
|
|
180
|
+
}),
|
|
181
|
+
),
|
|
182
|
+
userScriptLine: z.number().optional(),
|
|
183
|
+
userScriptColumn: z.number().optional(),
|
|
184
|
+
})
|
|
185
|
+
.optional(),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const searchResponseSchema = z.object({
|
|
189
|
+
results: z.array(
|
|
190
|
+
z.object({
|
|
191
|
+
name: z.string(),
|
|
192
|
+
signature: scriptSignatureSchema,
|
|
193
|
+
argsJsonSchema: argsJsonSchemaValueSchema,
|
|
194
|
+
description: z.string(),
|
|
195
|
+
score: z.number(),
|
|
196
|
+
}),
|
|
197
|
+
),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const scriptTypesResponseSchema = z.object({
|
|
201
|
+
signature: scriptSignatureSchema,
|
|
202
|
+
argsJsonSchema: argsJsonSchemaValueSchema,
|
|
203
|
+
sdkTypes: z.string(),
|
|
204
|
+
stdlibTypes: z.string(),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const typeDefsResponseSchema = z.object({
|
|
208
|
+
sdkTypes: z.string(),
|
|
209
|
+
stdlibTypes: z.string(),
|
|
210
|
+
});
|
|
211
|
+
|
|
92
212
|
const upsertRoute = route({
|
|
93
213
|
method: "post",
|
|
94
214
|
path: "/api/scripts/upsert",
|
|
@@ -99,7 +219,14 @@ const upsertRoute = route({
|
|
|
99
219
|
tags: ["Scripts"],
|
|
100
220
|
body: upsertBodySchema,
|
|
101
221
|
responses: {
|
|
102
|
-
200: {
|
|
222
|
+
200: {
|
|
223
|
+
description: "Script upserted",
|
|
224
|
+
schema: z.object({
|
|
225
|
+
name: z.string(),
|
|
226
|
+
version: z.number(),
|
|
227
|
+
contentDeduped: z.boolean(),
|
|
228
|
+
}),
|
|
229
|
+
},
|
|
103
230
|
400: { description: "Validation or typecheck failure" },
|
|
104
231
|
403: { description: "Global write requires lead agent" },
|
|
105
232
|
},
|
|
@@ -117,7 +244,7 @@ const runRoute = route({
|
|
|
117
244
|
tags: ["Scripts"],
|
|
118
245
|
body: runBodySchema,
|
|
119
246
|
responses: {
|
|
120
|
-
200: { description: "Script run completed" },
|
|
247
|
+
200: { description: "Script run completed", schema: scriptRunResponseSchema },
|
|
121
248
|
400: { description: "Validation error" },
|
|
122
249
|
404: { description: "Script not found" },
|
|
123
250
|
501: { description: "workspace-rw scripts are not supported in v1" },
|
|
@@ -134,7 +261,7 @@ const searchRoute = route({
|
|
|
134
261
|
tags: ["Scripts"],
|
|
135
262
|
body: searchBodySchema,
|
|
136
263
|
responses: {
|
|
137
|
-
200: { description: "Matching scripts" },
|
|
264
|
+
200: { description: "Matching scripts", schema: searchResponseSchema },
|
|
138
265
|
400: { description: "Validation error" },
|
|
139
266
|
},
|
|
140
267
|
rbac: { permission: "script.search" },
|
|
@@ -150,9 +277,10 @@ const deleteRoute = route({
|
|
|
150
277
|
params: nameParamsSchema,
|
|
151
278
|
query: scopeQuerySchema,
|
|
152
279
|
responses: {
|
|
153
|
-
200: { description: "Delete result" },
|
|
280
|
+
200: { description: "Delete result", schema: z.object({ deleted: z.boolean() }) },
|
|
154
281
|
400: { description: "Validation error" },
|
|
155
282
|
403: { description: "Global delete requires lead agent" },
|
|
283
|
+
409: { description: "Script is referenced by an app definition" },
|
|
156
284
|
},
|
|
157
285
|
rbac: { permission: "script.global.delete" },
|
|
158
286
|
});
|
|
@@ -167,7 +295,7 @@ const typesRoute = route({
|
|
|
167
295
|
params: nameParamsSchema,
|
|
168
296
|
query: optionalScopeQuerySchema,
|
|
169
297
|
responses: {
|
|
170
|
-
200: { description: "Script signature and type blobs" },
|
|
298
|
+
200: { description: "Script signature and type blobs", schema: scriptTypesResponseSchema },
|
|
171
299
|
404: { description: "Script not found" },
|
|
172
300
|
},
|
|
173
301
|
});
|
|
@@ -189,7 +317,10 @@ const listScriptsRoute = route({
|
|
|
189
317
|
tags: ["Scripts"],
|
|
190
318
|
query: listScriptsQuerySchema,
|
|
191
319
|
responses: {
|
|
192
|
-
200: {
|
|
320
|
+
200: {
|
|
321
|
+
description: "Saved scripts",
|
|
322
|
+
schema: z.object({ scripts: z.array(scriptListItemSchema) }),
|
|
323
|
+
},
|
|
193
324
|
400: { description: "Validation error" },
|
|
194
325
|
},
|
|
195
326
|
});
|
|
@@ -207,7 +338,10 @@ const typeDefsRoute = route({
|
|
|
207
338
|
"Generated .d.ts blobs for editor integration (e.g. Monaco extraLibs), including per-app types. Cacheable.",
|
|
208
339
|
tags: ["Scripts"],
|
|
209
340
|
responses: {
|
|
210
|
-
200: {
|
|
341
|
+
200: {
|
|
342
|
+
description: "SDK and stdlib type definition blobs",
|
|
343
|
+
schema: typeDefsResponseSchema,
|
|
344
|
+
},
|
|
211
345
|
},
|
|
212
346
|
});
|
|
213
347
|
|
|
@@ -221,7 +355,7 @@ const getScriptByIdRoute = route({
|
|
|
221
355
|
tags: ["Scripts"],
|
|
222
356
|
params: idParamsSchema,
|
|
223
357
|
responses: {
|
|
224
|
-
200: { description: "Script detail" },
|
|
358
|
+
200: { description: "Script detail", schema: z.object({ script: scriptDetailSchema }) },
|
|
225
359
|
404: { description: "Script not found" },
|
|
226
360
|
},
|
|
227
361
|
});
|
|
@@ -236,7 +370,13 @@ const listVersionsRoute = route({
|
|
|
236
370
|
tags: ["Scripts"],
|
|
237
371
|
params: idParamsSchema,
|
|
238
372
|
responses: {
|
|
239
|
-
200: {
|
|
373
|
+
200: {
|
|
374
|
+
description: "Script versions",
|
|
375
|
+
// `listScriptVersions` spreads a `SELECT *` row — see `rowAuditColumnsShape`.
|
|
376
|
+
schema: z.object({
|
|
377
|
+
versions: z.array(ScriptVersionRecordSchema.extend(rowAuditColumnsShape)),
|
|
378
|
+
}),
|
|
379
|
+
},
|
|
240
380
|
404: { description: "Script not found" },
|
|
241
381
|
},
|
|
242
382
|
});
|
|
@@ -272,7 +412,7 @@ const createScriptApiRoute = route({
|
|
|
272
412
|
params: idParamsSchema,
|
|
273
413
|
body: createScriptApiBodySchema,
|
|
274
414
|
responses: {
|
|
275
|
-
201: { description: "Endpoint created" },
|
|
415
|
+
201: { description: "Endpoint created", schema: scriptApiWithSecretSchema },
|
|
276
416
|
400: { description: "Validation error or script has no owning agent" },
|
|
277
417
|
404: { description: "Script not found" },
|
|
278
418
|
},
|
|
@@ -288,7 +428,10 @@ const listScriptApisRoute = route({
|
|
|
288
428
|
tags: ["Scripts"],
|
|
289
429
|
params: idParamsSchema,
|
|
290
430
|
responses: {
|
|
291
|
-
200: {
|
|
431
|
+
200: {
|
|
432
|
+
description: "Endpoints (without secrets)",
|
|
433
|
+
schema: z.object({ apis: z.array(ScriptApiRecordSchema) }),
|
|
434
|
+
},
|
|
292
435
|
404: { description: "Script not found" },
|
|
293
436
|
},
|
|
294
437
|
});
|
|
@@ -302,7 +445,10 @@ const revealScriptApiSecretRoute = route({
|
|
|
302
445
|
tags: ["Scripts"],
|
|
303
446
|
params: apiEndpointParamsSchema,
|
|
304
447
|
responses: {
|
|
305
|
-
200: {
|
|
448
|
+
200: {
|
|
449
|
+
description: "Decrypted token (null when authMode is 'none')",
|
|
450
|
+
schema: z.object({ token: z.string().nullable() }),
|
|
451
|
+
},
|
|
306
452
|
404: { description: "Endpoint not found" },
|
|
307
453
|
},
|
|
308
454
|
rbac: { permission: "script.api.read.secrets" },
|
|
@@ -318,7 +464,17 @@ const patchScriptApiRoute = route({
|
|
|
318
464
|
params: apiEndpointParamsSchema,
|
|
319
465
|
body: patchScriptApiBodySchema,
|
|
320
466
|
responses: {
|
|
321
|
-
|
|
467
|
+
// `updateScriptApi` is typed `ScriptApiRecord | null` (the id could vanish
|
|
468
|
+
// between the existence check and the UPDATE); the handler sends its
|
|
469
|
+
// result verbatim without a null guard, so this is honestly nullable.
|
|
470
|
+
// Spelled as an explicit union, not `.nullable()`: `.nullable()` on a
|
|
471
|
+
// `.openapi()`-registered schema emits `allOf: [$ref, {type:[object,null]}]`,
|
|
472
|
+
// an intersection that `null` can never satisfy — i.e. it silently documents
|
|
473
|
+
// the opposite of the handler's behavior.
|
|
474
|
+
200: {
|
|
475
|
+
description: "Updated endpoint",
|
|
476
|
+
schema: z.union([ScriptApiRecordSchema, z.null()]),
|
|
477
|
+
},
|
|
322
478
|
404: { description: "Endpoint not found" },
|
|
323
479
|
},
|
|
324
480
|
rbac: { permission: "script.api.update" },
|
|
@@ -333,7 +489,10 @@ const rotateScriptApiRoute = route({
|
|
|
333
489
|
tags: ["Scripts"],
|
|
334
490
|
params: apiEndpointParamsSchema,
|
|
335
491
|
responses: {
|
|
336
|
-
200: {
|
|
492
|
+
200: {
|
|
493
|
+
description: "Endpoint with new plaintext token",
|
|
494
|
+
schema: scriptApiWithSecretSchema,
|
|
495
|
+
},
|
|
337
496
|
400: { description: "Endpoint uses 'none' auth — nothing to rotate" },
|
|
338
497
|
404: { description: "Endpoint not found" },
|
|
339
498
|
},
|
|
@@ -349,12 +508,45 @@ const deleteScriptApiRoute = route({
|
|
|
349
508
|
tags: ["Scripts"],
|
|
350
509
|
params: apiEndpointParamsSchema,
|
|
351
510
|
responses: {
|
|
352
|
-
200: { description: "Deleted" },
|
|
511
|
+
200: { description: "Deleted", schema: z.object({ deleted: z.boolean() }) },
|
|
353
512
|
404: { description: "Endpoint not found" },
|
|
354
513
|
},
|
|
355
514
|
rbac: { permission: "script.api.delete" },
|
|
356
515
|
});
|
|
357
516
|
|
|
517
|
+
/**
|
|
518
|
+
* Every app definition that references `scriptId`, as path-bearing issues.
|
|
519
|
+
*
|
|
520
|
+
* Two-pass by design: the tolerant collector walks the stored JSON (so an app
|
|
521
|
+
* whose definition no longer parses still reports its references with exact
|
|
522
|
+
* paths), and any app that failed to decode at all — invalid stored JSON, where
|
|
523
|
+
* the "definition" is a raw string — falls back to a substring probe. A broken
|
|
524
|
+
* app is not consent to break it further.
|
|
525
|
+
*
|
|
526
|
+
* Cost: O(apps x definition size) per delete, all in memory. Deletes are rare
|
|
527
|
+
* and single-app installs hold tens of apps, so no index is warranted.
|
|
528
|
+
*/
|
|
529
|
+
function appScriptReferenceIssues(scriptId: string): AppValidationIssue[] {
|
|
530
|
+
const issues: AppValidationIssue[] = [];
|
|
531
|
+
for (const app of listAppRecords()) {
|
|
532
|
+
const paths = collectScriptReferences(app.definition).get(scriptId) ?? [];
|
|
533
|
+
if (
|
|
534
|
+
paths.length === 0 &&
|
|
535
|
+
app.definitionError !== undefined &&
|
|
536
|
+
JSON.stringify(app.definition ?? null).includes(scriptId)
|
|
537
|
+
) {
|
|
538
|
+
paths.push("its (unparseable) definition");
|
|
539
|
+
}
|
|
540
|
+
for (const path of paths) {
|
|
541
|
+
issues.push({
|
|
542
|
+
path: `apps.${app.id}`,
|
|
543
|
+
message: `app "${app.name}" (${app.id}) uses this script at ${path}`,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return issues;
|
|
548
|
+
}
|
|
549
|
+
|
|
358
550
|
function requireAgent(res: ServerResponse, agentId: string | undefined) {
|
|
359
551
|
if (!agentId) {
|
|
360
552
|
jsonError(res, "X-Agent-ID required for scripts API", 400);
|
|
@@ -487,7 +679,7 @@ export async function handleScripts(
|
|
|
487
679
|
});
|
|
488
680
|
}
|
|
489
681
|
|
|
490
|
-
|
|
682
|
+
upsertRoute.respond(res, 200, {
|
|
491
683
|
name: result.script.name,
|
|
492
684
|
version: result.script.version,
|
|
493
685
|
contentDeduped: result.contentDeduped,
|
|
@@ -608,8 +800,9 @@ export async function handleScripts(
|
|
|
608
800
|
// swallow — the run already executed; persistence is observability only.
|
|
609
801
|
}
|
|
610
802
|
|
|
611
|
-
|
|
803
|
+
runRoute.respond(
|
|
612
804
|
res,
|
|
805
|
+
200,
|
|
613
806
|
scrubObject({
|
|
614
807
|
result: output.result,
|
|
615
808
|
autoSaved,
|
|
@@ -639,12 +832,12 @@ export async function handleScripts(
|
|
|
639
832
|
limit: parsed.body.limit,
|
|
640
833
|
});
|
|
641
834
|
|
|
642
|
-
|
|
835
|
+
searchRoute.respond(res, 200, {
|
|
643
836
|
results: matches.map(({ script, score }) => ({
|
|
644
837
|
name: script.name,
|
|
645
838
|
signature: JSON.parse(script.signatureJson),
|
|
646
839
|
argsJsonSchema: script.argsJsonSchema
|
|
647
|
-
? (JSON.parse(script.argsJsonSchema) as unknown)
|
|
840
|
+
? (JSON.parse(script.argsJsonSchema) as Record<string, unknown>)
|
|
648
841
|
: null,
|
|
649
842
|
description: script.description,
|
|
650
843
|
score,
|
|
@@ -676,7 +869,7 @@ export async function handleScripts(
|
|
|
676
869
|
createdAt: script.createdAt,
|
|
677
870
|
updatedAt: script.updatedAt,
|
|
678
871
|
}));
|
|
679
|
-
|
|
872
|
+
listScriptsRoute.respond(res, 200, { scripts });
|
|
680
873
|
return true;
|
|
681
874
|
}
|
|
682
875
|
|
|
@@ -686,7 +879,7 @@ export async function handleScripts(
|
|
|
686
879
|
const apiTypes = getScriptApiTypes();
|
|
687
880
|
const mcpTypes = getScriptMcpTypes();
|
|
688
881
|
const appTypes = getScriptAppTypes();
|
|
689
|
-
|
|
882
|
+
typeDefsRoute.respond(res, 200, {
|
|
690
883
|
sdkTypes: scriptSdkTypesWithGeneratedApis(apiTypes, mcpTypes, appTypes),
|
|
691
884
|
stdlibTypes: scriptStdlibTypesWithGeneratedApis(apiTypes, mcpTypes, appTypes),
|
|
692
885
|
});
|
|
@@ -703,12 +896,14 @@ export async function handleScripts(
|
|
|
703
896
|
}
|
|
704
897
|
// `source` is author-supplied TS (same trust surface as script_runs.source,
|
|
705
898
|
// already served raw by GET /api/script-runs/{id}) — no env/secret material.
|
|
706
|
-
const detail
|
|
899
|
+
const detail = {
|
|
707
900
|
...script,
|
|
708
|
-
signature: JSON.parse(script.signatureJson) as
|
|
709
|
-
argsJsonSchema: script.argsJsonSchema
|
|
710
|
-
|
|
711
|
-
|
|
901
|
+
signature: JSON.parse(script.signatureJson) as ScriptSignature,
|
|
902
|
+
argsJsonSchema: script.argsJsonSchema
|
|
903
|
+
? (JSON.parse(script.argsJsonSchema) as Record<string, unknown>)
|
|
904
|
+
: null,
|
|
905
|
+
} satisfies ScriptDetail;
|
|
906
|
+
getScriptByIdRoute.respond(res, 200, { script: detail });
|
|
712
907
|
return true;
|
|
713
908
|
}
|
|
714
909
|
|
|
@@ -719,7 +914,7 @@ export async function handleScripts(
|
|
|
719
914
|
jsonError(res, "Script not found", 404);
|
|
720
915
|
return true;
|
|
721
916
|
}
|
|
722
|
-
|
|
917
|
+
listVersionsRoute.respond(res, 200, { versions: listScriptVersions(parsed.params.id) });
|
|
723
918
|
return true;
|
|
724
919
|
}
|
|
725
920
|
|
|
@@ -734,9 +929,11 @@ export async function handleScripts(
|
|
|
734
929
|
jsonError(res, "Script not found", 404);
|
|
735
930
|
return true;
|
|
736
931
|
}
|
|
737
|
-
|
|
932
|
+
typesRoute.respond(res, 200, {
|
|
738
933
|
signature: JSON.parse(script.signatureJson),
|
|
739
|
-
argsJsonSchema: script.argsJsonSchema
|
|
934
|
+
argsJsonSchema: script.argsJsonSchema
|
|
935
|
+
? (JSON.parse(script.argsJsonSchema) as Record<string, unknown>)
|
|
936
|
+
: null,
|
|
740
937
|
sdkTypes: scriptSdkTypesWithGeneratedApis(
|
|
741
938
|
getScriptApiTypes({ agentId: agent.id }),
|
|
742
939
|
getScriptMcpTypes({ agentId: agent.id }),
|
|
@@ -772,12 +969,33 @@ export async function handleScripts(
|
|
|
772
969
|
}
|
|
773
970
|
}
|
|
774
971
|
|
|
775
|
-
const
|
|
972
|
+
const identity = {
|
|
776
973
|
name: parsed.params.name,
|
|
777
974
|
scope: parsed.query.scope,
|
|
778
975
|
scopeId: parsed.query.scope === "agent" ? agent.id : null,
|
|
779
|
-
}
|
|
780
|
-
|
|
976
|
+
};
|
|
977
|
+
const existing = getScript(identity);
|
|
978
|
+
if (existing) {
|
|
979
|
+
// An app that wires this script as a source or a script action would be
|
|
980
|
+
// left with a dangling reference: its definition stops parsing and every
|
|
981
|
+
// write 409s "needs repair". Refuse the delete instead. UPDATES stay
|
|
982
|
+
// allowed — a contract break there is a pass error with zero row churn.
|
|
983
|
+
const references = appScriptReferenceIssues(existing.id);
|
|
984
|
+
if (references.length > 0) {
|
|
985
|
+
json(
|
|
986
|
+
res,
|
|
987
|
+
{
|
|
988
|
+
error: "script is referenced by an app definition",
|
|
989
|
+
issues: references,
|
|
990
|
+
},
|
|
991
|
+
409,
|
|
992
|
+
);
|
|
993
|
+
return true;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
const deleted = deleteScript(identity);
|
|
998
|
+
deleteRoute.respond(res, 200, { deleted });
|
|
781
999
|
return true;
|
|
782
1000
|
}
|
|
783
1001
|
|
|
@@ -804,7 +1022,7 @@ export async function handleScripts(
|
|
|
804
1022
|
label: parsed.body.label ?? null,
|
|
805
1023
|
createdBy: resolveHttpAuditUserId(req, agentId),
|
|
806
1024
|
});
|
|
807
|
-
|
|
1025
|
+
createScriptApiRoute.respond(res, 201, endpoint);
|
|
808
1026
|
return true;
|
|
809
1027
|
}
|
|
810
1028
|
|
|
@@ -815,7 +1033,7 @@ export async function handleScripts(
|
|
|
815
1033
|
jsonError(res, "Script not found", 404);
|
|
816
1034
|
return true;
|
|
817
1035
|
}
|
|
818
|
-
|
|
1036
|
+
listScriptApisRoute.respond(res, 200, { apis: listScriptApisForScript(parsed.params.id) });
|
|
819
1037
|
return true;
|
|
820
1038
|
}
|
|
821
1039
|
|
|
@@ -827,7 +1045,7 @@ export async function handleScripts(
|
|
|
827
1045
|
jsonError(res, "Endpoint not found", 404);
|
|
828
1046
|
return true;
|
|
829
1047
|
}
|
|
830
|
-
|
|
1048
|
+
revealScriptApiSecretRoute.respond(res, 200, { token: getScriptApiSecret(endpoint.id) });
|
|
831
1049
|
return true;
|
|
832
1050
|
}
|
|
833
1051
|
|
|
@@ -844,7 +1062,7 @@ export async function handleScripts(
|
|
|
844
1062
|
jsonError(res, "Cannot rotate a token on a 'none' auth endpoint", 400);
|
|
845
1063
|
return true;
|
|
846
1064
|
}
|
|
847
|
-
|
|
1065
|
+
rotateScriptApiRoute.respond(res, 200, rotated);
|
|
848
1066
|
return true;
|
|
849
1067
|
}
|
|
850
1068
|
|
|
@@ -861,7 +1079,7 @@ export async function handleScripts(
|
|
|
861
1079
|
label: parsed.body.label,
|
|
862
1080
|
updatedBy: resolveHttpAuditUserId(req, agentId),
|
|
863
1081
|
});
|
|
864
|
-
|
|
1082
|
+
patchScriptApiRoute.respond(res, 200, updated);
|
|
865
1083
|
return true;
|
|
866
1084
|
}
|
|
867
1085
|
|
|
@@ -873,7 +1091,7 @@ export async function handleScripts(
|
|
|
873
1091
|
jsonError(res, "Endpoint not found", 404);
|
|
874
1092
|
return true;
|
|
875
1093
|
}
|
|
876
|
-
|
|
1094
|
+
deleteScriptApiRoute.respond(res, 200, { deleted: deleteScriptApi(endpoint.id) });
|
|
877
1095
|
return true;
|
|
878
1096
|
}
|
|
879
1097
|
|