@desplega.ai/agent-swarm 1.112.0 → 1.113.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/dist/{actions-eckb4g8r.js → actions-q4n7cz6e.js} +6 -6
- package/dist/{app-b05sgm02.js → app-es4nzc71.js} +3 -3
- package/dist/{assistant-7255sbyw.js → assistant-0x8ey0vs.js} +9 -9
- package/dist/{boot-reembed-y1bt3ttn.js → boot-reembed-69agkhv6.js} +4 -4
- package/dist/{boot-reembed-yv7awg4y.js → boot-reembed-p0ny05t8.js} +3 -3
- package/dist/{boot-scrub-logs-ddyaeqar.js → boot-scrub-logs-yybrkmvr.js} +2 -2
- package/dist/{cli-s9pj3c7z.js → cli-30bbaveh.js} +2 -2
- package/dist/{cli-r1btc895.js → cli-36ymmpmp.js} +2 -2
- package/dist/{cli-15598a4k.js → cli-4xyj9jtq.js} +1215 -235
- package/dist/{cli-52tbq17n.js → cli-bgef578c.js} +1 -1
- package/dist/{cli-76d1vtvq.js → cli-bgvskydy.js} +1 -1
- package/dist/{cli-pt7c7qxz.js → cli-bnzbn8j8.js} +3 -3
- package/dist/{cli-8f0dwca2.js → cli-c3frqf65.js} +4 -3
- package/dist/{cli-9p0qhead.js → cli-c9mtm09b.js} +1 -1
- package/dist/{cli-709t753c.js → cli-f3qa069v.js} +2 -2
- package/dist/{cli-94vzr3nq.js → cli-gtkqc144.js} +4 -4
- package/dist/{cli-7xkwgkrs.js → cli-h3k622d0.js} +1 -1
- package/dist/{cli-h9jx5mag.js → cli-hjhvekf4.js} +4 -4
- package/dist/{cli-tp7e60s3.js → cli-hsetkkd3.js} +2 -2
- package/dist/{cli-tch0ypp6.js → cli-mq580e06.js} +4 -4
- package/dist/{cli-951w7c79.js → cli-ncfgsqbd.js} +63 -7
- package/dist/{cli-30d2ct3k.js → cli-pwc5e83c.js} +3 -3
- package/dist/{cli-3r5y6ayj.js → cli-qwgtacd6.js} +2 -2
- package/dist/{cli-56s4ehzw.js → cli-r5g6ngtn.js} +1 -1
- package/dist/{cli-rp8pca98.js → cli-tbw13sx8.js} +1 -1
- package/dist/{cli-hm27prrc.js → cli-trmapn9k.js} +5 -5
- package/dist/{cli-0c3n0eba.js → cli-y4ycrnjc.js} +1 -1
- package/dist/{cli-a5e8n5hg.js → cli-y6mrptcn.js} +1 -1
- package/dist/{cli-tg0f2wmz.js → cli-z46pnksz.js} +5 -5
- package/dist/cli.js +11 -10
- package/dist/{commands-14g7taf4.js → commands-z5dbwta3.js} +2 -2
- package/dist/{db-3tge4jrn.js → db-bbgahh4z.js} +2 -2
- package/dist/{handlers-sy64egg6.js → handlers-6b44317z.js} +9 -9
- package/dist/{hook-rnkzw8vp.js → hook-sbp5fmps.js} +1 -1
- package/dist/{http-xx9xptcs.js → http-2xz43gz3.js} +354 -94
- package/dist/{index-zfx5p959.js → index-0gzmqj4k.js} +8 -8
- package/dist/{index-3wr6v2mm.js → index-5ghxn8s6.js} +7 -7
- package/dist/{index-brma88d4.js → index-nhat35em.js} +9 -9
- package/dist/{index-ha8e9xn2.js → index-vfedgdw6.js} +26 -15
- package/dist/{keepalive-rbz05zad.js → keepalive-62gfj91d.js} +4 -4
- package/dist/{lead-s790v7r0.js → lead-ag0akn0v.js} +17 -17
- package/dist/{maintenance-420rmadb.js → maintenance-xxrrzk5x.js} +4 -4
- package/dist/{onboard-jgqamqnw.js → onboard-64zyfcbs.js} +2 -2
- package/dist/{otel-impl-ch3n3783.js → otel-impl-b86qseaa.js} +1 -1
- package/dist/{pricing-refresh-rmtrby6h.js → pricing-refresh-cgtkffpa.js} +4 -4
- package/dist/{seed-pricing-1szjzfj1.js → seed-pricing-1r1y1xkq.js} +3 -3
- package/dist/{setup-8jfzkyzv.js → setup-mkcgkjf7.js} +2 -2
- package/dist/{worker-40vq0pm8.js → worker-351sze63.js} +17 -17
- package/openapi.json +314 -3
- package/package.json +4 -3
- package/src/be/db.ts +43 -3
- package/src/be/memory/providers/sqlite-store.ts +5 -1
- package/src/be/migrations/108_rbac_permission_audit.sql +26 -0
- package/src/be/rbac-audit.ts +218 -0
- package/src/http/all-routes.ts +58 -0
- package/src/http/config.ts +55 -0
- package/src/http/favorites.ts +5 -0
- package/src/http/fs.ts +27 -7
- package/src/http/index.ts +26 -0
- package/src/http/kv.ts +21 -4
- package/src/http/route-def.ts +9 -0
- package/src/http/schedules.ts +56 -22
- package/src/http/scripts.ts +33 -0
- package/src/http/workflows.ts +13 -2
- package/src/prompts/base-prompt.ts +7 -6
- package/src/prompts/session-templates.ts +38 -11
- package/src/rbac/can.ts +44 -0
- package/src/rbac/index.ts +13 -0
- package/src/rbac/legacy-policy.ts +185 -0
- package/src/rbac/permissions.ts +182 -0
- package/src/rbac/types.ts +45 -0
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/swarm-sdk.ts +81 -0
- package/src/scripts-runtime/types/stdlib.d.ts +18 -2
- package/src/scripts-runtime/types/swarm-sdk.d.ts +18 -2
- package/src/server.ts +2 -0
- package/src/stdio.ts +43 -0
- package/src/tests/base-prompt.test.ts +7 -4
- package/src/tests/harness-provider-resolution.test.ts +5 -0
- package/src/tests/http-api-integration.test.ts +8 -1
- package/src/tests/prompt-template-session.test.ts +21 -8
- package/src/tests/rbac-audit.test.ts +345 -0
- package/src/tests/rbac-charact-http.test.ts +272 -0
- package/src/tests/rbac-charact-misc-tools.test.ts +428 -0
- package/src/tests/rbac-charact-skills.test.ts +492 -0
- package/src/tests/rbac-charact-slack.test.ts +283 -0
- package/src/tests/rbac-e2e-helpers.ts +305 -0
- package/src/tests/rbac-engine.test.ts +433 -0
- package/src/tests/rbac-lifecycle-e2e.test.ts +262 -0
- package/src/tests/rbac-wire-e2e.test.ts +574 -0
- package/src/tests/schedule-http-triage-tools.test.ts +121 -0
- package/src/tests/scheduled-tasks.test.ts +34 -0
- package/src/tests/scripts-http.test.ts +56 -7
- package/src/tests/swarm-config-reserved-keys.test.ts +17 -0
- package/src/tests/tool-annotations.test.ts +1 -0
- package/src/tests/update-schedule-mcp-tool.test.ts +57 -0
- package/src/tests/workflow-http-v2.test.ts +80 -0
- package/src/tools/cancel-task.ts +13 -3
- package/src/tools/context-diff.ts +12 -1
- package/src/tools/context-history.ts +12 -1
- package/src/tools/credential-bindings/tool.ts +12 -1
- package/src/tools/delete-channel.ts +12 -1
- package/src/tools/get-task-details.ts +1 -1
- package/src/tools/inject-learning.ts +12 -1
- package/src/tools/kv/kv-delete.ts +3 -18
- package/src/tools/kv/kv-incr.ts +3 -18
- package/src/tools/kv/kv-set.ts +3 -20
- package/src/tools/kv/kv-write-auth.ts +40 -0
- package/src/tools/manage-user.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-create.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-delete.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-install.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-uninstall.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-update.ts +12 -1
- package/src/tools/memory-delete.ts +12 -4
- package/src/tools/register-kapso-number.ts +23 -2
- package/src/tools/schedules/create-schedule.ts +4 -3
- package/src/tools/schedules/index.ts +1 -0
- package/src/tools/schedules/list-schedules.ts +36 -2
- package/src/tools/schedules/patch-schedule.ts +405 -0
- package/src/tools/schedules/update-schedule.ts +2 -2
- package/src/tools/script-connections/tool.ts +12 -1
- package/src/tools/skills/skill-create.ts +12 -1
- package/src/tools/skills/skill-delete.ts +12 -1
- package/src/tools/skills/skill-install-remote.ts +12 -1
- package/src/tools/skills/skill-install.ts +12 -1
- package/src/tools/skills/skill-uninstall.ts +12 -1
- package/src/tools/skills/skill-update.ts +25 -2
- package/src/tools/slack-delete.ts +8 -1
- package/src/tools/slack-post.ts +8 -1
- package/src/tools/slack-read.ts +8 -1
- package/src/tools/slack-start-thread.ts +8 -1
- package/src/tools/slack-update.ts +8 -1
- package/src/tools/slack-upload-file.ts +8 -1
- package/src/tools/swarm-config/delete-config.ts +28 -1
- package/src/tools/swarm-config/get-config.ts +32 -5
- package/src/tools/swarm-config/list-config.ts +31 -5
- package/src/tools/swarm-config/set-config.ts +38 -1
- package/src/tools/task-action.ts +1 -1
- package/src/tools/task-tool-ctx.ts +19 -3
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/update-profile.ts +8 -1
- package/src/tools/workflows/list-workflows.ts +14 -2
- package/templates/skills/swarm-scripts/SKILL.md +4 -9
- package/templates/skills/swarm-scripts/content.md +20 -9
|
@@ -8,11 +8,13 @@ import {
|
|
|
8
8
|
agentWithCapacity,
|
|
9
9
|
applyRating,
|
|
10
10
|
assertSelectOnlyQuery,
|
|
11
|
+
can,
|
|
11
12
|
canClaim,
|
|
12
13
|
canReadMemory,
|
|
13
14
|
cancelTaskHandler,
|
|
14
15
|
cancelTaskInputSchema,
|
|
15
16
|
cancelTaskOutputSchema,
|
|
17
|
+
clearAuditSink,
|
|
16
18
|
clearJiraMetadata,
|
|
17
19
|
createEvent,
|
|
18
20
|
createEventsBatch,
|
|
@@ -93,6 +95,7 @@ import {
|
|
|
93
95
|
sendKapsoReaction,
|
|
94
96
|
sendTaskHandler,
|
|
95
97
|
sendTaskOutputSchema,
|
|
98
|
+
setAuditSink,
|
|
96
99
|
setCorsHeaders,
|
|
97
100
|
shouldPersistAutomaticTaskMemory,
|
|
98
101
|
snapshotMetric,
|
|
@@ -111,7 +114,7 @@ import {
|
|
|
111
114
|
withRemoteContext,
|
|
112
115
|
withSpanContext,
|
|
113
116
|
writeSkillsToFilesystem
|
|
114
|
-
} from "./cli-
|
|
117
|
+
} from "./cli-4xyj9jtq.js";
|
|
115
118
|
import {
|
|
116
119
|
BROWSER_SDK_JS,
|
|
117
120
|
SWARM_UI_JS
|
|
@@ -144,11 +147,11 @@ import {
|
|
|
144
147
|
getOAuthApp,
|
|
145
148
|
getOAuthTokens,
|
|
146
149
|
releaseOAuthRefreshLock
|
|
147
|
-
} from "./cli-
|
|
148
|
-
import"./cli-
|
|
150
|
+
} from "./cli-bgef578c.js";
|
|
151
|
+
import"./cli-36ymmpmp.js";
|
|
149
152
|
import {
|
|
150
153
|
normalizeModelKey
|
|
151
|
-
} from "./cli-
|
|
154
|
+
} from "./cli-tbw13sx8.js";
|
|
152
155
|
import {
|
|
153
156
|
SCRIPT_STDLIB_TYPES,
|
|
154
157
|
ensureAgentFsCredentialsForAgent,
|
|
@@ -156,16 +159,16 @@ import {
|
|
|
156
159
|
extractScriptSignature,
|
|
157
160
|
scriptSdkTypesWithGeneratedApis,
|
|
158
161
|
typecheckScript
|
|
159
|
-
} from "./cli-
|
|
162
|
+
} from "./cli-hsetkkd3.js";
|
|
160
163
|
import {
|
|
161
164
|
checkHeartbeatChecklist,
|
|
162
165
|
runHeartbeatSweep,
|
|
163
166
|
stopHeartbeat
|
|
164
|
-
} from "./cli-
|
|
167
|
+
} from "./cli-bnzbn8j8.js";
|
|
165
168
|
import {
|
|
166
169
|
createResumeFollowUp,
|
|
167
170
|
createWorkerTaskFollowUp
|
|
168
|
-
} from "./cli-
|
|
171
|
+
} from "./cli-r5g6ngtn.js";
|
|
169
172
|
import"./cli-fad8m16k.js";
|
|
170
173
|
import {
|
|
171
174
|
calculateNextRun,
|
|
@@ -173,7 +176,7 @@ import {
|
|
|
173
176
|
ensure,
|
|
174
177
|
initialize,
|
|
175
178
|
require_dist
|
|
176
|
-
} from "./cli-
|
|
179
|
+
} from "./cli-gtkqc144.js";
|
|
177
180
|
import {
|
|
178
181
|
RawLlmConfigSchema,
|
|
179
182
|
TriggerSchemaError,
|
|
@@ -193,7 +196,7 @@ import {
|
|
|
193
196
|
validateDefinition,
|
|
194
197
|
validateJsonSchema,
|
|
195
198
|
verifyHmacSignature
|
|
196
|
-
} from "./cli-
|
|
199
|
+
} from "./cli-z46pnksz.js";
|
|
197
200
|
import {
|
|
198
201
|
getApiKey
|
|
199
202
|
} from "./cli-f14fvzag.js";
|
|
@@ -214,10 +217,10 @@ import {
|
|
|
214
217
|
rotateScriptApiSecret,
|
|
215
218
|
updateScriptApi,
|
|
216
219
|
upsertScriptByName
|
|
217
|
-
} from "./cli-
|
|
220
|
+
} from "./cli-qwgtacd6.js";
|
|
218
221
|
import {
|
|
219
222
|
searchScripts
|
|
220
|
-
} from "./cli-
|
|
223
|
+
} from "./cli-30bbaveh.js";
|
|
221
224
|
import {
|
|
222
225
|
CANDIDATE_SET_MULTIPLIER,
|
|
223
226
|
getEmbeddingProvider,
|
|
@@ -225,19 +228,19 @@ import {
|
|
|
225
228
|
init_constants,
|
|
226
229
|
init_reranker,
|
|
227
230
|
rerank
|
|
228
|
-
} from "./cli-
|
|
231
|
+
} from "./cli-f3qa069v.js";
|
|
229
232
|
import"./cli-q25ejbyn.js";
|
|
230
|
-
import"./cli-
|
|
233
|
+
import"./cli-bgvskydy.js";
|
|
231
234
|
import {
|
|
232
235
|
getSlackApp,
|
|
233
236
|
startSlackApp,
|
|
234
237
|
stopSlackApp
|
|
235
|
-
} from "./cli-
|
|
238
|
+
} from "./cli-trmapn9k.js";
|
|
236
239
|
import"./cli-rkndnn90.js";
|
|
237
240
|
import"./cli-b0p7rfnd.js";
|
|
238
241
|
import {
|
|
239
242
|
recordUnmappedIdentity
|
|
240
|
-
} from "./cli-
|
|
243
|
+
} from "./cli-c9mtm09b.js";
|
|
241
244
|
import {
|
|
242
245
|
findOrCreateUserByEmail,
|
|
243
246
|
findUserByExternalId,
|
|
@@ -251,7 +254,7 @@ import {
|
|
|
251
254
|
resolveUserByToken,
|
|
252
255
|
revokeToken,
|
|
253
256
|
unlinkIdentity
|
|
254
|
-
} from "./cli-
|
|
257
|
+
} from "./cli-h3k622d0.js";
|
|
255
258
|
import"./cli-6xd0bvf8.js";
|
|
256
259
|
import"./cli-wspgs9bt.js";
|
|
257
260
|
import {
|
|
@@ -259,7 +262,7 @@ import {
|
|
|
259
262
|
createTaskWithSiblingAwareness,
|
|
260
263
|
gitlabContextKey,
|
|
261
264
|
pageContextKey
|
|
262
|
-
} from "./cli-
|
|
265
|
+
} from "./cli-y4ycrnjc.js";
|
|
263
266
|
import {
|
|
264
267
|
getAppUrl,
|
|
265
268
|
getConfiguredAppUrls,
|
|
@@ -571,7 +574,7 @@ import {
|
|
|
571
574
|
upsertSwarmConfig,
|
|
572
575
|
validateConfigValue,
|
|
573
576
|
withFavoriteFlags
|
|
574
|
-
} from "./cli-
|
|
577
|
+
} from "./cli-ncfgsqbd.js";
|
|
575
578
|
import"./cli-z2zcxes1.js";
|
|
576
579
|
import {
|
|
577
580
|
init_zod
|
|
@@ -586,7 +589,7 @@ import {
|
|
|
586
589
|
import {
|
|
587
590
|
init_package,
|
|
588
591
|
package_default
|
|
589
|
-
} from "./cli-
|
|
592
|
+
} from "./cli-c3frqf65.js";
|
|
590
593
|
import {
|
|
591
594
|
init_secret_scrubber,
|
|
592
595
|
registerVolatileSecret,
|
|
@@ -636,6 +639,146 @@ import {
|
|
|
636
639
|
} from "node:http";
|
|
637
640
|
init_db();
|
|
638
641
|
|
|
642
|
+
// src/be/rbac-audit.ts
|
|
643
|
+
init_db();
|
|
644
|
+
var FLUSH_INTERVAL_MS = 2000;
|
|
645
|
+
var FLUSH_MAX_ROWS = 200;
|
|
646
|
+
var AUDIT_GC_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
647
|
+
var DEFAULT_RETENTION_DAYS = 30;
|
|
648
|
+
var buffer = [];
|
|
649
|
+
var flushTimer = null;
|
|
650
|
+
var auditGcTimer = null;
|
|
651
|
+
var thresholdFlushScheduled = false;
|
|
652
|
+
function isAuditDisabled() {
|
|
653
|
+
return process.env.RBAC_AUDIT_DISABLED === "true";
|
|
654
|
+
}
|
|
655
|
+
function principalIdOf(principal) {
|
|
656
|
+
switch (principal.kind) {
|
|
657
|
+
case "agent":
|
|
658
|
+
return principal.agentId;
|
|
659
|
+
case "user":
|
|
660
|
+
return principal.userId;
|
|
661
|
+
case "operator":
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function resourceIdOf(resource) {
|
|
666
|
+
if (!resource)
|
|
667
|
+
return null;
|
|
668
|
+
switch (resource.kind) {
|
|
669
|
+
case "task":
|
|
670
|
+
return resource.taskId;
|
|
671
|
+
case "agent":
|
|
672
|
+
return resource.agentId;
|
|
673
|
+
case "kv-namespace":
|
|
674
|
+
return resource.namespace;
|
|
675
|
+
case "owned":
|
|
676
|
+
return resource.ownerAgentId ?? null;
|
|
677
|
+
case "none":
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function toRow(check, decision) {
|
|
682
|
+
return {
|
|
683
|
+
principalType: check.principal.kind,
|
|
684
|
+
principalId: principalIdOf(check.principal),
|
|
685
|
+
originatorUserId: null,
|
|
686
|
+
verb: check.verb,
|
|
687
|
+
resourceType: check.resource?.kind ?? null,
|
|
688
|
+
resourceId: resourceIdOf(check.resource),
|
|
689
|
+
decision: decision.allow ? "allow" : "deny",
|
|
690
|
+
reason: decision.allow ? null : decision.reason,
|
|
691
|
+
source: check.source
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function enqueueAuditRow(check, decision) {
|
|
695
|
+
if (isAuditDisabled())
|
|
696
|
+
return;
|
|
697
|
+
try {
|
|
698
|
+
buffer.push(toRow(check, decision));
|
|
699
|
+
if (buffer.length >= FLUSH_MAX_ROWS && !thresholdFlushScheduled) {
|
|
700
|
+
thresholdFlushScheduled = true;
|
|
701
|
+
const t = setTimeout(() => {
|
|
702
|
+
thresholdFlushScheduled = false;
|
|
703
|
+
flushAuditBuffer();
|
|
704
|
+
}, 0);
|
|
705
|
+
if (typeof t.unref === "function")
|
|
706
|
+
t.unref();
|
|
707
|
+
}
|
|
708
|
+
} catch (err) {
|
|
709
|
+
console.warn("[rbac-audit] enqueue failed, dropping row:", err.message);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function flushAuditBuffer() {
|
|
713
|
+
if (buffer.length === 0)
|
|
714
|
+
return;
|
|
715
|
+
const rows = buffer;
|
|
716
|
+
buffer = [];
|
|
717
|
+
try {
|
|
718
|
+
const db = getDb();
|
|
719
|
+
const stmt = db.prepare(`INSERT INTO permission_audit
|
|
720
|
+
(principalType, principalId, originatorUserId, verb, resourceType, resourceId, decision, reason, source)
|
|
721
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
722
|
+
const insertAll = db.transaction((batch) => {
|
|
723
|
+
for (const r of batch) {
|
|
724
|
+
stmt.run(r.principalType, r.principalId, r.originatorUserId, r.verb, r.resourceType, r.resourceId, r.decision, r.reason, r.source);
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
insertAll(rows);
|
|
728
|
+
} catch (err) {
|
|
729
|
+
console.warn(`[rbac-audit] flush failed, dropping ${rows.length} row(s):`, err.message);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function flushIntervalMs() {
|
|
733
|
+
const v = Number(process.env.RBAC_AUDIT_FLUSH_MS);
|
|
734
|
+
return Number.isFinite(v) && v > 0 ? v : FLUSH_INTERVAL_MS;
|
|
735
|
+
}
|
|
736
|
+
function startAuditWriter(intervalMs = flushIntervalMs()) {
|
|
737
|
+
if (flushTimer)
|
|
738
|
+
return;
|
|
739
|
+
flushTimer = setInterval(() => flushAuditBuffer(), intervalMs);
|
|
740
|
+
if (typeof flushTimer?.unref === "function")
|
|
741
|
+
flushTimer.unref();
|
|
742
|
+
}
|
|
743
|
+
function stopAuditWriter() {
|
|
744
|
+
if (flushTimer) {
|
|
745
|
+
clearInterval(flushTimer);
|
|
746
|
+
flushTimer = null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
function purgeExpiredAuditRows() {
|
|
750
|
+
try {
|
|
751
|
+
const days = Number(process.env.RBAC_AUDIT_RETENTION_DAYS) || DEFAULT_RETENTION_DAYS;
|
|
752
|
+
const result = getDb().prepare("DELETE FROM permission_audit WHERE ts < datetime('now', ?)").run(`-${days} days`);
|
|
753
|
+
return result.changes;
|
|
754
|
+
} catch (err) {
|
|
755
|
+
console.warn("[rbac-audit] retention purge failed:", err.message);
|
|
756
|
+
return 0;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
function startAuditGc(intervalMs = AUDIT_GC_INTERVAL_MS) {
|
|
760
|
+
if (auditGcTimer)
|
|
761
|
+
return;
|
|
762
|
+
const purged = purgeExpiredAuditRows();
|
|
763
|
+
if (purged > 0) {
|
|
764
|
+
console.log(`[rbac-audit] Initial retention purge removed ${purged} audit row(s)`);
|
|
765
|
+
}
|
|
766
|
+
auditGcTimer = setInterval(() => {
|
|
767
|
+
const n = purgeExpiredAuditRows();
|
|
768
|
+
if (n > 0) {
|
|
769
|
+
console.log(`[rbac-audit] Retention purge removed ${n} audit row(s)`);
|
|
770
|
+
}
|
|
771
|
+
}, intervalMs);
|
|
772
|
+
if (typeof auditGcTimer?.unref === "function")
|
|
773
|
+
auditGcTimer.unref();
|
|
774
|
+
}
|
|
775
|
+
function stopAuditGc() {
|
|
776
|
+
if (auditGcTimer) {
|
|
777
|
+
clearInterval(auditGcTimer);
|
|
778
|
+
auditGcTimer = null;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
639
782
|
// src/gitlab/auth.ts
|
|
640
783
|
var initialized = false;
|
|
641
784
|
var webhookSecret = null;
|
|
@@ -2845,12 +2988,35 @@ async function handleCodexOAuthKeepWarm(req, res, pathSegments) {
|
|
|
2845
2988
|
init_zod();
|
|
2846
2989
|
init_db();
|
|
2847
2990
|
init_swarm_config_guard();
|
|
2991
|
+
init_request_auth_context();
|
|
2848
2992
|
init_secret_scrubber();
|
|
2849
2993
|
var MAX_ENV_PRESENCE_KEYS = 200;
|
|
2850
2994
|
var API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY"]);
|
|
2851
2995
|
function stripApiOnlyKeys(configs) {
|
|
2852
2996
|
return configs.filter((config) => !API_ONLY_CONFIG_KEYS.has(config.key));
|
|
2853
2997
|
}
|
|
2998
|
+
function singleHeader(req, name) {
|
|
2999
|
+
const raw = req.headers[name];
|
|
3000
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
3001
|
+
}
|
|
3002
|
+
function ensureConfigAdmin(req, res, verb) {
|
|
3003
|
+
const auth = getRequestAuth(req);
|
|
3004
|
+
if (auth?.kind === "operator" || auth?.kind === "user")
|
|
3005
|
+
return true;
|
|
3006
|
+
const agentId = singleHeader(req, "x-agent-id");
|
|
3007
|
+
const agent = agentId ? getAgentById(agentId) : undefined;
|
|
3008
|
+
const decision = can({
|
|
3009
|
+
principal: { kind: "agent", agentId: agentId ?? "", isLead: agent?.isLead ?? false },
|
|
3010
|
+
verb,
|
|
3011
|
+
resource: { kind: "none" },
|
|
3012
|
+
source: "http"
|
|
3013
|
+
});
|
|
3014
|
+
if (!decision.allow) {
|
|
3015
|
+
jsonError(res, verb === "config.write.any" ? "Writing swarm config requires the lead agent" : "Deleting swarm config requires the lead agent", 403);
|
|
3016
|
+
return false;
|
|
3017
|
+
}
|
|
3018
|
+
return true;
|
|
3019
|
+
}
|
|
2854
3020
|
var getResolvedConfigRoute = route({
|
|
2855
3021
|
method: "get",
|
|
2856
3022
|
path: "/api/config/resolved",
|
|
@@ -2928,6 +3094,7 @@ var upsertConfig = route({
|
|
|
2928
3094
|
pattern: ["api", "config"],
|
|
2929
3095
|
summary: "Create or update a config entry (reserved env-only keys are rejected). Global-scope writes auto-trigger an integrations reload (debounced ~250ms) so Slack/GitHub/Linear/Jira/AgentMail pick up new credentials without an explicit /api/config/reload call.",
|
|
2930
3096
|
tags: ["Config"],
|
|
3097
|
+
rbac: { permission: "config.write.any" },
|
|
2931
3098
|
body: exports_external.object({
|
|
2932
3099
|
scope: exports_external.enum(["global", "agent", "repo"]),
|
|
2933
3100
|
scopeId: exports_external.string().nullish(),
|
|
@@ -2948,6 +3115,7 @@ var deleteConfig = route({
|
|
|
2948
3115
|
pattern: ["api", "config", null],
|
|
2949
3116
|
summary: "Delete a config entry by ID (including legacy reserved rows for cleanup). Global-scope deletes auto-trigger an integrations reload.",
|
|
2950
3117
|
tags: ["Config"],
|
|
3118
|
+
rbac: { permission: "config.delete.any" },
|
|
2951
3119
|
params: exports_external.object({ id: exports_external.string() }),
|
|
2952
3120
|
responses: {
|
|
2953
3121
|
200: { description: "Config deleted" },
|
|
@@ -3041,6 +3209,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
3041
3209
|
const parsed = await upsertConfig.parse(req, res, pathSegments, queryParams);
|
|
3042
3210
|
if (!parsed)
|
|
3043
3211
|
return true;
|
|
3212
|
+
if (!ensureConfigAdmin(req, res, "config.write.any"))
|
|
3213
|
+
return true;
|
|
3044
3214
|
const { scope, scopeId, key, value, isSecret, envPath, description } = parsed.body;
|
|
3045
3215
|
if (scope === "global" && scopeId) {
|
|
3046
3216
|
jsonError(res, "Global scope must not have scopeId", 400);
|
|
@@ -3084,6 +3254,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
3084
3254
|
const parsed = await deleteConfig.parse(req, res, pathSegments, queryParams);
|
|
3085
3255
|
if (!parsed)
|
|
3086
3256
|
return true;
|
|
3257
|
+
if (!ensureConfigAdmin(req, res, "config.delete.any"))
|
|
3258
|
+
return true;
|
|
3087
3259
|
const existing = getSwarmConfigLookupById(parsed.params.id);
|
|
3088
3260
|
if (!existing) {
|
|
3089
3261
|
jsonError(res, "Config not found", 404);
|
|
@@ -3420,6 +3592,7 @@ var putFavorite = route({
|
|
|
3420
3592
|
pattern: ["api", "favorites"],
|
|
3421
3593
|
summary: "Set favorite state for an item",
|
|
3422
3594
|
tags: ["Favorites"],
|
|
3595
|
+
rbac: { ungated: "self-scoped to the authenticated user; no cross-principal access" },
|
|
3423
3596
|
body: exports_external.object({
|
|
3424
3597
|
itemType: FavoriteItemTypeSchema,
|
|
3425
3598
|
itemId: exports_external.string().min(1),
|
|
@@ -3985,7 +4158,8 @@ var uploadTaskFileRoute = route({
|
|
|
3985
4158
|
404: { description: "Task not found" },
|
|
3986
4159
|
413: { description: "Upload exceeds 50 MiB" }
|
|
3987
4160
|
},
|
|
3988
|
-
auth: { apiKey: true, agentId: true }
|
|
4161
|
+
auth: { apiKey: true, agentId: true },
|
|
4162
|
+
rbac: { permission: "task.fs.mutate" }
|
|
3989
4163
|
});
|
|
3990
4164
|
var getTaskFileRoute = route({
|
|
3991
4165
|
method: "get",
|
|
@@ -4038,7 +4212,8 @@ var deleteTaskFileRoute = route({
|
|
|
4038
4212
|
403: { description: "Caller cannot mutate this task" },
|
|
4039
4213
|
404: { description: "Task or attachment not found" }
|
|
4040
4214
|
},
|
|
4041
|
-
auth: { apiKey: true, agentId: true }
|
|
4215
|
+
auth: { apiKey: true, agentId: true },
|
|
4216
|
+
rbac: { permission: "task.fs.mutate" }
|
|
4042
4217
|
});
|
|
4043
4218
|
async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
4044
4219
|
if (ensureAgentCredentialsRoute.match(req.method, pathSegments)) {
|
|
@@ -4138,7 +4313,7 @@ async function sendUpload(req, res, taskId, query, agentId) {
|
|
|
4138
4313
|
return true;
|
|
4139
4314
|
}
|
|
4140
4315
|
const provider = getFileStorageProvider();
|
|
4141
|
-
const contentType =
|
|
4316
|
+
const contentType = singleHeader2(req, "content-type") ?? "application/octet-stream";
|
|
4142
4317
|
const scope = { taskId, name: query.name };
|
|
4143
4318
|
let uploaded;
|
|
4144
4319
|
try {
|
|
@@ -4273,31 +4448,39 @@ function findAttachment(taskId, attachmentId, res) {
|
|
|
4273
4448
|
return attachment;
|
|
4274
4449
|
}
|
|
4275
4450
|
function canMutateTask(task, myAgentId, req) {
|
|
4451
|
+
const resource = {
|
|
4452
|
+
kind: "task",
|
|
4453
|
+
taskId: task.id,
|
|
4454
|
+
agentId: task.agentId,
|
|
4455
|
+
creatorAgentId: task.creatorAgentId
|
|
4456
|
+
};
|
|
4276
4457
|
const auth = getRequestAuth(req);
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4458
|
+
let principal;
|
|
4459
|
+
if (auth?.kind === "operator") {
|
|
4460
|
+
principal = { kind: "operator" };
|
|
4461
|
+
} else if (auth?.kind === "user") {
|
|
4462
|
+
principal = { kind: "user", userId: auth.userId };
|
|
4463
|
+
} else {
|
|
4464
|
+
if (!myAgentId)
|
|
4465
|
+
return false;
|
|
4466
|
+
const agent = getAgentById(myAgentId);
|
|
4467
|
+
principal = { kind: "agent", agentId: myAgentId, isLead: agent?.isLead ?? false };
|
|
4468
|
+
}
|
|
4469
|
+
return can({ principal, verb: "task.fs.mutate", resource, source: "http" }).allow;
|
|
4287
4470
|
}
|
|
4288
4471
|
async function readRawBody(req, maxBytes) {
|
|
4289
4472
|
const chunks = [];
|
|
4290
4473
|
let total = 0;
|
|
4291
4474
|
for await (const chunk of req) {
|
|
4292
|
-
const
|
|
4293
|
-
total +=
|
|
4475
|
+
const buffer2 = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
4476
|
+
total += buffer2.byteLength;
|
|
4294
4477
|
if (total > maxBytes)
|
|
4295
4478
|
return BODY_TOO_LARGE;
|
|
4296
|
-
chunks.push(
|
|
4479
|
+
chunks.push(buffer2);
|
|
4297
4480
|
}
|
|
4298
4481
|
return Buffer.concat(chunks);
|
|
4299
4482
|
}
|
|
4300
|
-
function
|
|
4483
|
+
function singleHeader2(req, name) {
|
|
4301
4484
|
const raw = req.headers[name];
|
|
4302
4485
|
return Array.isArray(raw) ? raw[0] : raw;
|
|
4303
4486
|
}
|
|
@@ -4589,7 +4772,8 @@ var putKvHeader = route({
|
|
|
4589
4772
|
tags: ["KV"],
|
|
4590
4773
|
params: exports_external.object({ key: KvKeySchema }),
|
|
4591
4774
|
body: kvSetBodySchema,
|
|
4592
|
-
responses: RESPONSES_PUT
|
|
4775
|
+
responses: RESPONSES_PUT,
|
|
4776
|
+
rbac: { permission: "kv.write.any" }
|
|
4593
4777
|
});
|
|
4594
4778
|
var deleteKvHeader = route({
|
|
4595
4779
|
method: "delete",
|
|
@@ -4603,7 +4787,8 @@ var deleteKvHeader = route({
|
|
|
4603
4787
|
404: { description: "KV entry not found" },
|
|
4604
4788
|
403: { description: "Caller may not write this namespace" },
|
|
4605
4789
|
400: { description: "Validation error or unresolvable namespace" }
|
|
4606
|
-
}
|
|
4790
|
+
},
|
|
4791
|
+
rbac: { permission: "kv.write.any" }
|
|
4607
4792
|
});
|
|
4608
4793
|
var incrKvHeader = route({
|
|
4609
4794
|
method: "post",
|
|
@@ -4613,7 +4798,8 @@ var incrKvHeader = route({
|
|
|
4613
4798
|
tags: ["KV"],
|
|
4614
4799
|
params: exports_external.object({ key: KvKeySchema }),
|
|
4615
4800
|
body: kvIncrBodySchema,
|
|
4616
|
-
responses: RESPONSES_PUT
|
|
4801
|
+
responses: RESPONSES_PUT,
|
|
4802
|
+
rbac: { permission: "kv.write.any" }
|
|
4617
4803
|
});
|
|
4618
4804
|
var listKvHeader = route({
|
|
4619
4805
|
method: "get",
|
|
@@ -4641,7 +4827,8 @@ var putKvExplicit = route({
|
|
|
4641
4827
|
tags: ["KV"],
|
|
4642
4828
|
params: exports_external.object({ namespace: KvNamespaceSchema, key: KvKeySchema }),
|
|
4643
4829
|
body: kvSetBodySchema,
|
|
4644
|
-
responses: RESPONSES_PUT
|
|
4830
|
+
responses: RESPONSES_PUT,
|
|
4831
|
+
rbac: { permission: "kv.write.any" }
|
|
4645
4832
|
});
|
|
4646
4833
|
var deleteKvExplicit = route({
|
|
4647
4834
|
method: "delete",
|
|
@@ -4654,7 +4841,8 @@ var deleteKvExplicit = route({
|
|
|
4654
4841
|
204: { description: "KV entry deleted" },
|
|
4655
4842
|
404: { description: "KV entry not found" },
|
|
4656
4843
|
403: { description: "Caller may not write this namespace" }
|
|
4657
|
-
}
|
|
4844
|
+
},
|
|
4845
|
+
rbac: { permission: "kv.write.any" }
|
|
4658
4846
|
});
|
|
4659
4847
|
var incrKvExplicit = route({
|
|
4660
4848
|
method: "post",
|
|
@@ -4664,7 +4852,8 @@ var incrKvExplicit = route({
|
|
|
4664
4852
|
tags: ["KV"],
|
|
4665
4853
|
params: exports_external.object({ namespace: KvNamespaceSchema, key: KvKeySchema }),
|
|
4666
4854
|
body: kvIncrBodySchema,
|
|
4667
|
-
responses: RESPONSES_PUT
|
|
4855
|
+
responses: RESPONSES_PUT,
|
|
4856
|
+
rbac: { permission: "kv.write.any" }
|
|
4668
4857
|
});
|
|
4669
4858
|
var listKvExplicit = route({
|
|
4670
4859
|
method: "get",
|
|
@@ -4690,14 +4879,14 @@ function decodeKvSegment(res, raw, label) {
|
|
|
4690
4879
|
}
|
|
4691
4880
|
return decoded;
|
|
4692
4881
|
}
|
|
4693
|
-
function
|
|
4882
|
+
function singleHeader3(req, name) {
|
|
4694
4883
|
const raw = req.headers[name];
|
|
4695
4884
|
if (raw === undefined)
|
|
4696
4885
|
return;
|
|
4697
4886
|
return Array.isArray(raw) ? raw[0] : raw;
|
|
4698
4887
|
}
|
|
4699
4888
|
function resolveNamespaceFromHeaders(req) {
|
|
4700
|
-
const pageId =
|
|
4889
|
+
const pageId = singleHeader3(req, "x-page-id");
|
|
4701
4890
|
if (pageId) {
|
|
4702
4891
|
try {
|
|
4703
4892
|
return pageContextKey({ pageId });
|
|
@@ -4705,7 +4894,7 @@ function resolveNamespaceFromHeaders(req) {
|
|
|
4705
4894
|
return null;
|
|
4706
4895
|
}
|
|
4707
4896
|
}
|
|
4708
|
-
const sourceTaskId =
|
|
4897
|
+
const sourceTaskId = singleHeader3(req, "x-source-task-id");
|
|
4709
4898
|
if (sourceTaskId) {
|
|
4710
4899
|
const task = getTaskById(sourceTaskId);
|
|
4711
4900
|
if (task?.contextKey)
|
|
@@ -4716,7 +4905,7 @@ function resolveNamespaceFromHeaders(req) {
|
|
|
4716
4905
|
} catch {}
|
|
4717
4906
|
}
|
|
4718
4907
|
}
|
|
4719
|
-
const agentId =
|
|
4908
|
+
const agentId = singleHeader3(req, "x-agent-id");
|
|
4720
4909
|
if (agentId) {
|
|
4721
4910
|
try {
|
|
4722
4911
|
return agentContextKey({ agentId });
|
|
@@ -4727,8 +4916,8 @@ function resolveNamespaceFromHeaders(req) {
|
|
|
4727
4916
|
return null;
|
|
4728
4917
|
}
|
|
4729
4918
|
function buildAuthCtx(req) {
|
|
4730
|
-
const callerAgentId =
|
|
4731
|
-
const pageId =
|
|
4919
|
+
const callerAgentId = singleHeader3(req, "x-agent-id");
|
|
4920
|
+
const pageId = singleHeader3(req, "x-page-id");
|
|
4732
4921
|
let isLead = false;
|
|
4733
4922
|
if (callerAgentId) {
|
|
4734
4923
|
const agent = getAgentById(callerAgentId);
|
|
@@ -4744,12 +4933,16 @@ function authorizeWrite(namespace, ctx) {
|
|
|
4744
4933
|
return null;
|
|
4745
4934
|
}
|
|
4746
4935
|
if (namespace.startsWith("task:agent:")) {
|
|
4747
|
-
const
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4936
|
+
const allowed = ctx.callerAgentId != null && can({
|
|
4937
|
+
principal: { kind: "agent", agentId: ctx.callerAgentId, isLead: ctx.isLead },
|
|
4938
|
+
verb: "kv.write.any",
|
|
4939
|
+
resource: { kind: "kv-namespace", namespace },
|
|
4940
|
+
source: "http"
|
|
4941
|
+
}).allow;
|
|
4942
|
+
if (!allowed) {
|
|
4943
|
+
return { status: 403, message: "writes to another agent's namespace require lead" };
|
|
4944
|
+
}
|
|
4945
|
+
return null;
|
|
4753
4946
|
}
|
|
4754
4947
|
return null;
|
|
4755
4948
|
}
|
|
@@ -4789,7 +4982,7 @@ function encodeValueOrError(res, value, valueType) {
|
|
|
4789
4982
|
}
|
|
4790
4983
|
}
|
|
4791
4984
|
async function handleKv(req, res, pathSegments, queryParams) {
|
|
4792
|
-
const hasPageHeader =
|
|
4985
|
+
const hasPageHeader = singleHeader3(req, "x-page-id") !== undefined;
|
|
4793
4986
|
if (hasPageHeader && pathSegments[0] === "api" && pathSegments[1] === "kv" && pathSegments[2] === "_") {
|
|
4794
4987
|
pathSegments = [pathSegments[0], pathSegments[1], ...pathSegments.slice(4)];
|
|
4795
4988
|
}
|
|
@@ -6286,6 +6479,7 @@ var SDK_TOOL_NAME_MAP = {
|
|
|
6286
6479
|
schedule_list: "list-schedules",
|
|
6287
6480
|
schedule_create: "create-schedule",
|
|
6288
6481
|
schedule_update: "update-schedule",
|
|
6482
|
+
schedule_patch: "patch-schedule",
|
|
6289
6483
|
schedule_delete: "delete-schedule",
|
|
6290
6484
|
schedule_runNow: "run-schedule-now",
|
|
6291
6485
|
script_search: "script-search",
|
|
@@ -11867,6 +12061,26 @@ init_zod();
|
|
|
11867
12061
|
var import_cron_parser = __toESM(require_dist(), 1);
|
|
11868
12062
|
init_db();
|
|
11869
12063
|
init_types();
|
|
12064
|
+
var scheduleUpdateBodySchema = exports_external.object({
|
|
12065
|
+
name: exports_external.string().optional(),
|
|
12066
|
+
description: exports_external.string().optional(),
|
|
12067
|
+
cronExpression: exports_external.string().nullable().optional(),
|
|
12068
|
+
intervalMs: exports_external.number().int().positive().nullable().optional(),
|
|
12069
|
+
taskTemplate: exports_external.string().optional(),
|
|
12070
|
+
taskType: exports_external.string().optional(),
|
|
12071
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
12072
|
+
priority: exports_external.number().int().optional(),
|
|
12073
|
+
targetAgentId: exports_external.string().uuid().nullable().optional(),
|
|
12074
|
+
enabled: exports_external.boolean().optional(),
|
|
12075
|
+
timezone: exports_external.string().optional(),
|
|
12076
|
+
model: exports_external.string().nullable().optional(),
|
|
12077
|
+
modelTier: ModelTierSchema.nullable().optional(),
|
|
12078
|
+
nextRunAt: exports_external.string().nullable().optional(),
|
|
12079
|
+
targetType: ScheduledTaskTargetTypeSchema.optional(),
|
|
12080
|
+
workflowId: exports_external.string().uuid().nullable().optional(),
|
|
12081
|
+
scriptName: exports_external.string().nullable().optional(),
|
|
12082
|
+
scriptArgs: exports_external.record(exports_external.string(), exports_external.unknown()).nullable().optional()
|
|
12083
|
+
});
|
|
11870
12084
|
var createSchedule = route({
|
|
11871
12085
|
method: "post",
|
|
11872
12086
|
path: "/api/schedules",
|
|
@@ -11929,6 +12143,8 @@ var listSchedules = route({
|
|
|
11929
12143
|
workflowId: exports_external.string().uuid().optional(),
|
|
11930
12144
|
scriptName: exports_external.string().optional(),
|
|
11931
12145
|
hideCompleted: exports_external.enum(["true", "false"]).optional().transform((v) => v === undefined ? undefined : v === "true"),
|
|
12146
|
+
consecutiveErrorsMin: exports_external.coerce.number().int().min(0).optional(),
|
|
12147
|
+
lastRunStatus: exports_external.enum(["failed", "succeeded"]).optional(),
|
|
11932
12148
|
fields: exports_external.enum(["full", "slim"]).optional()
|
|
11933
12149
|
}),
|
|
11934
12150
|
responses: {
|
|
@@ -11954,26 +12170,7 @@ var updateSchedule = route({
|
|
|
11954
12170
|
summary: "Update a schedule",
|
|
11955
12171
|
tags: ["Schedules"],
|
|
11956
12172
|
params: exports_external.object({ id: exports_external.string() }),
|
|
11957
|
-
body:
|
|
11958
|
-
name: exports_external.string().optional(),
|
|
11959
|
-
description: exports_external.string().optional(),
|
|
11960
|
-
cronExpression: exports_external.string().nullable().optional(),
|
|
11961
|
-
intervalMs: exports_external.number().int().positive().nullable().optional(),
|
|
11962
|
-
taskTemplate: exports_external.string().optional(),
|
|
11963
|
-
taskType: exports_external.string().optional(),
|
|
11964
|
-
tags: exports_external.array(exports_external.string()).optional(),
|
|
11965
|
-
priority: exports_external.number().int().optional(),
|
|
11966
|
-
targetAgentId: exports_external.string().uuid().optional(),
|
|
11967
|
-
enabled: exports_external.boolean().optional(),
|
|
11968
|
-
timezone: exports_external.string().optional(),
|
|
11969
|
-
model: exports_external.string().optional(),
|
|
11970
|
-
modelTier: ModelTierSchema.nullable().optional(),
|
|
11971
|
-
nextRunAt: exports_external.string().nullable().optional(),
|
|
11972
|
-
targetType: ScheduledTaskTargetTypeSchema.optional(),
|
|
11973
|
-
workflowId: exports_external.string().uuid().nullable().optional(),
|
|
11974
|
-
scriptName: exports_external.string().nullable().optional(),
|
|
11975
|
-
scriptArgs: exports_external.record(exports_external.string(), exports_external.unknown()).nullable().optional()
|
|
11976
|
-
}),
|
|
12173
|
+
body: scheduleUpdateBodySchema,
|
|
11977
12174
|
responses: {
|
|
11978
12175
|
200: { description: "Schedule updated" },
|
|
11979
12176
|
400: { description: "Validation error" },
|
|
@@ -11981,6 +12178,25 @@ var updateSchedule = route({
|
|
|
11981
12178
|
409: { description: "Duplicate name" }
|
|
11982
12179
|
}
|
|
11983
12180
|
});
|
|
12181
|
+
var patchSchedule = route({
|
|
12182
|
+
method: "patch",
|
|
12183
|
+
path: "/api/schedules/{id}",
|
|
12184
|
+
pattern: ["api", "schedules", null],
|
|
12185
|
+
summary: "Patch a schedule",
|
|
12186
|
+
description: "Partially updates a schedule by shallow-merging provided fields over the existing row.",
|
|
12187
|
+
tags: ["Schedules"],
|
|
12188
|
+
params: exports_external.object({ id: exports_external.string() }),
|
|
12189
|
+
body: scheduleUpdateBodySchema,
|
|
12190
|
+
responses: {
|
|
12191
|
+
200: { description: "Schedule patched" },
|
|
12192
|
+
400: { description: "Validation error" },
|
|
12193
|
+
404: { description: "Schedule not found" },
|
|
12194
|
+
409: { description: "Duplicate name" }
|
|
12195
|
+
},
|
|
12196
|
+
rbac: {
|
|
12197
|
+
ungated: "matches existing schedule update/delete posture: bearer-authenticated agents may manage schedules"
|
|
12198
|
+
}
|
|
12199
|
+
});
|
|
11984
12200
|
var deleteSchedule = route({
|
|
11985
12201
|
method: "delete",
|
|
11986
12202
|
path: "/api/schedules/{id}",
|
|
@@ -12005,7 +12221,9 @@ async function handleSchedules(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
12005
12221
|
targetType: parsed.query.targetType,
|
|
12006
12222
|
workflowId: parsed.query.workflowId,
|
|
12007
12223
|
scriptName: parsed.query.scriptName,
|
|
12008
|
-
hideCompleted: parsed.query.hideCompleted
|
|
12224
|
+
hideCompleted: parsed.query.hideCompleted,
|
|
12225
|
+
consecutiveErrorsMin: parsed.query.consecutiveErrorsMin,
|
|
12226
|
+
lastRunStatus: parsed.query.lastRunStatus
|
|
12009
12227
|
};
|
|
12010
12228
|
const schedules = parsed.query.fields === "full" ? getScheduledTasks(filters) : getScheduledTasks(filters, { slim: true });
|
|
12011
12229
|
const userId = resolveHttpAuditUserId(req, myAgentId);
|
|
@@ -12180,8 +12398,9 @@ async function handleSchedules(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
12180
12398
|
json(res, decorated ?? schedule);
|
|
12181
12399
|
return true;
|
|
12182
12400
|
}
|
|
12183
|
-
if (updateSchedule.match(req.method, pathSegments)) {
|
|
12184
|
-
const
|
|
12401
|
+
if (updateSchedule.match(req.method, pathSegments) || patchSchedule.match(req.method, pathSegments)) {
|
|
12402
|
+
const routeHandle = updateSchedule.match(req.method, pathSegments) ? updateSchedule : patchSchedule;
|
|
12403
|
+
const parsed = await routeHandle.parse(req, res, pathSegments, queryParams);
|
|
12185
12404
|
if (!parsed)
|
|
12186
12405
|
return true;
|
|
12187
12406
|
const body = parsed.body;
|
|
@@ -12888,7 +13107,8 @@ var upsertRoute2 = route({
|
|
|
12888
13107
|
200: { description: "Script upserted" },
|
|
12889
13108
|
400: { description: "Validation or typecheck failure" },
|
|
12890
13109
|
403: { description: "Global write requires lead agent" }
|
|
12891
|
-
}
|
|
13110
|
+
},
|
|
13111
|
+
rbac: { permission: "script.global.write" }
|
|
12892
13112
|
});
|
|
12893
13113
|
var runRoute = route({
|
|
12894
13114
|
method: "post",
|
|
@@ -12933,7 +13153,8 @@ var deleteRoute = route({
|
|
|
12933
13153
|
200: { description: "Delete result" },
|
|
12934
13154
|
400: { description: "Validation error" },
|
|
12935
13155
|
403: { description: "Global delete requires lead agent" }
|
|
12936
|
-
}
|
|
13156
|
+
},
|
|
13157
|
+
rbac: { permission: "script.global.delete" }
|
|
12937
13158
|
});
|
|
12938
13159
|
var typesRoute = route({
|
|
12939
13160
|
method: "get",
|
|
@@ -13151,6 +13372,18 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
13151
13372
|
const agent = requireAgent2(res, agentId);
|
|
13152
13373
|
if (!agent)
|
|
13153
13374
|
return true;
|
|
13375
|
+
if (parsed.body.scope === "global") {
|
|
13376
|
+
const decision = can({
|
|
13377
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
13378
|
+
verb: "script.global.write",
|
|
13379
|
+
resource: { kind: "owned", scope: "global" },
|
|
13380
|
+
source: "http"
|
|
13381
|
+
});
|
|
13382
|
+
if (!decision.allow) {
|
|
13383
|
+
jsonError(res, "Global write requires lead agent", 403);
|
|
13384
|
+
return true;
|
|
13385
|
+
}
|
|
13386
|
+
}
|
|
13154
13387
|
const typecheck = typecheckScript(parsed.body.source, { agentId: agent.id });
|
|
13155
13388
|
if (!typecheck.ok) {
|
|
13156
13389
|
json(res, {
|
|
@@ -13406,6 +13639,18 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
13406
13639
|
const agent = requireAgent2(res, agentId);
|
|
13407
13640
|
if (!agent)
|
|
13408
13641
|
return true;
|
|
13642
|
+
if (parsed.query.scope === "global") {
|
|
13643
|
+
const decision = can({
|
|
13644
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
13645
|
+
verb: "script.global.delete",
|
|
13646
|
+
resource: { kind: "owned", scope: "global" },
|
|
13647
|
+
source: "http"
|
|
13648
|
+
});
|
|
13649
|
+
if (!decision.allow) {
|
|
13650
|
+
jsonError(res, "Global delete requires lead agent", 403);
|
|
13651
|
+
return true;
|
|
13652
|
+
}
|
|
13653
|
+
}
|
|
13409
13654
|
const deleted = deleteScript({
|
|
13410
13655
|
name: parsed.params.name,
|
|
13411
13656
|
scope: parsed.query.scope,
|
|
@@ -18026,6 +18271,9 @@ var listWorkflowsRoute = route({
|
|
|
18026
18271
|
description: "Returns workflows WITHOUT the heavy `definition` (the full DAG) by default — the list view only needs a `nodeCount`, which is included. Pass `fields=full` to restore `definition` + trigger config. Fetch the full workflow via `GET /api/workflows/{id}`.",
|
|
18027
18272
|
tags: ["Workflows"],
|
|
18028
18273
|
query: exports_external.object({
|
|
18274
|
+
enabled: exports_external.enum(["true", "false"]).optional().transform((v) => v === undefined ? undefined : v === "true"),
|
|
18275
|
+
consecutiveErrorsMin: exports_external.coerce.number().int().min(0).optional(),
|
|
18276
|
+
lastRunStatus: WorkflowRunStatusSchema.optional(),
|
|
18029
18277
|
fields: exports_external.enum(["full", "slim"]).optional()
|
|
18030
18278
|
}),
|
|
18031
18279
|
responses: {
|
|
@@ -18342,10 +18590,15 @@ async function handleWorkflows(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
18342
18590
|
if (!parsed)
|
|
18343
18591
|
return true;
|
|
18344
18592
|
const userId = resolveHttpAuditUserId(req, myAgentId);
|
|
18593
|
+
const filters = {
|
|
18594
|
+
enabled: parsed.query.enabled,
|
|
18595
|
+
consecutiveErrorsMin: parsed.query.consecutiveErrorsMin,
|
|
18596
|
+
lastRunStatus: parsed.query.lastRunStatus
|
|
18597
|
+
};
|
|
18345
18598
|
if (parsed.query.fields === "full") {
|
|
18346
|
-
json(res, withFavoriteFlags(listWorkflows(), { userId, itemType: "workflow" }));
|
|
18599
|
+
json(res, withFavoriteFlags(listWorkflows(filters), { userId, itemType: "workflow" }));
|
|
18347
18600
|
} else {
|
|
18348
|
-
json(res, withFavoriteFlags(listWorkflows(
|
|
18601
|
+
json(res, withFavoriteFlags(listWorkflows(filters, { slim: true }), {
|
|
18349
18602
|
userId,
|
|
18350
18603
|
itemType: "workflow"
|
|
18351
18604
|
}));
|
|
@@ -19027,18 +19280,22 @@ async function shutdown() {
|
|
|
19027
19280
|
sessionsProcessed: getServerSessionsProcessed()
|
|
19028
19281
|
});
|
|
19029
19282
|
if (hasCapability("scheduling")) {
|
|
19030
|
-
const { stopScheduler } = await import("./index-
|
|
19283
|
+
const { stopScheduler } = await import("./index-0gzmqj4k.js");
|
|
19031
19284
|
stopScheduler();
|
|
19032
19285
|
}
|
|
19033
19286
|
stopHeartbeat();
|
|
19034
19287
|
stopScriptRunSupervisor();
|
|
19035
19288
|
await stopSlackApp();
|
|
19036
19289
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
19037
|
-
const { stopOAuthKeepalive } = await import("./keepalive-
|
|
19290
|
+
const { stopOAuthKeepalive } = await import("./keepalive-62gfj91d.js");
|
|
19038
19291
|
await stopOAuthKeepalive();
|
|
19039
19292
|
}
|
|
19040
19293
|
stopMcpOAuthPendingGc();
|
|
19041
19294
|
stopMemoryGc();
|
|
19295
|
+
stopAuditGc();
|
|
19296
|
+
stopAuditWriter();
|
|
19297
|
+
flushAuditBuffer();
|
|
19298
|
+
clearAuditSink();
|
|
19042
19299
|
if (globalState.__apiGcInterval) {
|
|
19043
19300
|
clearInterval(globalState.__apiGcInterval);
|
|
19044
19301
|
delete globalState.__apiGcInterval;
|
|
@@ -19087,19 +19344,22 @@ try {
|
|
|
19087
19344
|
throw err;
|
|
19088
19345
|
}
|
|
19089
19346
|
try {
|
|
19090
|
-
const { seedPricingFromModelsDev } = await import("./seed-pricing-
|
|
19347
|
+
const { seedPricingFromModelsDev } = await import("./seed-pricing-1r1y1xkq.js");
|
|
19091
19348
|
seedPricingFromModelsDev();
|
|
19092
|
-
const { startPricingRefreshLoop } = await import("./pricing-refresh-
|
|
19349
|
+
const { startPricingRefreshLoop } = await import("./pricing-refresh-cgtkffpa.js");
|
|
19093
19350
|
startPricingRefreshLoop();
|
|
19094
19351
|
} catch (err) {
|
|
19095
19352
|
console.error("[startup] Failed to seed pricing rows:", err);
|
|
19096
19353
|
}
|
|
19097
19354
|
try {
|
|
19098
|
-
const { runAllSeeders } = await import("./index-
|
|
19355
|
+
const { runAllSeeders } = await import("./index-vfedgdw6.js");
|
|
19099
19356
|
await runAllSeeders({ scriptEmbeddingMode: "skip" });
|
|
19100
19357
|
} catch (err) {
|
|
19101
19358
|
console.error("[startup] Failed to seed built-in entities:", err);
|
|
19102
19359
|
}
|
|
19360
|
+
setAuditSink(enqueueAuditRow);
|
|
19361
|
+
startAuditWriter();
|
|
19362
|
+
startAuditGc();
|
|
19103
19363
|
initialize();
|
|
19104
19364
|
await initOtel("api");
|
|
19105
19365
|
httpServer.listen(port, async () => {
|
|
@@ -19128,31 +19388,31 @@ httpServer.listen(port, async () => {
|
|
|
19128
19388
|
initWorkflows();
|
|
19129
19389
|
startScriptRunSupervisor(getMcpBaseUrl());
|
|
19130
19390
|
if (hasCapability("scheduling")) {
|
|
19131
|
-
const { startScheduler } = await import("./index-
|
|
19132
|
-
const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-
|
|
19391
|
+
const { startScheduler } = await import("./index-0gzmqj4k.js");
|
|
19392
|
+
const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-5ghxn8s6.js");
|
|
19133
19393
|
const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
|
|
19134
19394
|
startScheduler(getExecutorRegistry2(), intervalMs, {
|
|
19135
19395
|
runId: globalState.__runId
|
|
19136
19396
|
});
|
|
19137
19397
|
}
|
|
19138
19398
|
if (process.env.HEARTBEAT_DISABLE !== "true") {
|
|
19139
|
-
const { startHeartbeat } = await import("./index-
|
|
19399
|
+
const { startHeartbeat } = await import("./index-nhat35em.js");
|
|
19140
19400
|
const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
|
|
19141
19401
|
startHeartbeat(heartbeatMs);
|
|
19142
19402
|
}
|
|
19143
19403
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
19144
|
-
const { startOAuthKeepalive } = await import("./keepalive-
|
|
19404
|
+
const { startOAuthKeepalive } = await import("./keepalive-62gfj91d.js");
|
|
19145
19405
|
startOAuthKeepalive();
|
|
19146
19406
|
}
|
|
19147
19407
|
startMcpOAuthPendingGc();
|
|
19148
19408
|
startMemoryGc();
|
|
19149
|
-
import("./boot-reembed-
|
|
19409
|
+
import("./boot-reembed-p0ny05t8.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
19150
19410
|
console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
|
|
19151
19411
|
});
|
|
19152
|
-
import("./boot-reembed-
|
|
19412
|
+
import("./boot-reembed-69agkhv6.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
19153
19413
|
console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
|
|
19154
19414
|
});
|
|
19155
|
-
import("./boot-scrub-logs-
|
|
19415
|
+
import("./boot-scrub-logs-yybrkmvr.js").then(({ runBootScrubLogs }) => runBootScrubLogs()).catch((err) => {
|
|
19156
19416
|
console.error("[boot-scrub-logs] startup scrub failed (non-fatal):", err);
|
|
19157
19417
|
});
|
|
19158
19418
|
}).on("error", (err) => {
|