@desplega.ai/agent-swarm 2.7.0 → 2.7.1
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/{acp-adapter-ma1js5n7.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-1vhmtvg9.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-sxqqvmdk.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-mwx1d5a9.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-yx1p6g82.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-7h78vmvv.js → boot-reembed-fxfw17m2.js} +10 -3
- package/dist/{boot-reembed-5h37a2c2.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-8mawd4gw.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-ca84dhnn.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-wqt4hfkq.js → claude-adapter-ahmyf231.js} +7 -7
- package/dist/{claude-managed-adapter-6fjqtr2z.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-rqv98sea.js → claude-managed-setup-mdc7dy6b.js} +2 -2
- package/dist/{claude-sdk-session-f29f3b2x.js → claude-sdk-session-n5wen9zk.js} +8728 -8599
- package/dist/{cli-m5e802r7.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-e1595bgx.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-2y7v133v.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-d20qvpr2.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-twctnkng.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-he9gbdx0.js → cli-5qt9vzmq.js} +39 -20
- package/dist/{cli-a6ds2epe.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-6srv653c.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-1hhbzkbb.js → cli-6w2yrrc0.js} +8 -8
- package/dist/{cli-f12mr20z.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-j3y0x2n4.js → cli-8feswf2v.js} +43 -33
- package/dist/{cli-07q03xm2.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ymjpkxgn.js → cli-9csfthbw.js} +8 -6
- package/dist/{cli-f1e0ptjj.js → cli-b41p764a.js} +21 -2
- package/dist/{cli-kv7sbc2c.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-54sj64fc.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-8mbc9za9.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-c668vvcf.js → cli-d8gaphtr.js} +1 -1
- package/dist/{cli-86ehn4s5.js → cli-dgtfysne.js} +2 -2
- package/dist/{cli-sjctjr48.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-j0ppywdr.js → cli-dv11pgpv.js} +12 -12
- package/dist/{cli-1m9x8dr5.js → cli-ekptgk2m.js} +5 -5
- package/dist/{cli-9gsm2rzp.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-s0mgx214.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-yd2nreqw.js → cli-hkxqdmnb.js} +2 -2
- package/dist/{cli-8wmj18cz.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-wqr7cea4.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sgqm2582.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-ay40dnj3.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-j9bpmykh.js → cli-kbtdftbw.js} +5 -5
- package/dist/{cli-x37r7qwf.js → cli-kj69b9tw.js} +25 -5
- package/dist/{cli-gsfk58nw.js → cli-kk9jy4nm.js} +1 -1
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-h5jeg46p.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-a89zdqz9.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-v6shg7na.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-6a9qfa8b.js → cli-pzafyqtk.js} +118 -48
- package/dist/{cli-48gxjmcz.js → cli-qvaa1gjp.js} +87 -24
- package/dist/{cli-qj442fws.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-aypd09nf.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-9pshx6rd.js → cli-s504c3w7.js} +1 -1
- package/dist/{cli-ptfq202x.js → cli-swkx9jm2.js} +3 -3
- package/dist/{cli-m30k9777.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-60sty27p.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-rd5m70gj.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-yxbsmx56.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-s09j9vp5.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-b3yzyc57.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-6mgdba42.js → cli-xbz6mcg4.js} +2 -2
- package/dist/{cli-2pvzmas1.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-f4dbh7a5.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +27 -19
- package/dist/{codex-adapter-zk59x2zh.js → codex-adapter-7wepzq15.js} +5 -5
- package/dist/{codex-hook-xyjhvxwd.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-kh6s4j3n.js → codex-session-runner-02sb49e4.js} +5 -5
- package/dist/{commands-68et1qv2.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-fyrbnzg4.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-nep9h2xv.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-yss3pg7t.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-28gjyvxj.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-8ftwvzba.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-hayexmkr.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-46wawff9.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-8d1e0h4m.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-cjrfxfnm.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-xbmxcg09.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-ag4wks2a.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-psfvcgwp.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cyq4mryn.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-8pz04697.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-haj1nd6k.js → http-q6j4k989.js} +934 -180
- package/dist/{index-z5h22pes.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-zyq8ct52.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-syvhnvtw.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-y1ajx04a.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-jwt2bj6f.js → index-ejqdkkqx.js} +11 -10
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-q52f47pe.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-xdvsamvj.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/{lead-hqg57kdp.js → lead-c0pjcaw4.js} +36 -36
- package/dist/{maintenance-4y2xzh3a.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-jd4r7zbs.js → oauth-refresh-sweep-7qt0g55z.js} +2 -2
- package/dist/{onboard-18447q9q.js → onboard-k7x13fqg.js} +118 -5
- package/dist/{opencode-adapter-26b2frdf.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-f9tgr8yw.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-qn6qbga1.js → pi-mono-adapter-m2qnafxm.js} +63 -9
- package/dist/{pricing-refresh-dw2tgb53.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-s4nwj2m3.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-aygm2760.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-yfvdfr0x.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-1bmvjbf3.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-f7tpzedy.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-r5garej6.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-424wzv0r.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-8tygvxkq.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-xsw323b3.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-ggnghpgf.js → work-objects-0y07495y.js} +20 -20
- package/dist/{worker-sqjd1nqf.js → worker-d4q9sqbc.js} +36 -36
- package/openapi.json +1070 -9
- package/package.json +3 -3
- package/src/be/connector-codes.ts +65 -22
- package/src/be/memory/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +110 -8
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/embeddings.ts +2 -2
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +112 -1
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/runner.ts +14 -1
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +4 -1
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/prompts/base-prompt.ts +24 -9
- package/src/prompts/session-templates.ts +26 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +58 -3
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/base-prompt.test.ts +69 -2
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/onboard-compose.test.ts +142 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/prompt-template-session.test.ts +19 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- package/src/tests/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +95 -2
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- package/src/tools/store-progress.ts +5 -1
- package/src/tools/utils.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/task-attachment-links.ts +15 -3
- package/templates/skills/swarm-docs/content.md +1 -1
- package/dist/handlers-tzmevqb5.js +0 -41
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
|
@@ -296,7 +296,7 @@ import {
|
|
|
296
296
|
hasCapability,
|
|
297
297
|
getEnabledCapabilities,
|
|
298
298
|
createServer
|
|
299
|
-
} from "./cli-
|
|
299
|
+
} from "./cli-pzafyqtk.js";
|
|
300
300
|
import {
|
|
301
301
|
chatThreadOfContextKey,
|
|
302
302
|
agentContextKey,
|
|
@@ -338,7 +338,9 @@ import {
|
|
|
338
338
|
init_asset_key_audit,
|
|
339
339
|
CredentialBindingSchema,
|
|
340
340
|
placeholderForConfigKey,
|
|
341
|
+
isPrivateAddress,
|
|
341
342
|
assertUrlSafe,
|
|
343
|
+
init_egress_guard,
|
|
342
344
|
init_credential_broker,
|
|
343
345
|
encryptSecret,
|
|
344
346
|
decryptSecret,
|
|
@@ -406,6 +408,7 @@ import {
|
|
|
406
408
|
getAllTasks2,
|
|
407
409
|
getTasksCount2,
|
|
408
410
|
getTaskStats2,
|
|
411
|
+
getInProgressTasksByContextKey2,
|
|
409
412
|
getLatestScriptRunStepTaskByContextKey2,
|
|
410
413
|
init_read,
|
|
411
414
|
MODEL_PROFILE_CONFIG_KEY,
|
|
@@ -523,6 +526,7 @@ import {
|
|
|
523
526
|
searchMemoryCandidates,
|
|
524
527
|
init_search_candidates,
|
|
525
528
|
getEmbeddingProvider2,
|
|
529
|
+
resetEmbeddingProvider2,
|
|
526
530
|
getMemoryStore2,
|
|
527
531
|
init_memory,
|
|
528
532
|
searchScripts,
|
|
@@ -765,7 +769,7 @@ import {
|
|
|
765
769
|
countScriptRunJournalSteps2,
|
|
766
770
|
countScriptRunJournalAgentTaskSteps2,
|
|
767
771
|
init_db3
|
|
768
|
-
} from "./cli-
|
|
772
|
+
} from "./cli-qvaa1gjp.js";
|
|
769
773
|
import {
|
|
770
774
|
PRIMARY_ROLE_NAMES2,
|
|
771
775
|
getUserGrant2,
|
|
@@ -776,22 +780,22 @@ import {
|
|
|
776
780
|
assertNotLastAdmin2,
|
|
777
781
|
setUserRoles2,
|
|
778
782
|
init_rbac_roles
|
|
779
|
-
} from "./cli-
|
|
780
|
-
import"./cli-
|
|
783
|
+
} from "./cli-hpkvgdad.js";
|
|
784
|
+
import"./cli-typdpe8g.js";
|
|
781
785
|
import {
|
|
782
786
|
normalizeModelKey
|
|
783
|
-
} from "./cli-
|
|
787
|
+
} from "./cli-2sjm3t4r.js";
|
|
784
788
|
import {
|
|
785
789
|
upsertModelCatalogOverlay,
|
|
786
790
|
deleteModelCatalogOverlay,
|
|
787
791
|
loadModelsCatalog,
|
|
788
792
|
requestModelCatalogRefresh2
|
|
789
|
-
} from "./cli-
|
|
793
|
+
} from "./cli-twjnyvs8.js";
|
|
790
794
|
import {
|
|
791
795
|
AMP_ESTIMATE_MODELS,
|
|
792
796
|
ampBillsCacheWrites,
|
|
793
797
|
resolveAmpModel
|
|
794
|
-
} from "./cli-
|
|
798
|
+
} from "./cli-cb50g9cd.js";
|
|
795
799
|
import {
|
|
796
800
|
workflowEventBus2,
|
|
797
801
|
init_event_bus
|
|
@@ -831,7 +835,7 @@ import {
|
|
|
831
835
|
generateEdges,
|
|
832
836
|
definitionNodeIds,
|
|
833
837
|
validateDefinition
|
|
834
|
-
} from "./cli-
|
|
838
|
+
} from "./cli-dv11pgpv.js";
|
|
835
839
|
import {
|
|
836
840
|
require_dist2,
|
|
837
841
|
isInvalidClientError,
|
|
@@ -913,14 +917,14 @@ import {
|
|
|
913
917
|
typecheckWithAmbient,
|
|
914
918
|
typecheckScript,
|
|
915
919
|
parseSkillContent
|
|
916
|
-
} from "./cli-
|
|
920
|
+
} from "./cli-kbtdftbw.js";
|
|
917
921
|
import {
|
|
918
922
|
buildAuthorizationUrl,
|
|
919
923
|
exchangeAuthorizationCode,
|
|
920
924
|
oauthAppRowToProviderConfig,
|
|
921
925
|
forceRefreshAuthorizationOrThrow,
|
|
922
926
|
forceRefreshTokenOrThrow
|
|
923
|
-
} from "./cli-
|
|
927
|
+
} from "./cli-rm66vqdz.js";
|
|
924
928
|
import {
|
|
925
929
|
signalProcessGroup,
|
|
926
930
|
detachedProcessGroup,
|
|
@@ -953,9 +957,9 @@ import {
|
|
|
953
957
|
getSlackApp2,
|
|
954
958
|
startSlackApp2,
|
|
955
959
|
stopSlackApp2
|
|
956
|
-
} from "./cli-
|
|
957
|
-
import"./cli-
|
|
958
|
-
import"./cli-
|
|
960
|
+
} from "./cli-thsaq4pd.js";
|
|
961
|
+
import"./cli-vhpckzvp.js";
|
|
962
|
+
import"./cli-ekptgk2m.js";
|
|
959
963
|
import {
|
|
960
964
|
_fetchHolder,
|
|
961
965
|
CLIENT_ID,
|
|
@@ -972,7 +976,7 @@ import {
|
|
|
972
976
|
import {
|
|
973
977
|
retryFailedRun2,
|
|
974
978
|
cancelWorkflowRun2
|
|
975
|
-
} from "./cli-
|
|
979
|
+
} from "./cli-3v2cyqb8.js";
|
|
976
980
|
import {
|
|
977
981
|
duplicateGuardedQueryKey,
|
|
978
982
|
warnIfCorsAllowsAnyOrigin,
|
|
@@ -982,6 +986,7 @@ import {
|
|
|
982
986
|
parseQueryParams,
|
|
983
987
|
getPathSegments,
|
|
984
988
|
wireHttpSpanLifecycle,
|
|
989
|
+
redactShareTokenInPath,
|
|
985
990
|
safeRequestUrlForLog,
|
|
986
991
|
agentWithCapacity,
|
|
987
992
|
RequestBodyTooLargeError,
|
|
@@ -1011,13 +1016,15 @@ import {
|
|
|
1011
1016
|
readableByFor2,
|
|
1012
1017
|
SESSION_WRITE_DENIED2,
|
|
1013
1018
|
hasSessionWrite2,
|
|
1019
|
+
NOT_YOUR_TASK_DENIED2,
|
|
1020
|
+
deniesNonOwnerTaskWrite2,
|
|
1014
1021
|
denyParentAttach2,
|
|
1015
1022
|
runtimeInstanceHeader,
|
|
1016
1023
|
bodyTask,
|
|
1017
1024
|
bodyListTask,
|
|
1018
1025
|
describeRequestRoute,
|
|
1019
1026
|
route
|
|
1020
|
-
} from "./cli-
|
|
1027
|
+
} from "./cli-hq2v4rr5.js";
|
|
1021
1028
|
import {
|
|
1022
1029
|
FilesError,
|
|
1023
1030
|
normalizeFilesError,
|
|
@@ -1045,7 +1052,7 @@ import {
|
|
|
1045
1052
|
resolvePoolDelivery2,
|
|
1046
1053
|
createPoolAgent2,
|
|
1047
1054
|
init_db
|
|
1048
|
-
} from "./cli-
|
|
1055
|
+
} from "./cli-6vwjsetx.js";
|
|
1049
1056
|
import {
|
|
1050
1057
|
RATING_MODEL_MAX_LENGTH,
|
|
1051
1058
|
REFERENCES_SOURCE_MAX_LENGTH,
|
|
@@ -1055,14 +1062,17 @@ import"./cli-737pyqxr.js";
|
|
|
1055
1062
|
import {
|
|
1056
1063
|
startPoolRouting,
|
|
1057
1064
|
stopPoolRouting,
|
|
1065
|
+
INVITE_CODE_TTL_MS,
|
|
1058
1066
|
DEFAULT_CONNECTOR_CODE_LABEL,
|
|
1067
|
+
DEFAULT_INVITE_CODE_LABEL,
|
|
1059
1068
|
createConnectorCode,
|
|
1060
1069
|
exchangeConnectorCode,
|
|
1070
|
+
deleteUnusedInviteCodes,
|
|
1061
1071
|
checkHeartbeatChecklist2,
|
|
1062
1072
|
runHeartbeatSweep2,
|
|
1063
1073
|
stopHeartbeat2
|
|
1064
|
-
} from "./cli-
|
|
1065
|
-
import"./cli-
|
|
1074
|
+
} from "./cli-5qt9vzmq.js";
|
|
1075
|
+
import"./cli-rg4b2g6j.js";
|
|
1066
1076
|
import {
|
|
1067
1077
|
import_websocket,
|
|
1068
1078
|
import_websocket_server
|
|
@@ -1071,7 +1081,7 @@ import {
|
|
|
1071
1081
|
runtimeSupervisorStatus,
|
|
1072
1082
|
startRuntimeSupervisor,
|
|
1073
1083
|
stopRuntimeSupervisor
|
|
1074
|
-
} from "./cli-
|
|
1084
|
+
} from "./cli-f3mzy662.js";
|
|
1075
1085
|
import"./cli-ns59dc6r.js";
|
|
1076
1086
|
import {
|
|
1077
1087
|
ACP_TARGET_IDS
|
|
@@ -1092,14 +1102,14 @@ import {
|
|
|
1092
1102
|
isUnknownModelError,
|
|
1093
1103
|
checkProviderPoolKey,
|
|
1094
1104
|
writeSkillsToFilesystem
|
|
1095
|
-
} from "./cli-
|
|
1105
|
+
} from "./cli-xbz6mcg4.js";
|
|
1096
1106
|
import {
|
|
1097
1107
|
inviteEmailToSharedOrg,
|
|
1098
1108
|
ensureAgentFsCredentialsForAgent
|
|
1099
|
-
} from "./cli-
|
|
1109
|
+
} from "./cli-x3cngb1h.js";
|
|
1100
1110
|
import {
|
|
1101
1111
|
Anthropic
|
|
1102
|
-
} from "./cli-
|
|
1112
|
+
} from "./cli-prcyzecg.js";
|
|
1103
1113
|
import {
|
|
1104
1114
|
isCodexOAuthConfigKey
|
|
1105
1115
|
} from "./cli-j4bwnpkm.js";
|
|
@@ -1116,6 +1126,7 @@ import {
|
|
|
1116
1126
|
registerSessionTokenConnection,
|
|
1117
1127
|
init_session_token_connections,
|
|
1118
1128
|
findUserById,
|
|
1129
|
+
findUserByEmail,
|
|
1119
1130
|
getUserIdentities,
|
|
1120
1131
|
listUserEvents,
|
|
1121
1132
|
listUserTokens,
|
|
@@ -1136,7 +1147,7 @@ import {
|
|
|
1136
1147
|
revokeAgentTokens,
|
|
1137
1148
|
fingerprintApiKey,
|
|
1138
1149
|
init_users
|
|
1139
|
-
} from "./cli-
|
|
1150
|
+
} from "./cli-nf6k60sn.js";
|
|
1140
1151
|
import"./cli-v41kn3cd.js";
|
|
1141
1152
|
import"./cli-wgtk7xxr.js";
|
|
1142
1153
|
import"./cli-1f0gp9vz.js";
|
|
@@ -1151,7 +1162,7 @@ import {
|
|
|
1151
1162
|
clearAuditSink,
|
|
1152
1163
|
can,
|
|
1153
1164
|
init_rbac
|
|
1154
|
-
} from "./cli-
|
|
1165
|
+
} from "./cli-k4awgvzm.js";
|
|
1155
1166
|
import {
|
|
1156
1167
|
ASSET_KEY_MAX_LENGTH,
|
|
1157
1168
|
normalizeAssetKey,
|
|
@@ -1302,12 +1313,12 @@ import {
|
|
|
1302
1313
|
KvKeySchema,
|
|
1303
1314
|
KvEntrySchema,
|
|
1304
1315
|
init_types
|
|
1305
|
-
} from "./cli-
|
|
1316
|
+
} from "./cli-s504c3w7.js";
|
|
1306
1317
|
import {
|
|
1307
1318
|
IDENTITY_FIELD_BUDGETS,
|
|
1308
1319
|
IdentityFieldBudgetError,
|
|
1309
1320
|
init_identity_field_budget
|
|
1310
|
-
} from "./cli-
|
|
1321
|
+
} from "./cli-kj69b9tw.js";
|
|
1311
1322
|
import {
|
|
1312
1323
|
isSteeringEnabled2,
|
|
1313
1324
|
init_steering_enabled
|
|
@@ -1344,7 +1355,7 @@ import {
|
|
|
1344
1355
|
resolveTemplate,
|
|
1345
1356
|
init_resolver,
|
|
1346
1357
|
init_session_templates
|
|
1347
|
-
} from "./cli-
|
|
1358
|
+
} from "./cli-b41p764a.js";
|
|
1348
1359
|
import {
|
|
1349
1360
|
init_zod
|
|
1350
1361
|
} from "./cli-4chz1hye.js";
|
|
@@ -1379,25 +1390,28 @@ import {
|
|
|
1379
1390
|
import {
|
|
1380
1391
|
init_package,
|
|
1381
1392
|
package_default
|
|
1382
|
-
} from "./cli-
|
|
1383
|
-
import {
|
|
1384
|
-
realtimeBus,
|
|
1385
|
-
init_bus
|
|
1386
|
-
} from "./cli-syfdvhkc.js";
|
|
1393
|
+
} from "./cli-swkx9jm2.js";
|
|
1387
1394
|
import {
|
|
1388
1395
|
MAX_PROFILE_FILE_LENGTH,
|
|
1396
|
+
DEFAULT_APP_URL,
|
|
1389
1397
|
getConfiguredAppUrls,
|
|
1390
1398
|
getAppUrl,
|
|
1391
1399
|
getMcpBaseUrl,
|
|
1392
1400
|
getPublicMcpBaseUrl,
|
|
1393
1401
|
agentFsFileRoute,
|
|
1402
|
+
isCombEnabled,
|
|
1403
|
+
isAgentFsProxyEnabled,
|
|
1394
1404
|
init_constants2
|
|
1395
|
-
} from "./cli-
|
|
1405
|
+
} from "./cli-3cywb44q.js";
|
|
1396
1406
|
import {
|
|
1397
1407
|
parseEnvFlag,
|
|
1398
1408
|
isEnvFlagEnabled,
|
|
1399
1409
|
init_env_flag
|
|
1400
1410
|
} from "./cli-cebvbc9t.js";
|
|
1411
|
+
import {
|
|
1412
|
+
realtimeBus,
|
|
1413
|
+
init_bus
|
|
1414
|
+
} from "./cli-syfdvhkc.js";
|
|
1401
1415
|
import {
|
|
1402
1416
|
getApiKey,
|
|
1403
1417
|
init_api_key
|
|
@@ -4283,6 +4297,353 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
|
|
|
4283
4297
|
return false;
|
|
4284
4298
|
}
|
|
4285
4299
|
|
|
4300
|
+
// src/http/agentfs-proxy.ts
|
|
4301
|
+
init_api_key();
|
|
4302
|
+
init_constants2();
|
|
4303
|
+
import { Readable } from "node:stream";
|
|
4304
|
+
|
|
4305
|
+
// src/http/ip-rate-limit.ts
|
|
4306
|
+
var PRUNE_THRESHOLD = 1e4;
|
|
4307
|
+
var PRUNE_INTERVAL_MS = 60000;
|
|
4308
|
+
var MAX_BUCKETS = 50000;
|
|
4309
|
+
function isPrivateAddress2(address) {
|
|
4310
|
+
const ip = address.startsWith("::ffff:") ? address.slice(7) : address;
|
|
4311
|
+
if (ip === "::1" || ip.startsWith("127.") || ip.startsWith("10."))
|
|
4312
|
+
return true;
|
|
4313
|
+
if (ip.startsWith("192.168.") || ip.startsWith("169.254."))
|
|
4314
|
+
return true;
|
|
4315
|
+
const match = /^172\.(\d+)\./.exec(ip);
|
|
4316
|
+
if (match && Number(match[1]) >= 16 && Number(match[1]) <= 31)
|
|
4317
|
+
return true;
|
|
4318
|
+
const lower = ip.toLowerCase();
|
|
4319
|
+
return lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
|
|
4320
|
+
}
|
|
4321
|
+
function clientIp(req) {
|
|
4322
|
+
const peer = req.socket.remoteAddress ?? "unknown";
|
|
4323
|
+
if (!isPrivateAddress2(peer))
|
|
4324
|
+
return peer;
|
|
4325
|
+
const raw = req.headers["x-forwarded-for"];
|
|
4326
|
+
const header = Array.isArray(raw) ? raw[raw.length - 1] : raw;
|
|
4327
|
+
const hop = header?.split(",").pop()?.trim();
|
|
4328
|
+
return hop || peer;
|
|
4329
|
+
}
|
|
4330
|
+
function createIpRateLimiter(options) {
|
|
4331
|
+
const buckets = new Map;
|
|
4332
|
+
let lastPruneAt = 0;
|
|
4333
|
+
function refill(bucket, now) {
|
|
4334
|
+
const elapsed = now - bucket.updatedAt;
|
|
4335
|
+
bucket.tokens = Math.min(options.capacity, bucket.tokens + elapsed * options.refillPerMs);
|
|
4336
|
+
bucket.updatedAt = now;
|
|
4337
|
+
}
|
|
4338
|
+
return {
|
|
4339
|
+
take(ip, now = Date.now()) {
|
|
4340
|
+
if (buckets.size > PRUNE_THRESHOLD && now - lastPruneAt >= PRUNE_INTERVAL_MS) {
|
|
4341
|
+
lastPruneAt = now;
|
|
4342
|
+
for (const [key, bucket] of buckets) {
|
|
4343
|
+
refill(bucket, now);
|
|
4344
|
+
if (bucket.tokens >= options.capacity)
|
|
4345
|
+
buckets.delete(key);
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
let bucket = buckets.get(ip);
|
|
4349
|
+
if (!bucket) {
|
|
4350
|
+
if (buckets.size >= MAX_BUCKETS) {
|
|
4351
|
+
const oldest = buckets.keys().next().value;
|
|
4352
|
+
if (oldest !== undefined)
|
|
4353
|
+
buckets.delete(oldest);
|
|
4354
|
+
}
|
|
4355
|
+
bucket = { tokens: options.capacity, updatedAt: now };
|
|
4356
|
+
buckets.set(ip, bucket);
|
|
4357
|
+
}
|
|
4358
|
+
refill(bucket, now);
|
|
4359
|
+
if (bucket.tokens < 1)
|
|
4360
|
+
return false;
|
|
4361
|
+
bucket.tokens -= 1;
|
|
4362
|
+
return true;
|
|
4363
|
+
},
|
|
4364
|
+
reset() {
|
|
4365
|
+
buckets.clear();
|
|
4366
|
+
lastPruneAt = 0;
|
|
4367
|
+
}
|
|
4368
|
+
};
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
// src/http/agentfs-proxy.ts
|
|
4372
|
+
var API_PREFIX = "/agentfs";
|
|
4373
|
+
var PUBLIC_PREFIXES = ["/share/", "/site/"];
|
|
4374
|
+
function upstreamBase() {
|
|
4375
|
+
const v = process.env.AGENT_FS_API_URL?.trim().replace(/\/+$/, "");
|
|
4376
|
+
return v || null;
|
|
4377
|
+
}
|
|
4378
|
+
var REQUEST_HEADER_ALLOWLIST = [
|
|
4379
|
+
"content-type",
|
|
4380
|
+
"content-length",
|
|
4381
|
+
"accept",
|
|
4382
|
+
"if-match",
|
|
4383
|
+
"if-none-match",
|
|
4384
|
+
"range",
|
|
4385
|
+
"x-agent-fs-message",
|
|
4386
|
+
"x-agent-fs-message-encoding"
|
|
4387
|
+
];
|
|
4388
|
+
var AGENT_FS_BEARER = /^Bearer\s+af_\S+$/i;
|
|
4389
|
+
var RESPONSE_HEADER_ALLOWLIST = new Set([
|
|
4390
|
+
"content-type",
|
|
4391
|
+
"content-length",
|
|
4392
|
+
"content-disposition",
|
|
4393
|
+
"content-range",
|
|
4394
|
+
"accept-ranges",
|
|
4395
|
+
"etag",
|
|
4396
|
+
"last-modified",
|
|
4397
|
+
"cache-control",
|
|
4398
|
+
"location",
|
|
4399
|
+
"retry-after",
|
|
4400
|
+
"content-security-policy",
|
|
4401
|
+
"x-content-type-options",
|
|
4402
|
+
"x-frame-options",
|
|
4403
|
+
"referrer-policy",
|
|
4404
|
+
"x-robots-tag",
|
|
4405
|
+
"cross-origin-resource-policy"
|
|
4406
|
+
]);
|
|
4407
|
+
var EXPOSED_HEADERS = ["X-Agent-FS-Version", "X-Agent-FS-Content-Hash", "Content-Disposition"];
|
|
4408
|
+
var UNGATED = "agent-fs enforces its own af_ auth; register needs a swarm credential; swarm credentials are never forwarded";
|
|
4409
|
+
var OK_200 = {
|
|
4410
|
+
description: "Upstream agent-fs response, streamed verbatim",
|
|
4411
|
+
unstructured: "Body is streamed from agent-fs; shape and content-type vary per upstream route"
|
|
4412
|
+
};
|
|
4413
|
+
for (const method of ["get", "post", "put", "delete", "patch"]) {
|
|
4414
|
+
route({
|
|
4415
|
+
method,
|
|
4416
|
+
path: "/agentfs/{path}",
|
|
4417
|
+
pattern: ["agentfs", null],
|
|
4418
|
+
exact: false,
|
|
4419
|
+
summary: "Streaming pass-through proxy to agent-fs (enabled by AGENT_FS_PROXY)",
|
|
4420
|
+
tags: ["Comb"],
|
|
4421
|
+
responses: {
|
|
4422
|
+
200: OK_200,
|
|
4423
|
+
401: {
|
|
4424
|
+
description: "Returned by agent-fs when credentials are missing or invalid, or by the swarm when POST /agentfs/auth/register has no swarm credential"
|
|
4425
|
+
},
|
|
4426
|
+
403: {
|
|
4427
|
+
description: "POST /agentfs/auth/register with a user token for an email that is not the user's"
|
|
4428
|
+
},
|
|
4429
|
+
413: { description: "POST /agentfs/auth/register body over 16 KB" },
|
|
4430
|
+
429: { description: "Too many registrations from this IP (POST /agentfs/auth/register)" },
|
|
4431
|
+
502: { description: "agent-fs is unreachable" }
|
|
4432
|
+
},
|
|
4433
|
+
auth: { apiKey: false },
|
|
4434
|
+
...method === "get" ? {} : { rbac: { ungated: UNGATED } }
|
|
4435
|
+
});
|
|
4436
|
+
}
|
|
4437
|
+
for (const prefix of ["share", "site"]) {
|
|
4438
|
+
route({
|
|
4439
|
+
method: "get",
|
|
4440
|
+
path: `/${prefix}/{path}`,
|
|
4441
|
+
pattern: [prefix, null],
|
|
4442
|
+
exact: false,
|
|
4443
|
+
summary: `Public agent-fs ${prefix} page, served through the API (enabled by AGENT_FS_PROXY)`,
|
|
4444
|
+
tags: ["Comb"],
|
|
4445
|
+
responses: {
|
|
4446
|
+
200: {
|
|
4447
|
+
description: `agent-fs /${prefix} response, streamed verbatim`,
|
|
4448
|
+
unstructured: "HTML page or raw file streamed from agent-fs; content-type varies"
|
|
4449
|
+
},
|
|
4450
|
+
404: { description: "Unknown or expired link (from agent-fs), or the proxy is off" },
|
|
4451
|
+
502: { description: "agent-fs is unreachable" }
|
|
4452
|
+
},
|
|
4453
|
+
auth: { apiKey: false }
|
|
4454
|
+
});
|
|
4455
|
+
}
|
|
4456
|
+
var registerRateLimiter = createIpRateLimiter({ capacity: 5, refillPerMs: 5 / 60000 });
|
|
4457
|
+
var REGISTER_BODY_MAX_BYTES = 16 * 1024;
|
|
4458
|
+
async function checkRegister(req, res) {
|
|
4459
|
+
const auth = await resolveHttpRequestAuth(req, getApiKey());
|
|
4460
|
+
if (!auth || auth.page || auth.kind !== "operator" && auth.kind !== "user") {
|
|
4461
|
+
jsonError(res, "Creating an agent-fs account here needs a swarm API key or user token", 401);
|
|
4462
|
+
return null;
|
|
4463
|
+
}
|
|
4464
|
+
let body;
|
|
4465
|
+
try {
|
|
4466
|
+
body = await parseBody(req, REGISTER_BODY_MAX_BYTES);
|
|
4467
|
+
} catch (err) {
|
|
4468
|
+
if (err instanceof RequestBodyTooLargeError)
|
|
4469
|
+
jsonError(res, "Request body too large", 413);
|
|
4470
|
+
else
|
|
4471
|
+
jsonError(res, "Invalid JSON body", 400);
|
|
4472
|
+
return null;
|
|
4473
|
+
}
|
|
4474
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
4475
|
+
jsonError(res, "Invalid JSON body", 400);
|
|
4476
|
+
return null;
|
|
4477
|
+
}
|
|
4478
|
+
if (auth.kind === "user") {
|
|
4479
|
+
const email = body.email;
|
|
4480
|
+
const own = [auth.user.email, ...auth.user.emailAliases].filter((e) => typeof e === "string").map((e) => e.trim().toLowerCase());
|
|
4481
|
+
if (typeof email !== "string" || !own.includes(email.trim().toLowerCase())) {
|
|
4482
|
+
jsonError(res, "You can create an agent-fs account only for your own email", 403);
|
|
4483
|
+
return null;
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
return JSON.stringify(body);
|
|
4487
|
+
}
|
|
4488
|
+
function routedPath(pathOnly) {
|
|
4489
|
+
let decoded = pathOnly;
|
|
4490
|
+
try {
|
|
4491
|
+
decoded = decodeURIComponent(pathOnly);
|
|
4492
|
+
} catch {}
|
|
4493
|
+
return decoded.replace(/\/{2,}/g, "/").replace(/\/+$/, "");
|
|
4494
|
+
}
|
|
4495
|
+
function isNonCanonical(pathOnly) {
|
|
4496
|
+
let decoded = pathOnly;
|
|
4497
|
+
try {
|
|
4498
|
+
decoded = decodeURIComponent(pathOnly);
|
|
4499
|
+
} catch {}
|
|
4500
|
+
for (const p of [pathOnly, decoded]) {
|
|
4501
|
+
if (p.includes("\\"))
|
|
4502
|
+
return true;
|
|
4503
|
+
if (p.split("/").some((s) => s === "." || s === ".."))
|
|
4504
|
+
return true;
|
|
4505
|
+
}
|
|
4506
|
+
return pathOnly.split("/").some((s) => /^(\.|%2e){1,2}$/i.test(s));
|
|
4507
|
+
}
|
|
4508
|
+
async function handleAgentFsProxy(req, res) {
|
|
4509
|
+
const url = req.url ?? "";
|
|
4510
|
+
const isApi = url.startsWith(`${API_PREFIX}/`);
|
|
4511
|
+
const isPublicPage = !isApi && PUBLIC_PREFIXES.some((p) => url.startsWith(p));
|
|
4512
|
+
if (!isApi && !isPublicPage)
|
|
4513
|
+
return false;
|
|
4514
|
+
const base = upstreamBase();
|
|
4515
|
+
if (!base || !isAgentFsProxyEnabled())
|
|
4516
|
+
return false;
|
|
4517
|
+
const method = req.method ?? "GET";
|
|
4518
|
+
const allowed = isApi ? ["GET", "POST", "PUT", "DELETE", "PATCH"] : ["GET"];
|
|
4519
|
+
if (!allowed.includes(method))
|
|
4520
|
+
return false;
|
|
4521
|
+
const upstreamPath = isApi ? url.slice(API_PREFIX.length) : url;
|
|
4522
|
+
const queryStart = upstreamPath.indexOf("?");
|
|
4523
|
+
const pathOnly = queryStart === -1 ? upstreamPath : upstreamPath.slice(0, queryStart);
|
|
4524
|
+
if (isNonCanonical(pathOnly)) {
|
|
4525
|
+
jsonError(res, "Invalid path", 400);
|
|
4526
|
+
return true;
|
|
4527
|
+
}
|
|
4528
|
+
const upstreamUrl = `${base}${upstreamPath}`;
|
|
4529
|
+
let sentPath;
|
|
4530
|
+
try {
|
|
4531
|
+
const basePath = new URL(base).pathname.replace(/\/+$/, "");
|
|
4532
|
+
sentPath = new URL(upstreamUrl).pathname.slice(basePath.length);
|
|
4533
|
+
} catch {
|
|
4534
|
+
jsonError(res, "Invalid path", 400);
|
|
4535
|
+
return true;
|
|
4536
|
+
}
|
|
4537
|
+
const isRegister = isApi && method === "POST" && routedPath(sentPath) === "/auth/register";
|
|
4538
|
+
if (isRegister && !registerRateLimiter.take(clientIp(req))) {
|
|
4539
|
+
res.setHeader("Retry-After", "12");
|
|
4540
|
+
jsonError(res, "rate_limited", 429);
|
|
4541
|
+
return true;
|
|
4542
|
+
}
|
|
4543
|
+
const registerBody = isRegister ? await checkRegister(req, res) : null;
|
|
4544
|
+
if (isRegister && registerBody === null)
|
|
4545
|
+
return true;
|
|
4546
|
+
const headers = new Headers;
|
|
4547
|
+
const authorization = req.headers.authorization?.trim();
|
|
4548
|
+
if (registerBody === null && authorization && AGENT_FS_BEARER.test(authorization) && authorization.replace(/^bearer\s+/i, "") !== getApiKey()) {
|
|
4549
|
+
headers.set("authorization", authorization);
|
|
4550
|
+
}
|
|
4551
|
+
for (const name of REQUEST_HEADER_ALLOWLIST) {
|
|
4552
|
+
const v = req.headers[name];
|
|
4553
|
+
if (typeof v === "string")
|
|
4554
|
+
headers.set(name, v);
|
|
4555
|
+
}
|
|
4556
|
+
if (registerBody !== null) {
|
|
4557
|
+
headers.delete("content-length");
|
|
4558
|
+
headers.set("content-type", "application/json");
|
|
4559
|
+
}
|
|
4560
|
+
const ac = new AbortController;
|
|
4561
|
+
res.on("close", () => {
|
|
4562
|
+
if (!res.writableEnded)
|
|
4563
|
+
ac.abort();
|
|
4564
|
+
});
|
|
4565
|
+
const hasBody = method !== "GET" && method !== "DELETE";
|
|
4566
|
+
let upstream;
|
|
4567
|
+
try {
|
|
4568
|
+
const init = {
|
|
4569
|
+
method,
|
|
4570
|
+
headers,
|
|
4571
|
+
redirect: "manual",
|
|
4572
|
+
signal: ac.signal
|
|
4573
|
+
};
|
|
4574
|
+
if (registerBody !== null) {
|
|
4575
|
+
init.body = registerBody;
|
|
4576
|
+
} else if (hasBody) {
|
|
4577
|
+
init.body = Readable.toWeb(req);
|
|
4578
|
+
init.duplex = "half";
|
|
4579
|
+
}
|
|
4580
|
+
upstream = await fetch(upstreamUrl, init);
|
|
4581
|
+
} catch {
|
|
4582
|
+
if (!res.headersSent && !res.destroyed)
|
|
4583
|
+
jsonError(res, "agent-fs upstream unavailable", 502);
|
|
4584
|
+
else
|
|
4585
|
+
res.destroy();
|
|
4586
|
+
return true;
|
|
4587
|
+
}
|
|
4588
|
+
if (isPublicPage) {
|
|
4589
|
+
for (const name of res.getHeaderNames()) {
|
|
4590
|
+
if (name.startsWith("access-control-"))
|
|
4591
|
+
res.removeHeader(name);
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
const encoded = upstream.headers.has("content-encoding");
|
|
4595
|
+
upstream.headers.forEach((value, name) => {
|
|
4596
|
+
const n = name.toLowerCase();
|
|
4597
|
+
const keep = RESPONSE_HEADER_ALLOWLIST.has(n) || n.startsWith("x-agent-fs-") || isPublicPage && n.startsWith("access-control-");
|
|
4598
|
+
if (!keep)
|
|
4599
|
+
return;
|
|
4600
|
+
if (encoded && n === "content-length")
|
|
4601
|
+
return;
|
|
4602
|
+
res.setHeader(name, value);
|
|
4603
|
+
});
|
|
4604
|
+
if (isApi) {
|
|
4605
|
+
const expose = res.getHeader("Access-Control-Expose-Headers");
|
|
4606
|
+
if (typeof expose === "string" && expose !== "*") {
|
|
4607
|
+
res.setHeader("Access-Control-Expose-Headers", `${expose}, ${EXPOSED_HEADERS.join(", ")}`);
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
res.writeHead(upstream.status);
|
|
4611
|
+
if (!upstream.body) {
|
|
4612
|
+
res.end();
|
|
4613
|
+
return true;
|
|
4614
|
+
}
|
|
4615
|
+
await pump(upstream.body, res);
|
|
4616
|
+
return true;
|
|
4617
|
+
}
|
|
4618
|
+
async function pump(body, res) {
|
|
4619
|
+
const reader = body.getReader();
|
|
4620
|
+
const closed = new Promise((resolve) => {
|
|
4621
|
+
if (res.destroyed)
|
|
4622
|
+
resolve();
|
|
4623
|
+
else
|
|
4624
|
+
res.once("close", () => resolve());
|
|
4625
|
+
});
|
|
4626
|
+
try {
|
|
4627
|
+
for (;; ) {
|
|
4628
|
+
if (res.destroyed)
|
|
4629
|
+
break;
|
|
4630
|
+
const { done, value } = await reader.read();
|
|
4631
|
+
if (done) {
|
|
4632
|
+
res.end();
|
|
4633
|
+
break;
|
|
4634
|
+
}
|
|
4635
|
+
if (!res.write(value)) {
|
|
4636
|
+
await Promise.race([new Promise((resolve) => res.once("drain", resolve)), closed]);
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
} catch {
|
|
4640
|
+
res.destroy();
|
|
4641
|
+
} finally {
|
|
4642
|
+
reader.cancel().catch(() => {});
|
|
4643
|
+
}
|
|
4644
|
+
await closed;
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4286
4647
|
// src/http/agents.ts
|
|
4287
4648
|
init_src2();
|
|
4288
4649
|
init_zod();
|
|
@@ -8383,6 +8744,7 @@ var OnboardingStepIdSchema = _enum2([
|
|
|
8383
8744
|
"agents",
|
|
8384
8745
|
"memory",
|
|
8385
8746
|
"integrations",
|
|
8747
|
+
"files",
|
|
8386
8748
|
"first_task"
|
|
8387
8749
|
]);
|
|
8388
8750
|
var OnboardingErrorClassSchema = _enum2([
|
|
@@ -8418,6 +8780,14 @@ var OnboardingMemoryPresetSchema = _enum2([
|
|
|
8418
8780
|
"custom",
|
|
8419
8781
|
"existing"
|
|
8420
8782
|
]);
|
|
8783
|
+
var OnboardingMemoryProviderSchema = _enum2([
|
|
8784
|
+
"local",
|
|
8785
|
+
"openai",
|
|
8786
|
+
"openrouter",
|
|
8787
|
+
"vercel",
|
|
8788
|
+
"azure",
|
|
8789
|
+
"custom"
|
|
8790
|
+
]);
|
|
8421
8791
|
var OnboardingIntegrationMethodSchema = _enum2([
|
|
8422
8792
|
"slack",
|
|
8423
8793
|
"github",
|
|
@@ -8426,6 +8796,7 @@ var OnboardingIntegrationMethodSchema = _enum2([
|
|
|
8426
8796
|
"linear_oauth",
|
|
8427
8797
|
"jira_oauth"
|
|
8428
8798
|
]);
|
|
8799
|
+
var OnboardingFilesMethodSchema = _enum2(["created", "connected"]);
|
|
8429
8800
|
var OnboardingFirstTaskMethodSchema = _enum2(["suggestion", "free_form"]);
|
|
8430
8801
|
var OnboardingStepMethodSchema = union2([
|
|
8431
8802
|
OnboardingConnectMethodSchema,
|
|
@@ -8434,6 +8805,7 @@ var OnboardingStepMethodSchema = union2([
|
|
|
8434
8805
|
OnboardingAgentsMethodSchema,
|
|
8435
8806
|
OnboardingMemoryPresetSchema,
|
|
8436
8807
|
OnboardingIntegrationMethodSchema,
|
|
8808
|
+
OnboardingFilesMethodSchema,
|
|
8437
8809
|
OnboardingFirstTaskMethodSchema
|
|
8438
8810
|
]);
|
|
8439
8811
|
function onboardingStepStateSchema(method) {
|
|
@@ -8455,6 +8827,7 @@ var OnboardingAiStepStateSchema = onboardingStepStateSchema(OnboardingAiMethodSc
|
|
|
8455
8827
|
var OnboardingAgentsStepStateSchema = onboardingStepStateSchema(OnboardingAgentsMethodSchema);
|
|
8456
8828
|
var OnboardingMemoryStepStateSchema = onboardingStepStateSchema(OnboardingMemoryPresetSchema);
|
|
8457
8829
|
var OnboardingIntegrationStepStateSchema = onboardingStepStateSchema(OnboardingIntegrationMethodSchema);
|
|
8830
|
+
var OnboardingFilesStepStateSchema = onboardingStepStateSchema(OnboardingFilesMethodSchema);
|
|
8458
8831
|
var OnboardingFirstTaskStepStateSchema = onboardingStepStateSchema(OnboardingFirstTaskMethodSchema);
|
|
8459
8832
|
var OnboardingStateSchema = object2({
|
|
8460
8833
|
version: literal2(1),
|
|
@@ -8472,6 +8845,7 @@ var OnboardingStateSchema = object2({
|
|
|
8472
8845
|
agents: OnboardingAgentsStepStateSchema,
|
|
8473
8846
|
memory: OnboardingMemoryStepStateSchema,
|
|
8474
8847
|
integrations: OnboardingIntegrationStepStateSchema,
|
|
8848
|
+
files: OnboardingFilesStepStateSchema,
|
|
8475
8849
|
first_task: OnboardingFirstTaskStepStateSchema
|
|
8476
8850
|
})
|
|
8477
8851
|
});
|
|
@@ -8486,7 +8860,11 @@ var OnboardingSignalsSchema = object2({
|
|
|
8486
8860
|
})),
|
|
8487
8861
|
embeddings: object2({
|
|
8488
8862
|
configured: boolean(),
|
|
8489
|
-
dimensions: number2().int().positive()
|
|
8863
|
+
dimensions: number2().int().positive(),
|
|
8864
|
+
model: string2().nullable(),
|
|
8865
|
+
local: boolean(),
|
|
8866
|
+
provider: OnboardingMemoryProviderSchema.nullable(),
|
|
8867
|
+
localDefault: object2({ model: string2() }).nullable()
|
|
8490
8868
|
}),
|
|
8491
8869
|
integrations: object2({
|
|
8492
8870
|
slack: boolean(),
|
|
@@ -8500,7 +8878,8 @@ var OnboardingSignalsSchema = object2({
|
|
|
8500
8878
|
leadsOnline: number2().int().nonnegative(),
|
|
8501
8879
|
workersOnline: number2().int().nonnegative()
|
|
8502
8880
|
}),
|
|
8503
|
-
firstTask: object2({ id: string2(), status: string2() }).nullable()
|
|
8881
|
+
firstTask: object2({ id: string2(), status: string2() }).nullable(),
|
|
8882
|
+
files: object2({ enabled: boolean() })
|
|
8504
8883
|
});
|
|
8505
8884
|
var OnboardingResponseSchema = object2({
|
|
8506
8885
|
state: OnboardingStateSchema,
|
|
@@ -8533,9 +8912,14 @@ var OnboardingActionSchema = union2([
|
|
|
8533
8912
|
step: literal2("integrations"),
|
|
8534
8913
|
method: OnboardingIntegrationMethodSchema
|
|
8535
8914
|
}).strict(),
|
|
8915
|
+
object2({
|
|
8916
|
+
action: literal2("complete"),
|
|
8917
|
+
step: literal2("files"),
|
|
8918
|
+
method: OnboardingFilesMethodSchema
|
|
8919
|
+
}).strict(),
|
|
8536
8920
|
object2({
|
|
8537
8921
|
action: literal2("skip"),
|
|
8538
|
-
step: _enum2(["name", "ai", "agents", "memory", "integrations", "first_task"])
|
|
8922
|
+
step: _enum2(["name", "ai", "agents", "memory", "integrations", "files", "first_task"])
|
|
8539
8923
|
}).strict(),
|
|
8540
8924
|
object2({
|
|
8541
8925
|
action: literal2("fail"),
|
|
@@ -8564,8 +8948,12 @@ var OnboardingMemoryRequestSchema = object2({
|
|
|
8564
8948
|
baseUrl: OnboardingHttpUrlSchema.optional(),
|
|
8565
8949
|
model: string2().trim().min(1).optional(),
|
|
8566
8950
|
apiKey: string2().min(1).optional(),
|
|
8567
|
-
reuseKey: _enum2(["OPENAI_API_KEY", "OPENROUTER_API_KEY"]).optional()
|
|
8568
|
-
|
|
8951
|
+
reuseKey: _enum2(["OPENAI_API_KEY", "OPENROUTER_API_KEY"]).optional(),
|
|
8952
|
+
restoreDefault: boolean().optional()
|
|
8953
|
+
}).strict().refine((body) => !body.restoreDefault || body.preset === "existing", {
|
|
8954
|
+
message: "restoreDefault needs preset existing",
|
|
8955
|
+
path: ["restoreDefault"]
|
|
8956
|
+
});
|
|
8569
8957
|
var OnboardingMemoryResponseSchema = object2({
|
|
8570
8958
|
ok: boolean(),
|
|
8571
8959
|
dimensions: number2().int().positive().optional(),
|
|
@@ -8593,6 +8981,7 @@ function freshState(now, autoCompleted) {
|
|
|
8593
8981
|
agents: emptyStep(),
|
|
8594
8982
|
memory: emptyStep(),
|
|
8595
8983
|
integrations: emptyStep(),
|
|
8984
|
+
files: emptyStep(),
|
|
8596
8985
|
first_task: emptyStep()
|
|
8597
8986
|
}
|
|
8598
8987
|
};
|
|
@@ -8751,6 +9140,14 @@ function deriveState(state, signals, funnelEvents) {
|
|
|
8751
9140
|
}
|
|
8752
9141
|
return changed;
|
|
8753
9142
|
}
|
|
9143
|
+
function presentState(state, signals) {
|
|
9144
|
+
if (signals.files.enabled || state.steps.files.status !== "todo")
|
|
9145
|
+
return state;
|
|
9146
|
+
return {
|
|
9147
|
+
...state,
|
|
9148
|
+
steps: { ...state.steps, files: { ...state.steps.files, status: "skipped" } }
|
|
9149
|
+
};
|
|
9150
|
+
}
|
|
8754
9151
|
|
|
8755
9152
|
class OnboardingTaskNotFoundError extends Error {
|
|
8756
9153
|
}
|
|
@@ -8820,7 +9217,7 @@ async function readOrUpdateOnboarding(loadSignals, action) {
|
|
|
8820
9217
|
readOnly: true
|
|
8821
9218
|
});
|
|
8822
9219
|
if (!read.dirty)
|
|
8823
|
-
return { state: read.state, signals: read.signals };
|
|
9220
|
+
return { state: presentState(read.state, read.signals), signals: read.signals };
|
|
8824
9221
|
}
|
|
8825
9222
|
return await client.transaction(async () => {
|
|
8826
9223
|
const events = [];
|
|
@@ -8835,20 +9232,30 @@ async function readOrUpdateOnboarding(loadSignals, action) {
|
|
|
8835
9232
|
if (dirty)
|
|
8836
9233
|
await persistState(state);
|
|
8837
9234
|
queueTelemetry(events);
|
|
8838
|
-
return { state, signals };
|
|
9235
|
+
return { state: presentState(state, signals), signals };
|
|
8839
9236
|
});
|
|
8840
9237
|
}
|
|
8841
|
-
|
|
9238
|
+
var EMBEDDING_PREFIX_KEYS = [
|
|
9239
|
+
"EMBEDDING_QUERY_PREFIX",
|
|
9240
|
+
"EMBEDDING_DOCUMENT_PREFIX"
|
|
9241
|
+
];
|
|
9242
|
+
var EMBEDDING_CONFIG_KEYS = [
|
|
9243
|
+
"EMBEDDING_API_BASE_URL",
|
|
9244
|
+
"EMBEDDING_MODEL",
|
|
9245
|
+
"EMBEDDING_API_KEY",
|
|
9246
|
+
...EMBEDDING_PREFIX_KEYS
|
|
9247
|
+
];
|
|
9248
|
+
async function updateOnboardingMemory(preset, result, change) {
|
|
8842
9249
|
return await getDbClient2().transaction(async () => {
|
|
8843
9250
|
const events = [];
|
|
8844
9251
|
const loaded = await loadState(events);
|
|
8845
9252
|
let dirty = loaded.dirty;
|
|
8846
9253
|
const eventsForState = loaded.state.autoCompleted ? [] : events;
|
|
8847
|
-
if (
|
|
9254
|
+
if (change?.kind === "save") {
|
|
8848
9255
|
const configValues = [
|
|
8849
|
-
["EMBEDDING_API_BASE_URL",
|
|
8850
|
-
["EMBEDDING_MODEL",
|
|
8851
|
-
...
|
|
9256
|
+
["EMBEDDING_API_BASE_URL", change.baseUrl],
|
|
9257
|
+
["EMBEDDING_MODEL", change.model],
|
|
9258
|
+
...change.apiKey ? [["EMBEDDING_API_KEY", change.apiKey]] : []
|
|
8852
9259
|
];
|
|
8853
9260
|
for (const [key, value] of configValues) {
|
|
8854
9261
|
const validationError = validateConfigValue(key, value);
|
|
@@ -8858,17 +9265,26 @@ async function updateOnboardingMemory(preset, result, saveConfig) {
|
|
|
8858
9265
|
await upsertSwarmConfig2({
|
|
8859
9266
|
scope: "global",
|
|
8860
9267
|
key: "EMBEDDING_API_BASE_URL",
|
|
8861
|
-
value:
|
|
9268
|
+
value: change.baseUrl
|
|
8862
9269
|
});
|
|
8863
|
-
await upsertSwarmConfig2({ scope: "global", key: "EMBEDDING_MODEL", value:
|
|
8864
|
-
if (
|
|
9270
|
+
await upsertSwarmConfig2({ scope: "global", key: "EMBEDDING_MODEL", value: change.model });
|
|
9271
|
+
if (change.apiKey) {
|
|
8865
9272
|
await upsertSwarmConfig2({
|
|
8866
9273
|
scope: "global",
|
|
8867
9274
|
key: "EMBEDDING_API_KEY",
|
|
8868
|
-
value:
|
|
9275
|
+
value: change.apiKey,
|
|
8869
9276
|
isSecret: true
|
|
8870
9277
|
});
|
|
8871
9278
|
}
|
|
9279
|
+
for (const { key, value } of change.prefixes) {
|
|
9280
|
+
if (value === null)
|
|
9281
|
+
await deleteSwarmConfigByKey2("global", null, key);
|
|
9282
|
+
else
|
|
9283
|
+
await upsertSwarmConfig2({ scope: "global", key, value });
|
|
9284
|
+
}
|
|
9285
|
+
} else if (change?.kind === "restore") {
|
|
9286
|
+
for (const key of EMBEDDING_CONFIG_KEYS)
|
|
9287
|
+
await deleteSwarmConfigByKey2("global", null, key);
|
|
8872
9288
|
}
|
|
8873
9289
|
if (result.ok) {
|
|
8874
9290
|
dirty = completeStep(loaded.state, "memory", preset, false, eventsForState) || dirty;
|
|
@@ -8878,7 +9294,7 @@ async function updateOnboardingMemory(preset, result, saveConfig) {
|
|
|
8878
9294
|
if (dirty)
|
|
8879
9295
|
await persistState(loaded.state);
|
|
8880
9296
|
queueTelemetry(events);
|
|
8881
|
-
return
|
|
9297
|
+
return change !== undefined;
|
|
8882
9298
|
});
|
|
8883
9299
|
}
|
|
8884
9300
|
async function updateOnboardingAiFromCodexDevice(result) {
|
|
@@ -9152,6 +9568,9 @@ async function completeFlow(flowId, credentials) {
|
|
|
9152
9568
|
isSecret: true,
|
|
9153
9569
|
description: `Codex ChatGPT OAuth credentials slot ${slot} (stored by dashboard device login)`
|
|
9154
9570
|
});
|
|
9571
|
+
const keySuffix = deriveCodexKeySuffix(credentials.access, credentials.accountId);
|
|
9572
|
+
const checked = [{ keyIndex: slot, keySuffix, ok: true }];
|
|
9573
|
+
await recordKeyValidation2("CODEX_OAUTH", checked, "global", null, { retireAbsent: false });
|
|
9155
9574
|
await updateOnboardingAiFromCodexDevice({ status: "complete" });
|
|
9156
9575
|
state.status = "complete";
|
|
9157
9576
|
state.slot = slot;
|
|
@@ -14040,6 +14459,7 @@ ${block}`, new Date().toISOString(), taskId]);
|
|
|
14040
14459
|
}
|
|
14041
14460
|
|
|
14042
14461
|
// src/http/fs.ts
|
|
14462
|
+
init_rbac_roles();
|
|
14043
14463
|
init_rbac();
|
|
14044
14464
|
init_types();
|
|
14045
14465
|
|
|
@@ -14085,7 +14505,11 @@ var agentFsCredentialStateSchema = object2({
|
|
|
14085
14505
|
});
|
|
14086
14506
|
var agentFsInviteStateSchema = object2({
|
|
14087
14507
|
orgId: string2(),
|
|
14088
|
-
invited: boolean()
|
|
14508
|
+
invited: boolean().describe("False only when the email already holds the requested role."),
|
|
14509
|
+
status: _enum2(["already_member", "added", "pending"]).describe("already_member: no change. added: an existing agent-fs account joined the org. pending: the email has no account, so agent-fs created an invite the person must accept."),
|
|
14510
|
+
inviteId: string2().optional().describe("Pending invite id (status pending)."),
|
|
14511
|
+
expiresAt: string2().optional().describe("ISO time the accept link expires (status pending)."),
|
|
14512
|
+
acceptUrl: string2().optional().describe("Link the invitee opens to accept (status pending). agent-fs sends no email, so hand it to the person. Absent when agent-fs has no app URL configured.")
|
|
14089
14513
|
});
|
|
14090
14514
|
var signedUrlResponseSchema = object2({
|
|
14091
14515
|
url: string2(),
|
|
@@ -14125,21 +14549,23 @@ var inviteMemberRoute = route({
|
|
|
14125
14549
|
path: "/api/fs/members/invite",
|
|
14126
14550
|
pattern: ["api", "fs", "members", "invite"],
|
|
14127
14551
|
summary: "Invite an external member into the agent-fs shared org",
|
|
14128
|
-
description: "The API server performs the invite with its own bootstrap credentials (which are API-only and never served over HTTP), provisioning the shared org/drive first when needed. Intended for the cloud control plane's Connect-to-Drive flow. No keys are returned; the invitee obtains their own key via agent-fs registration.",
|
|
14552
|
+
description: "The API server performs the invite with its own bootstrap credentials (which are API-only and never served over HTTP), provisioning the shared org/drive first when needed. Intended for the cloud control plane's Connect-to-Drive flow. An email with an agent-fs account is added directly (status added). An email without one gets a pending invite (status pending) with an acceptUrl when agent-fs returns one; agent-fs sends no email, so the caller must hand the link to the invitee. No keys are returned; the invitee obtains their own key via agent-fs registration.",
|
|
14129
14553
|
tags: ["FS"],
|
|
14130
14554
|
body: object2({
|
|
14131
14555
|
email: email(),
|
|
14132
14556
|
role: _enum2(["viewer", "editor", "admin"]).default("editor")
|
|
14133
14557
|
}),
|
|
14134
14558
|
responses: {
|
|
14135
|
-
200: {
|
|
14559
|
+
200: {
|
|
14560
|
+
description: "Invite state ({ orgId, invited, status, acceptUrl? })",
|
|
14561
|
+
schema: agentFsInviteStateSchema
|
|
14562
|
+
},
|
|
14136
14563
|
400: { description: "Invalid body" },
|
|
14564
|
+
403: { description: "A non-admin user token named an email other than its own" },
|
|
14137
14565
|
500: { description: "Provisioning or invite failed" }
|
|
14138
14566
|
},
|
|
14139
14567
|
auth: { apiKey: true },
|
|
14140
|
-
rbac: {
|
|
14141
|
-
ungated: "Tenant API key is the org-owner credential; the invite runs under the API server's own bootstrap identity, no per-agent principal applies."
|
|
14142
|
-
}
|
|
14568
|
+
rbac: { permission: "fs.join.own" }
|
|
14143
14569
|
});
|
|
14144
14570
|
var listTaskFilesRoute = route({
|
|
14145
14571
|
method: "get",
|
|
@@ -14238,6 +14664,27 @@ var deleteTaskFileRoute = route({
|
|
|
14238
14664
|
auth: { apiKey: true, agentId: true },
|
|
14239
14665
|
rbac: { permission: "task.fs.mutate" }
|
|
14240
14666
|
});
|
|
14667
|
+
async function resolveInvite(req, body) {
|
|
14668
|
+
const auth = getRequestAuth(req);
|
|
14669
|
+
if (auth?.kind !== "user" || !isRbacEnabled())
|
|
14670
|
+
return body;
|
|
14671
|
+
const grant = await getUserGrant2(auth.userId);
|
|
14672
|
+
if (grant.grantsAll)
|
|
14673
|
+
return body;
|
|
14674
|
+
const decision = can({
|
|
14675
|
+
principal: { kind: "user", userId: auth.userId },
|
|
14676
|
+
verb: "fs.join.own",
|
|
14677
|
+
resource: { kind: "none" },
|
|
14678
|
+
source: "http"
|
|
14679
|
+
});
|
|
14680
|
+
if (!decision.allow)
|
|
14681
|
+
return { denied: decision.reason };
|
|
14682
|
+
const ownEmail = auth.user.email?.trim();
|
|
14683
|
+
if (!ownEmail || ownEmail.toLowerCase() !== body.email.trim().toLowerCase()) {
|
|
14684
|
+
return { denied: "You can only join the shared drive with your own account email" };
|
|
14685
|
+
}
|
|
14686
|
+
return { email: ownEmail, role: grant.verbs.has("task.fs.mutate") ? "editor" : "viewer" };
|
|
14687
|
+
}
|
|
14241
14688
|
async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
14242
14689
|
if (ensureAgentCredentialsRoute.match(req.method, pathSegments)) {
|
|
14243
14690
|
const parsed = await ensureAgentCredentialsRoute.parse(req, res, pathSegments, queryParams);
|
|
@@ -14262,8 +14709,13 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
14262
14709
|
const parsed = await inviteMemberRoute.parse(req, res, pathSegments, queryParams);
|
|
14263
14710
|
if (!parsed)
|
|
14264
14711
|
return true;
|
|
14712
|
+
const invite = await resolveInvite(req, parsed.body);
|
|
14713
|
+
if ("denied" in invite) {
|
|
14714
|
+
jsonError(res, invite.denied, 403);
|
|
14715
|
+
return true;
|
|
14716
|
+
}
|
|
14265
14717
|
try {
|
|
14266
|
-
const result = await inviteEmailToSharedOrg(
|
|
14718
|
+
const result = await inviteEmailToSharedOrg(invite.email, invite.role);
|
|
14267
14719
|
inviteMemberRoute.respond(res, 200, result);
|
|
14268
14720
|
} catch (error) {
|
|
14269
14721
|
const message = scrubSecrets(error instanceof Error ? error.message : String(error));
|
|
@@ -14600,7 +15052,10 @@ async function handleHeartbeat(req, res, pathSegments) {
|
|
|
14600
15052
|
// src/http/inbox-state.ts
|
|
14601
15053
|
init_zod();
|
|
14602
15054
|
init_db3();
|
|
15055
|
+
init_rbac_roles();
|
|
15056
|
+
init_rbac();
|
|
14603
15057
|
init_types();
|
|
15058
|
+
init_request_auth_context();
|
|
14604
15059
|
var listState = route({
|
|
14605
15060
|
method: "get",
|
|
14606
15061
|
path: "/api/inbox-state",
|
|
@@ -14643,9 +15098,11 @@ var upsertState = route({
|
|
|
14643
15098
|
schema: object2({ item: InboxItemStateSchema })
|
|
14644
15099
|
},
|
|
14645
15100
|
400: { description: "Validation error" },
|
|
14646
|
-
401: { description: "Unauthorized" }
|
|
15101
|
+
401: { description: "Unauthorized" },
|
|
15102
|
+
403: { description: "A non-admin user token wrote another user's state" }
|
|
14647
15103
|
},
|
|
14648
|
-
auth: { apiKey: true }
|
|
15104
|
+
auth: { apiKey: true },
|
|
15105
|
+
rbac: { permission: "favorite.write.own" }
|
|
14649
15106
|
});
|
|
14650
15107
|
var bulkUpsertState = route({
|
|
14651
15108
|
method: "patch",
|
|
@@ -14663,11 +15120,19 @@ var bulkUpsertState = route({
|
|
|
14663
15120
|
schema: object2({ items: array2(InboxItemStateSchema) })
|
|
14664
15121
|
},
|
|
14665
15122
|
400: { description: "Validation error" },
|
|
14666
|
-
401: { description: "Unauthorized" }
|
|
15123
|
+
401: { description: "Unauthorized" },
|
|
15124
|
+
403: { description: "A non-admin user token wrote another user's state" }
|
|
14667
15125
|
},
|
|
14668
15126
|
auth: { apiKey: true },
|
|
14669
|
-
rbac: {
|
|
15127
|
+
rbac: { permission: "favorite.write.own" }
|
|
14670
15128
|
});
|
|
15129
|
+
var OTHER_USER_STATE_DENIED = "You can only change your own inbox state";
|
|
15130
|
+
async function writesOtherUsersState(req, userId) {
|
|
15131
|
+
const auth = getRequestAuth(req);
|
|
15132
|
+
if (auth?.kind !== "user" || auth.userId === userId || !isRbacEnabled())
|
|
15133
|
+
return false;
|
|
15134
|
+
return !(await getUserGrant2(auth.userId)).grantsAll;
|
|
15135
|
+
}
|
|
14671
15136
|
async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
14672
15137
|
if (listState.match(req.method, pathSegments)) {
|
|
14673
15138
|
const parsed = await listState.parse(req, res, pathSegments, queryParams);
|
|
@@ -14685,6 +15150,10 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
|
14685
15150
|
const parsed = await bulkUpsertState.parse(req, res, pathSegments, queryParams);
|
|
14686
15151
|
if (!parsed)
|
|
14687
15152
|
return true;
|
|
15153
|
+
if (await writesOtherUsersState(req, parsed.body.userId)) {
|
|
15154
|
+
jsonError(res, OTHER_USER_STATE_DENIED, 403);
|
|
15155
|
+
return true;
|
|
15156
|
+
}
|
|
14688
15157
|
try {
|
|
14689
15158
|
const items = await getDbClient2().transaction(async () => {
|
|
14690
15159
|
const rows = [];
|
|
@@ -14703,6 +15172,10 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
|
14703
15172
|
const parsed = await upsertState.parse(req, res, pathSegments, queryParams);
|
|
14704
15173
|
if (!parsed)
|
|
14705
15174
|
return true;
|
|
15175
|
+
if (await writesOtherUsersState(req, parsed.body.userId)) {
|
|
15176
|
+
jsonError(res, OTHER_USER_STATE_DENIED, 403);
|
|
15177
|
+
return true;
|
|
15178
|
+
}
|
|
14706
15179
|
try {
|
|
14707
15180
|
const item = await upsertInboxState2({
|
|
14708
15181
|
userId: parsed.body.userId,
|
|
@@ -15824,7 +16297,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
15824
16297
|
// node_modules/@hono/node-server/dist/index.mjs
|
|
15825
16298
|
import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
|
|
15826
16299
|
import { Http2ServerRequest } from "http2";
|
|
15827
|
-
import { Readable } from "stream";
|
|
16300
|
+
import { Readable as Readable2 } from "stream";
|
|
15828
16301
|
import crypto2 from "crypto";
|
|
15829
16302
|
var RequestError = class extends Error {
|
|
15830
16303
|
constructor(message, options) {
|
|
@@ -15891,7 +16364,7 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
|
|
|
15891
16364
|
init.body = new ReadableStream({
|
|
15892
16365
|
async pull(controller) {
|
|
15893
16366
|
try {
|
|
15894
|
-
reader ||=
|
|
16367
|
+
reader ||= Readable2.toWeb(incoming).getReader();
|
|
15895
16368
|
const { done, value } = await reader.read();
|
|
15896
16369
|
if (done) {
|
|
15897
16370
|
controller.close();
|
|
@@ -15904,7 +16377,7 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
|
|
|
15904
16377
|
}
|
|
15905
16378
|
});
|
|
15906
16379
|
} else {
|
|
15907
|
-
init.body =
|
|
16380
|
+
init.body = Readable2.toWeb(incoming);
|
|
15908
16381
|
}
|
|
15909
16382
|
}
|
|
15910
16383
|
return new Request2(url, init);
|
|
@@ -19804,7 +20277,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
19804
20277
|
try {
|
|
19805
20278
|
const provider = getEmbeddingProvider2();
|
|
19806
20279
|
const store = getMemoryStore2();
|
|
19807
|
-
const queryEmbedding = await provider.embed(query);
|
|
20280
|
+
const queryEmbedding = await provider.embed(query, { kind: "query" });
|
|
19808
20281
|
const candidateLimit = Math.min(limit, 20) * CANDIDATE_SET_MULTIPLIER;
|
|
19809
20282
|
const candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), myAgentId, {
|
|
19810
20283
|
scope,
|
|
@@ -19885,7 +20358,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
19885
20358
|
try {
|
|
19886
20359
|
if (query && query.trim().length > 0) {
|
|
19887
20360
|
const provider = getEmbeddingProvider2();
|
|
19888
|
-
const queryEmbedding = await provider.embed(query.trim());
|
|
20361
|
+
const queryEmbedding = await provider.embed(query.trim(), { kind: "query" });
|
|
19889
20362
|
const candidateLimit = Math.min(4096, Math.max(offset + pageLimit, pageLimit) * CANDIDATE_SET_MULTIPLIER);
|
|
19890
20363
|
let candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), viewer.agentId, {
|
|
19891
20364
|
scope,
|
|
@@ -20100,7 +20573,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
20100
20573
|
});
|
|
20101
20574
|
if (result.changed) {
|
|
20102
20575
|
const provider = getEmbeddingProvider2();
|
|
20103
|
-
const embedding = await provider.embed(result.memory.content);
|
|
20576
|
+
const embedding = await provider.embed(result.memory.content, { kind: "document" });
|
|
20104
20577
|
if (embedding)
|
|
20105
20578
|
await store.updateEmbedding(result.memory.id, embedding, provider.name);
|
|
20106
20579
|
try {
|
|
@@ -20178,7 +20651,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
20178
20651
|
for (let i = 0;i < memories.length; i += batchSize) {
|
|
20179
20652
|
const batch = memories.slice(i, i + batchSize);
|
|
20180
20653
|
try {
|
|
20181
|
-
const embeddings = await provider.embedBatch(batch.map((m) => m.content));
|
|
20654
|
+
const embeddings = await provider.embedBatch(batch.map((m) => m.content), { kind: "document" });
|
|
20182
20655
|
for (let j = 0;j < embeddings.length; j++) {
|
|
20183
20656
|
if (embeddings[j]) {
|
|
20184
20657
|
await store.updateEmbedding(batch[j].id, embeddings[j], provider.name);
|
|
@@ -21909,7 +22382,9 @@ init_db3();
|
|
|
21909
22382
|
init_memory();
|
|
21910
22383
|
init_constants();
|
|
21911
22384
|
init_types();
|
|
22385
|
+
init_constants2();
|
|
21912
22386
|
init_credentials();
|
|
22387
|
+
init_egress_guard();
|
|
21913
22388
|
init_openrouter_base_url();
|
|
21914
22389
|
|
|
21915
22390
|
// src/http/status.ts
|
|
@@ -22501,6 +22976,58 @@ async function handleStatus(req, res, pathSegments, queryParams) {
|
|
|
22501
22976
|
}
|
|
22502
22977
|
|
|
22503
22978
|
// src/http/onboarding.ts
|
|
22979
|
+
function isLocalEndpoint(baseUrl) {
|
|
22980
|
+
if (!baseUrl?.trim())
|
|
22981
|
+
return false;
|
|
22982
|
+
let host;
|
|
22983
|
+
try {
|
|
22984
|
+
host = new URL(baseUrl.trim()).hostname.toLowerCase().replace(/\.$/, "");
|
|
22985
|
+
} catch {
|
|
22986
|
+
return false;
|
|
22987
|
+
}
|
|
22988
|
+
if (!host)
|
|
22989
|
+
return false;
|
|
22990
|
+
if (host === "localhost" || host.endsWith(".localhost"))
|
|
22991
|
+
return true;
|
|
22992
|
+
if (isPrivateAddress(host))
|
|
22993
|
+
return true;
|
|
22994
|
+
return !host.includes(".") && !host.includes(":");
|
|
22995
|
+
}
|
|
22996
|
+
var PRESET_HOSTS = [
|
|
22997
|
+
["openai", (host) => host === "api.openai.com"],
|
|
22998
|
+
["openrouter", (host) => host === "openrouter.ai"],
|
|
22999
|
+
["vercel", (host) => host === "ai-gateway.vercel.sh"],
|
|
23000
|
+
[
|
|
23001
|
+
"azure",
|
|
23002
|
+
(host) => [".services.ai.azure.com", ".openai.azure.com", ".cognitiveservices.azure.com"].some((suffix) => host.endsWith(suffix))
|
|
23003
|
+
]
|
|
23004
|
+
];
|
|
23005
|
+
function embeddingsProvider(baseUrl) {
|
|
23006
|
+
if (isLocalEndpoint(baseUrl))
|
|
23007
|
+
return "local";
|
|
23008
|
+
if (!baseUrl?.trim())
|
|
23009
|
+
return "openai";
|
|
23010
|
+
let host;
|
|
23011
|
+
try {
|
|
23012
|
+
host = new URL(baseUrl.trim()).hostname.toLowerCase().replace(/\.$/, "");
|
|
23013
|
+
} catch {
|
|
23014
|
+
return "custom";
|
|
23015
|
+
}
|
|
23016
|
+
return PRESET_HOSTS.find(([, matches]) => matches(host))?.[0] ?? "custom";
|
|
23017
|
+
}
|
|
23018
|
+
function deploymentEmbeddings() {
|
|
23019
|
+
return {
|
|
23020
|
+
baseUrl: getDeploymentEnvValue("EMBEDDING_API_BASE_URL"),
|
|
23021
|
+
model: getDeploymentEnvValue("EMBEDDING_MODEL") ?? "text-embedding-3-small",
|
|
23022
|
+
apiKey: getDeploymentEnvValue("EMBEDDING_API_KEY") ?? process.env.OPENAI_API_KEY
|
|
23023
|
+
};
|
|
23024
|
+
}
|
|
23025
|
+
function localDefault() {
|
|
23026
|
+
const deployment = deploymentEmbeddings();
|
|
23027
|
+
if (!isLocalEndpoint(deployment.baseUrl) || !deployment.apiKey)
|
|
23028
|
+
return null;
|
|
23029
|
+
return { model: deployment.model };
|
|
23030
|
+
}
|
|
22504
23031
|
async function buildSignals(state) {
|
|
22505
23032
|
const providers = [];
|
|
22506
23033
|
for (const entry of await getAgentHarnessProviders2()) {
|
|
@@ -22521,11 +23048,16 @@ async function buildSignals(state) {
|
|
|
22521
23048
|
const integrations = await getAutomationSetupStates();
|
|
22522
23049
|
const agentCounts = await getReadyAgentCounts2();
|
|
22523
23050
|
const firstTask = state.firstTaskId ? await getTaskById2(state.firstTaskId) : null;
|
|
23051
|
+
const embeddingsOn = getEmbeddingProvider2().isConfigured();
|
|
22524
23052
|
return {
|
|
22525
23053
|
providers,
|
|
22526
23054
|
embeddings: {
|
|
22527
|
-
configured:
|
|
22528
|
-
dimensions: EMBEDDING_DIMENSIONS
|
|
23055
|
+
configured: embeddingsOn,
|
|
23056
|
+
dimensions: EMBEDDING_DIMENSIONS,
|
|
23057
|
+
model: embeddingsOn ? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small" : null,
|
|
23058
|
+
local: embeddingsOn && isLocalEndpoint(process.env.EMBEDDING_API_BASE_URL),
|
|
23059
|
+
provider: embeddingsOn ? embeddingsProvider(process.env.EMBEDDING_API_BASE_URL) : null,
|
|
23060
|
+
localDefault: localDefault()
|
|
22529
23061
|
},
|
|
22530
23062
|
integrations: {
|
|
22531
23063
|
slack: integrations.slack === "configured",
|
|
@@ -22539,7 +23071,8 @@ async function buildSignals(state) {
|
|
|
22539
23071
|
leadsOnline: agentCounts.leads,
|
|
22540
23072
|
workersOnline: agentCounts.workers
|
|
22541
23073
|
},
|
|
22542
|
-
firstTask: firstTask ? { id: firstTask.id, status: firstTask.status } : null
|
|
23074
|
+
firstTask: firstTask ? { id: firstTask.id, status: firstTask.status } : null,
|
|
23075
|
+
files: { enabled: isCombEnabled() }
|
|
22543
23076
|
};
|
|
22544
23077
|
}
|
|
22545
23078
|
var getOnboarding = route({
|
|
@@ -22636,19 +23169,34 @@ function memoryErrorMessage(errorClass, status) {
|
|
|
22636
23169
|
return `The endpoint returned an error${statusSuffix}.`;
|
|
22637
23170
|
}
|
|
22638
23171
|
}
|
|
22639
|
-
async function updateMemoryState(preset, result,
|
|
22640
|
-
const
|
|
22641
|
-
if (
|
|
22642
|
-
|
|
23172
|
+
async function updateMemoryState(preset, result, change) {
|
|
23173
|
+
const configChanged = await updateOnboardingMemory(preset, result, change);
|
|
23174
|
+
if (!configChanged)
|
|
23175
|
+
return;
|
|
23176
|
+
if (change?.kind === "restore") {
|
|
23177
|
+
await loadGlobalConfigsIntoEnv(true);
|
|
23178
|
+
resetEmbeddingProvider2();
|
|
23179
|
+
}
|
|
23180
|
+
scheduleIntegrationsReload();
|
|
23181
|
+
}
|
|
23182
|
+
function prefixChanges(model) {
|
|
23183
|
+
const sameModel = model === getDeploymentEnvValue("EMBEDDING_MODEL");
|
|
23184
|
+
return EMBEDDING_PREFIX_KEYS.filter((key) => getDeploymentEnvValue(key)).map((key) => ({
|
|
23185
|
+
key,
|
|
23186
|
+
value: sameModel ? null : ""
|
|
23187
|
+
}));
|
|
22643
23188
|
}
|
|
22644
23189
|
async function handleMemoryProbe(body) {
|
|
22645
23190
|
const explicitKey = body.apiKey;
|
|
22646
23191
|
const preset = body.preset === "openai" || body.preset === "openrouter" || body.preset === "vercel" ? MEMORY_PRESETS[body.preset] : undefined;
|
|
22647
|
-
const
|
|
22648
|
-
const
|
|
23192
|
+
const restore = body.preset === "existing" && body.restoreDefault === true;
|
|
23193
|
+
const deployment = restore ? deploymentEmbeddings() : null;
|
|
23194
|
+
const baseUrl = body.preset === "existing" ? deployment ? deployment.baseUrl : process.env.EMBEDDING_API_BASE_URL : body.baseUrl ?? preset?.baseUrl ?? process.env.EMBEDDING_API_BASE_URL ?? "https://api.openai.com/v1";
|
|
23195
|
+
const model = body.preset === "existing" ? deployment?.model ?? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small" : body.model ?? preset?.model ?? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small";
|
|
23196
|
+
const configuredEndpoint = body.preset === "existing" && !!baseUrl && baseUrl === getDeploymentEnvValue("EMBEDDING_API_BASE_URL");
|
|
22649
23197
|
let endpoint;
|
|
22650
23198
|
try {
|
|
22651
|
-
endpoint = assertUrlSafe(baseUrl ?? "", publicEndpointSsrfOptions());
|
|
23199
|
+
endpoint = configuredEndpoint ? new URL(baseUrl ?? "") : assertUrlSafe(baseUrl ?? "", publicEndpointSsrfOptions());
|
|
22652
23200
|
} catch {
|
|
22653
23201
|
return {
|
|
22654
23202
|
ok: false,
|
|
@@ -22660,7 +23208,7 @@ async function handleMemoryProbe(body) {
|
|
|
22660
23208
|
const reuseHost = body.reuseKey === "OPENAI_API_KEY" ? "api.openai.com" : body.reuseKey === "OPENROUTER_API_KEY" ? "openrouter.ai" : undefined;
|
|
22661
23209
|
const canReuseKey = reuseHost === undefined || endpoint.hostname === reuseHost;
|
|
22662
23210
|
const reusedKey = !explicitKey && body.reuseKey && canReuseKey ? process.env[body.reuseKey] : undefined;
|
|
22663
|
-
const implicitKey = body.preset === "existing" ? process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY : undefined;
|
|
23211
|
+
const implicitKey = body.preset === "existing" ? deployment ? deployment.apiKey : process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY : undefined;
|
|
22664
23212
|
const apiKey = splitCredentialPool(explicitKey ?? reusedKey ?? implicitKey ?? "")[0];
|
|
22665
23213
|
if (!apiKey) {
|
|
22666
23214
|
return {
|
|
@@ -22698,10 +23246,12 @@ async function handleMemoryProbe(body) {
|
|
|
22698
23246
|
};
|
|
22699
23247
|
}
|
|
22700
23248
|
const keyToSave = explicitKey || reusedKey ? apiKey : undefined;
|
|
22701
|
-
await updateMemoryState(body.preset, { ok: true }, body.preset === "existing" ? undefined : {
|
|
23249
|
+
await updateMemoryState(body.preset, { ok: true }, body.preset === "existing" ? restore ? { kind: "restore" } : undefined : {
|
|
23250
|
+
kind: "save",
|
|
22702
23251
|
baseUrl: baseUrl ?? "https://api.openai.com/v1",
|
|
22703
23252
|
model,
|
|
22704
|
-
apiKey: keyToSave
|
|
23253
|
+
apiKey: keyToSave,
|
|
23254
|
+
prefixes: prefixChanges(model)
|
|
22705
23255
|
});
|
|
22706
23256
|
return { ok: true, dimensions, latencyMs };
|
|
22707
23257
|
} catch (error) {
|
|
@@ -22980,6 +23530,7 @@ init_request_auth_context();
|
|
|
22980
23530
|
init_zod();
|
|
22981
23531
|
init_db3();
|
|
22982
23532
|
init_model_profiles();
|
|
23533
|
+
init_rbac_roles();
|
|
22983
23534
|
init_steering();
|
|
22984
23535
|
init_users();
|
|
22985
23536
|
init_rbac();
|
|
@@ -23156,6 +23707,7 @@ var createTask = route({
|
|
|
23156
23707
|
path: "/api/tasks",
|
|
23157
23708
|
pattern: ["api", "tasks"],
|
|
23158
23709
|
session: bodyTask("parentTaskId"),
|
|
23710
|
+
rbac: { permission: "task.create.own" },
|
|
23159
23711
|
summary: "Create a new task",
|
|
23160
23712
|
tags: ["Tasks"],
|
|
23161
23713
|
body: object2({
|
|
@@ -23262,6 +23814,7 @@ var cancelTaskRoute = route({
|
|
|
23262
23814
|
path: "/api/tasks/{id}/cancel",
|
|
23263
23815
|
pattern: ["api", "tasks", null, "cancel"],
|
|
23264
23816
|
session: { taskParam: "id" },
|
|
23817
|
+
rbac: { permission: "task.cancel.own" },
|
|
23265
23818
|
summary: "Cancel a pending or in-progress task",
|
|
23266
23819
|
tags: ["Tasks"],
|
|
23267
23820
|
params: object2({ id: string2() }),
|
|
@@ -23512,6 +24065,7 @@ var pauseTaskRoute = route({
|
|
|
23512
24065
|
path: "/api/tasks/{id}/pause",
|
|
23513
24066
|
pattern: ["api", "tasks", null, "pause"],
|
|
23514
24067
|
session: { taskParam: "id" },
|
|
24068
|
+
rbac: { permission: "task.action.own" },
|
|
23515
24069
|
summary: "Pause an in-progress task",
|
|
23516
24070
|
tags: ["Tasks"],
|
|
23517
24071
|
params: object2({ id: string2() }),
|
|
@@ -23527,6 +24081,7 @@ var resumeTaskRoute = route({
|
|
|
23527
24081
|
path: "/api/tasks/{id}/resume",
|
|
23528
24082
|
pattern: ["api", "tasks", null, "resume"],
|
|
23529
24083
|
session: { taskParam: "id" },
|
|
24084
|
+
rbac: { permission: "task.action.own" },
|
|
23530
24085
|
summary: "Resume a paused task",
|
|
23531
24086
|
tags: ["Tasks"],
|
|
23532
24087
|
params: object2({ id: string2() }),
|
|
@@ -23596,9 +24151,7 @@ var updateTaskTitleRoute = route({
|
|
|
23596
24151
|
404: { description: "Task not found" }
|
|
23597
24152
|
},
|
|
23598
24153
|
auth: { apiKey: true },
|
|
23599
|
-
rbac: {
|
|
23600
|
-
ungated: "mirrors the pre-RBAC PATCH /api/tasks/{id}/vcs sibling route: bearer auth only"
|
|
23601
|
-
}
|
|
24154
|
+
rbac: { permission: "task.action.own" }
|
|
23602
24155
|
});
|
|
23603
24156
|
async function canSteerTask(req, myAgentId, task, sessionAccess = "none") {
|
|
23604
24157
|
const auth = getRequestAuth(req);
|
|
@@ -23668,6 +24221,34 @@ async function resolveTaskWritePrincipal(req, myAgentId) {
|
|
|
23668
24221
|
const agent = await getAgentById2(agentId);
|
|
23669
24222
|
return { kind: "agent", agentId, isLead: agent?.isLead === true };
|
|
23670
24223
|
}
|
|
24224
|
+
async function mayBindChatThread(principal) {
|
|
24225
|
+
if (principal.kind === "operator")
|
|
24226
|
+
return true;
|
|
24227
|
+
if (principal.kind === "user") {
|
|
24228
|
+
if (!isRbacEnabled())
|
|
24229
|
+
return true;
|
|
24230
|
+
const grant = await getUserGrant2(principal.userId);
|
|
24231
|
+
return grant.grantsAll || grant.verbs.has("integration.chat.post");
|
|
24232
|
+
}
|
|
24233
|
+
return can({
|
|
24234
|
+
principal,
|
|
24235
|
+
verb: "integration.chat.post",
|
|
24236
|
+
resource: { kind: "none" },
|
|
24237
|
+
source: "http"
|
|
24238
|
+
}).allow;
|
|
24239
|
+
}
|
|
24240
|
+
async function deniesForeignContext(req, contextKey) {
|
|
24241
|
+
const auth = getRequestAuth(req);
|
|
24242
|
+
if (auth?.kind !== "user" || !isRbacEnabled())
|
|
24243
|
+
return false;
|
|
24244
|
+
if ((await getUserGrant2(auth.userId)).grantsAll)
|
|
24245
|
+
return false;
|
|
24246
|
+
for (const task of await getInProgressTasksByContextKey2(contextKey)) {
|
|
24247
|
+
if (await deniesNonOwnerTaskWrite2(req, task))
|
|
24248
|
+
return true;
|
|
24249
|
+
}
|
|
24250
|
+
return false;
|
|
24251
|
+
}
|
|
23671
24252
|
async function resolveTaskRequester(req, myAgentId, bodyUserId, trustedUserId) {
|
|
23672
24253
|
const trusted = trustedUserId === undefined ? await resolveHttpAuditUserId(req, myAgentId) : trustedUserId;
|
|
23673
24254
|
if (trusted)
|
|
@@ -23783,8 +24364,12 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23783
24364
|
if (parentTask && await denyParentAttach2(req, res, myAgentId, parentTask, principal)) {
|
|
23784
24365
|
return true;
|
|
23785
24366
|
}
|
|
23786
|
-
if (
|
|
23787
|
-
jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
|
|
24367
|
+
if (parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && chatThreadOfContextKey(parsed.body.contextKey) && !await mayBindChatThread(principal)) {
|
|
24368
|
+
jsonError(res, "Permission denied: only a lead or an admin can bind a task to a chat thread.", 403);
|
|
24369
|
+
return true;
|
|
24370
|
+
}
|
|
24371
|
+
if (parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && await deniesForeignContext(req, parsed.body.contextKey)) {
|
|
24372
|
+
jsonError(res, "You don't have access to the tasks in this context.", 403);
|
|
23788
24373
|
return true;
|
|
23789
24374
|
}
|
|
23790
24375
|
let assetKey;
|
|
@@ -23904,6 +24489,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23904
24489
|
jsonError(res, "Task not found", 404);
|
|
23905
24490
|
return true;
|
|
23906
24491
|
}
|
|
24492
|
+
if (await deniesNonOwnerTaskWrite2(req, task)) {
|
|
24493
|
+
jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
|
|
24494
|
+
return true;
|
|
24495
|
+
}
|
|
23907
24496
|
const principal = await resolveTaskWritePrincipal(req, myAgentId);
|
|
23908
24497
|
if (principal.kind === "agent" && !can({
|
|
23909
24498
|
principal,
|
|
@@ -24395,6 +24984,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24395
24984
|
jsonError(res, "Task not found", 404);
|
|
24396
24985
|
return true;
|
|
24397
24986
|
}
|
|
24987
|
+
if (await deniesNonOwnerTaskWrite2(req, task)) {
|
|
24988
|
+
jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
|
|
24989
|
+
return true;
|
|
24990
|
+
}
|
|
24398
24991
|
if (myAgentId && task.agentId !== myAgentId) {
|
|
24399
24992
|
jsonError(res, "Task belongs to another agent", 403);
|
|
24400
24993
|
return true;
|
|
@@ -24455,6 +25048,15 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24455
25048
|
const parsed = await updateTaskTitleRoute.parse(req, res, pathSegments, queryParams);
|
|
24456
25049
|
if (!parsed)
|
|
24457
25050
|
return true;
|
|
25051
|
+
const existing = await getTaskById2(parsed.params.id);
|
|
25052
|
+
if (!existing) {
|
|
25053
|
+
jsonError(res, "Task not found", 404);
|
|
25054
|
+
return true;
|
|
25055
|
+
}
|
|
25056
|
+
if (await deniesNonOwnerTaskWrite2(req, existing)) {
|
|
25057
|
+
jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
|
|
25058
|
+
return true;
|
|
25059
|
+
}
|
|
24458
25060
|
const task = await updateTaskTitle2(parsed.params.id, parsed.body.title);
|
|
24459
25061
|
if (!task) {
|
|
24460
25062
|
jsonError(res, "Task not found", 404);
|
|
@@ -24472,6 +25074,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24472
25074
|
jsonError(res, "Task not found", 404);
|
|
24473
25075
|
return true;
|
|
24474
25076
|
}
|
|
25077
|
+
if (await deniesNonOwnerTaskWrite2(req, task)) {
|
|
25078
|
+
jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
|
|
25079
|
+
return true;
|
|
25080
|
+
}
|
|
24475
25081
|
if (myAgentId && task.agentId !== myAgentId) {
|
|
24476
25082
|
jsonError(res, "Task belongs to another agent", 403);
|
|
24477
25083
|
return true;
|
|
@@ -34819,7 +35425,7 @@ var triageSessionListRoute = route({
|
|
|
34819
35425
|
401: { description: "Unauthorized" }
|
|
34820
35426
|
},
|
|
34821
35427
|
auth: { apiKey: true },
|
|
34822
|
-
rbac: {
|
|
35428
|
+
rbac: { permission: "favorite.write.own" }
|
|
34823
35429
|
});
|
|
34824
35430
|
var SESSION_MOVE_LIMIT = 200;
|
|
34825
35431
|
var SESSION_MOVE_OWNER_ONLY = "Only the session owner can move this group";
|
|
@@ -34828,9 +35434,7 @@ var SessionMoveResultSchema = object2({
|
|
|
34828
35434
|
moved: array2(string2()),
|
|
34829
35435
|
skipped: array2(object2({ rootTaskId: string2(), reason: string2() }))
|
|
34830
35436
|
});
|
|
34831
|
-
var sessionMoveRbac = {
|
|
34832
|
-
ungated: "per-session access, per-task task.fs.mutate, and asset-key write checks in the handler, as PATCH /api/assets/task/{id}/key"
|
|
34833
|
-
};
|
|
35437
|
+
var sessionMoveRbac = { permission: "task.fs.mutate" };
|
|
34834
35438
|
var sessionMoveResponses = {
|
|
34835
35439
|
400: { description: "Invalid namespace or request" },
|
|
34836
35440
|
403: { description: "Personal destination not authorized" },
|
|
@@ -34902,6 +35506,8 @@ async function checkSessionMove(rootTaskId, actor, req, myAgentId, options = {})
|
|
|
34902
35506
|
return { reason: SESSION_MOVE_UNAVAILABLE };
|
|
34903
35507
|
if (!hasSessionAccess(access, "write"))
|
|
34904
35508
|
return { reason: "Session write access required" };
|
|
35509
|
+
if (await deniesNonOwnerTaskWrite2(req, root))
|
|
35510
|
+
return { reason: NOT_YOUR_TASK_DENIED2 };
|
|
34905
35511
|
}
|
|
34906
35512
|
if (root.parentTaskId)
|
|
34907
35513
|
return { reason: "Task is not a session root" };
|
|
@@ -36989,7 +37595,7 @@ var uiEvent = route({
|
|
|
36989
37595
|
401: { description: "Unauthorized" },
|
|
36990
37596
|
403: { description: "Guest page sessions cannot send events" }
|
|
36991
37597
|
},
|
|
36992
|
-
rbac: {
|
|
37598
|
+
rbac: { permission: "favorite.write.own" }
|
|
36993
37599
|
});
|
|
36994
37600
|
var enableTelemetry = route({
|
|
36995
37601
|
method: "post",
|
|
@@ -37913,76 +38519,11 @@ init_zod();
|
|
|
37913
38519
|
init_db3();
|
|
37914
38520
|
init_rbac_roles();
|
|
37915
38521
|
init_users();
|
|
38522
|
+
init_rbac();
|
|
37916
38523
|
init_types();
|
|
38524
|
+
init_constants2();
|
|
38525
|
+
init_egress_guard();
|
|
37917
38526
|
init_request_auth_context();
|
|
37918
|
-
|
|
37919
|
-
// src/http/ip-rate-limit.ts
|
|
37920
|
-
var PRUNE_THRESHOLD = 1e4;
|
|
37921
|
-
var PRUNE_INTERVAL_MS = 60000;
|
|
37922
|
-
var MAX_BUCKETS = 50000;
|
|
37923
|
-
function isPrivateAddress(address) {
|
|
37924
|
-
const ip = address.startsWith("::ffff:") ? address.slice(7) : address;
|
|
37925
|
-
if (ip === "::1" || ip.startsWith("127.") || ip.startsWith("10."))
|
|
37926
|
-
return true;
|
|
37927
|
-
if (ip.startsWith("192.168.") || ip.startsWith("169.254."))
|
|
37928
|
-
return true;
|
|
37929
|
-
const match = /^172\.(\d+)\./.exec(ip);
|
|
37930
|
-
if (match && Number(match[1]) >= 16 && Number(match[1]) <= 31)
|
|
37931
|
-
return true;
|
|
37932
|
-
const lower = ip.toLowerCase();
|
|
37933
|
-
return lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
|
|
37934
|
-
}
|
|
37935
|
-
function clientIp(req) {
|
|
37936
|
-
const peer = req.socket.remoteAddress ?? "unknown";
|
|
37937
|
-
if (!isPrivateAddress(peer))
|
|
37938
|
-
return peer;
|
|
37939
|
-
const raw = req.headers["x-forwarded-for"];
|
|
37940
|
-
const header = Array.isArray(raw) ? raw[raw.length - 1] : raw;
|
|
37941
|
-
const hop = header?.split(",").pop()?.trim();
|
|
37942
|
-
return hop || peer;
|
|
37943
|
-
}
|
|
37944
|
-
function createIpRateLimiter(options) {
|
|
37945
|
-
const buckets = new Map;
|
|
37946
|
-
let lastPruneAt = 0;
|
|
37947
|
-
function refill(bucket, now) {
|
|
37948
|
-
const elapsed = now - bucket.updatedAt;
|
|
37949
|
-
bucket.tokens = Math.min(options.capacity, bucket.tokens + elapsed * options.refillPerMs);
|
|
37950
|
-
bucket.updatedAt = now;
|
|
37951
|
-
}
|
|
37952
|
-
return {
|
|
37953
|
-
take(ip, now = Date.now()) {
|
|
37954
|
-
if (buckets.size > PRUNE_THRESHOLD && now - lastPruneAt >= PRUNE_INTERVAL_MS) {
|
|
37955
|
-
lastPruneAt = now;
|
|
37956
|
-
for (const [key, bucket] of buckets) {
|
|
37957
|
-
refill(bucket, now);
|
|
37958
|
-
if (bucket.tokens >= options.capacity)
|
|
37959
|
-
buckets.delete(key);
|
|
37960
|
-
}
|
|
37961
|
-
}
|
|
37962
|
-
let bucket = buckets.get(ip);
|
|
37963
|
-
if (!bucket) {
|
|
37964
|
-
if (buckets.size >= MAX_BUCKETS) {
|
|
37965
|
-
const oldest = buckets.keys().next().value;
|
|
37966
|
-
if (oldest !== undefined)
|
|
37967
|
-
buckets.delete(oldest);
|
|
37968
|
-
}
|
|
37969
|
-
bucket = { tokens: options.capacity, updatedAt: now };
|
|
37970
|
-
buckets.set(ip, bucket);
|
|
37971
|
-
}
|
|
37972
|
-
refill(bucket, now);
|
|
37973
|
-
if (bucket.tokens < 1)
|
|
37974
|
-
return false;
|
|
37975
|
-
bucket.tokens -= 1;
|
|
37976
|
-
return true;
|
|
37977
|
-
},
|
|
37978
|
-
reset() {
|
|
37979
|
-
buckets.clear();
|
|
37980
|
-
lastPruneAt = 0;
|
|
37981
|
-
}
|
|
37982
|
-
};
|
|
37983
|
-
}
|
|
37984
|
-
|
|
37985
|
-
// src/http/users.ts
|
|
37986
38527
|
async function composeUser(userId, recentEventLimit = 5) {
|
|
37987
38528
|
const user = await getUserById2(userId);
|
|
37988
38529
|
if (!user)
|
|
@@ -38054,7 +38595,12 @@ var whoamiRoute = route({
|
|
|
38054
38595
|
description: "Authenticated principal",
|
|
38055
38596
|
schema: object2({
|
|
38056
38597
|
kind: _enum2(["operator", "user"]),
|
|
38057
|
-
user: union2([UserSchema, _null()])
|
|
38598
|
+
user: union2([UserSchema, _null()]),
|
|
38599
|
+
grants: object2({
|
|
38600
|
+
grantsAll: boolean(),
|
|
38601
|
+
verbs: array2(string2()),
|
|
38602
|
+
roles: array2(string2())
|
|
38603
|
+
}).optional()
|
|
38058
38604
|
})
|
|
38059
38605
|
},
|
|
38060
38606
|
401: { description: "Unauthorized" }
|
|
@@ -38303,6 +38849,72 @@ var exchangeConnectorCodeRoute = route({
|
|
|
38303
38849
|
ungated: "the single-use code is the credential; the minted token carries the user's own grant and is admitted on use"
|
|
38304
38850
|
}
|
|
38305
38851
|
});
|
|
38852
|
+
var INVITE_ROLES = ["requester", "viewer", "admin"];
|
|
38853
|
+
var createInviteRoute = route({
|
|
38854
|
+
method: "post",
|
|
38855
|
+
path: "/api/invites",
|
|
38856
|
+
pattern: ["api", "invites"],
|
|
38857
|
+
summary: "Invite a teammate with a one-time API link",
|
|
38858
|
+
description: "Creates an invited user with the given role (requester by default) and returns a 7-day single-use API link. The API redirects to the current APP_URL dashboard with the code in the URL fragment. The browser exchanges the code at POST /api/invites/exchange for a personal MCP token. Re-inviting an invited user replaces the role and link. Active or suspended users return 409 user_exists. No email is sent.",
|
|
38859
|
+
tags: ["Users"],
|
|
38860
|
+
body: object2({
|
|
38861
|
+
email: email(),
|
|
38862
|
+
name: string2().trim().min(1).max(200).optional(),
|
|
38863
|
+
role: _enum2(INVITE_ROLES).default("requester")
|
|
38864
|
+
}),
|
|
38865
|
+
responses: {
|
|
38866
|
+
201: {
|
|
38867
|
+
description: "The invited user, API link, dashboard URL, expiry and API reachability",
|
|
38868
|
+
schema: object2({
|
|
38869
|
+
user: ComposedUserSchema.nullable(),
|
|
38870
|
+
inviteUrl: string2(),
|
|
38871
|
+
appUrl: string2(),
|
|
38872
|
+
expiresAt: string2(),
|
|
38873
|
+
apiReachable: _enum2(["public", "local"])
|
|
38874
|
+
})
|
|
38875
|
+
},
|
|
38876
|
+
401: { description: "Unauthorized" },
|
|
38877
|
+
403: { description: "Not allowed to manage roles" },
|
|
38878
|
+
409: { description: "The email belongs to an active or suspended user" }
|
|
38879
|
+
},
|
|
38880
|
+
auth: { apiKey: true },
|
|
38881
|
+
rbac: { permission: "user.role.manage" }
|
|
38882
|
+
});
|
|
38883
|
+
var openInviteRoute = route({
|
|
38884
|
+
method: "get",
|
|
38885
|
+
path: "/invite/{code}",
|
|
38886
|
+
pattern: ["invite", null],
|
|
38887
|
+
summary: "Open an invite in the configured dashboard",
|
|
38888
|
+
description: "Redirects to the current APP_URL dashboard with the code in the URL fragment. Does not validate or consume the invite.",
|
|
38889
|
+
tags: ["Users"],
|
|
38890
|
+
params: object2({ code: string2() }),
|
|
38891
|
+
responses: {
|
|
38892
|
+
302: { description: "Redirect to the dashboard", unstructured: "Dashboard redirect" },
|
|
38893
|
+
404: { description: "Malformed code (code_invalid)" }
|
|
38894
|
+
},
|
|
38895
|
+
auth: { apiKey: false }
|
|
38896
|
+
});
|
|
38897
|
+
var exchangeInviteCodeRoute = route({
|
|
38898
|
+
method: "post",
|
|
38899
|
+
path: "/api/invites/exchange",
|
|
38900
|
+
pattern: ["api", "invites", "exchange"],
|
|
38901
|
+
summary: "Exchange an invite code for the invited user's MCP token",
|
|
38902
|
+
description: "Unauthenticated: the single-use invite code is the credential. Rate limited per IP. Activates the invited user. Unknown, expired, used and connector codes, and codes for users who are no longer invited, all return 404 code_invalid.",
|
|
38903
|
+
tags: ["Users"],
|
|
38904
|
+
body: object2({ code: string2().min(1).max(256) }),
|
|
38905
|
+
maxBodyBytes: 1024,
|
|
38906
|
+
responses: {
|
|
38907
|
+
200: {
|
|
38908
|
+
description: "Minted MCP token for the invited user",
|
|
38909
|
+
schema: object2({ token: string2(), userId: string2() })
|
|
38910
|
+
},
|
|
38911
|
+
404: { description: "Code unknown, malformed, expired, already used or not an invite" },
|
|
38912
|
+
413: { description: "Request body too large" },
|
|
38913
|
+
429: { description: "Rate limited" }
|
|
38914
|
+
},
|
|
38915
|
+
auth: { apiKey: false },
|
|
38916
|
+
rbac: { ungated: "the one-time invite code is the credential" }
|
|
38917
|
+
});
|
|
38306
38918
|
var connectorDiscoveryRoute = route({
|
|
38307
38919
|
method: "get",
|
|
38308
38920
|
path: "/api/connector/discovery",
|
|
@@ -38483,6 +39095,26 @@ var connectorExchangeRateLimiter = createIpRateLimiter({
|
|
|
38483
39095
|
capacity: 10,
|
|
38484
39096
|
refillPerMs: 10 / 60000
|
|
38485
39097
|
});
|
|
39098
|
+
var inviteExchangeRateLimiter = createIpRateLimiter({
|
|
39099
|
+
capacity: 10,
|
|
39100
|
+
refillPerMs: 10 / 60000
|
|
39101
|
+
});
|
|
39102
|
+
function apiReachability(apiUrl) {
|
|
39103
|
+
let host;
|
|
39104
|
+
try {
|
|
39105
|
+
host = assertUrlSafe(apiUrl, { allowInsecure: true }).hostname.toLowerCase();
|
|
39106
|
+
} catch {
|
|
39107
|
+
return "local";
|
|
39108
|
+
}
|
|
39109
|
+
if (!host.includes(".") && !host.includes(":"))
|
|
39110
|
+
return "local";
|
|
39111
|
+
if (/\.(local|internal|lan|home\.arpa)\.?$/.test(host))
|
|
39112
|
+
return "local";
|
|
39113
|
+
return "public";
|
|
39114
|
+
}
|
|
39115
|
+
|
|
39116
|
+
class InviteUserExistsError extends Error {
|
|
39117
|
+
}
|
|
38486
39118
|
async function handleUsers(req, res, pathSegments, queryParams) {
|
|
38487
39119
|
if (whoamiRoute.match(req.method, pathSegments)) {
|
|
38488
39120
|
const parsed = await whoamiRoute.parse(req, res, pathSegments, queryParams);
|
|
@@ -38490,9 +39122,26 @@ async function handleUsers(req, res, pathSegments, queryParams) {
|
|
|
38490
39122
|
return true;
|
|
38491
39123
|
const auth = getRequestAuth(req);
|
|
38492
39124
|
if (auth?.kind === "user") {
|
|
38493
|
-
|
|
39125
|
+
const [grant, roles] = await Promise.all([
|
|
39126
|
+
getUserGrant2(auth.user.id),
|
|
39127
|
+
listUserRoles2(auth.user.id)
|
|
39128
|
+
]);
|
|
39129
|
+
const grantsAll = !isRbacEnabled() || grant.grantsAll;
|
|
39130
|
+
whoamiRoute.respond(res, 200, {
|
|
39131
|
+
kind: "user",
|
|
39132
|
+
user: auth.user,
|
|
39133
|
+
grants: {
|
|
39134
|
+
grantsAll,
|
|
39135
|
+
verbs: grantsAll ? [] : [...grant.verbs].sort(),
|
|
39136
|
+
roles: roles.map((role) => role.name)
|
|
39137
|
+
}
|
|
39138
|
+
});
|
|
38494
39139
|
} else {
|
|
38495
|
-
whoamiRoute.respond(res, 200, {
|
|
39140
|
+
whoamiRoute.respond(res, 200, {
|
|
39141
|
+
kind: "operator",
|
|
39142
|
+
user: null,
|
|
39143
|
+
grants: { grantsAll: true, verbs: [], roles: [] }
|
|
39144
|
+
});
|
|
38496
39145
|
}
|
|
38497
39146
|
return true;
|
|
38498
39147
|
}
|
|
@@ -38696,6 +39345,110 @@ async function handleUsers(req, res, pathSegments, queryParams) {
|
|
|
38696
39345
|
}
|
|
38697
39346
|
return true;
|
|
38698
39347
|
}
|
|
39348
|
+
if (createInviteRoute.match(req.method, pathSegments)) {
|
|
39349
|
+
const parsed = await createInviteRoute.parse(req, res, pathSegments, queryParams);
|
|
39350
|
+
if (!parsed)
|
|
39351
|
+
return true;
|
|
39352
|
+
if (!await ensureRoleManager(req, res))
|
|
39353
|
+
return true;
|
|
39354
|
+
const actor = getOperatorActor(req, res);
|
|
39355
|
+
if (!actor)
|
|
39356
|
+
return true;
|
|
39357
|
+
const { email: email2, name, role } = parsed.body;
|
|
39358
|
+
const actorUserId = actor.kind === "user" ? actor.id : null;
|
|
39359
|
+
let invited;
|
|
39360
|
+
try {
|
|
39361
|
+
invited = await getDbClient2().transaction(async () => {
|
|
39362
|
+
const existing = await findUserByEmail(email2);
|
|
39363
|
+
if (existing && existing.status !== "invited")
|
|
39364
|
+
throw new InviteUserExistsError;
|
|
39365
|
+
let userId;
|
|
39366
|
+
if (existing) {
|
|
39367
|
+
userId = existing.id;
|
|
39368
|
+
await deleteUnusedInviteCodes(userId);
|
|
39369
|
+
} else {
|
|
39370
|
+
const user = await createUser2({
|
|
39371
|
+
name: name ?? (email2.split("@")[0] || email2),
|
|
39372
|
+
email: email2,
|
|
39373
|
+
status: "invited"
|
|
39374
|
+
});
|
|
39375
|
+
userId = user.id;
|
|
39376
|
+
await recordIdentityEvent(userId, "identity_added", actor, null, {
|
|
39377
|
+
email: email2,
|
|
39378
|
+
source: "invite"
|
|
39379
|
+
});
|
|
39380
|
+
}
|
|
39381
|
+
const { before, after } = await setUserRoles2(userId, [role], actorUserId);
|
|
39382
|
+
if (before.join(",") !== after.join(",")) {
|
|
39383
|
+
await recordIdentityEvent(userId, "profile_changed", actor, { rbacRoles: before }, { rbacRoles: after });
|
|
39384
|
+
}
|
|
39385
|
+
const { code, expiresAt } = await createConnectorCode(userId, DEFAULT_INVITE_CODE_LABEL, actor, new Date, { kind: "invite", ttlMs: INVITE_CODE_TTL_MS });
|
|
39386
|
+
return { userId, code, expiresAt };
|
|
39387
|
+
});
|
|
39388
|
+
} catch (err) {
|
|
39389
|
+
if (err instanceof InviteUserExistsError) {
|
|
39390
|
+
jsonError(res, "user_exists", 409);
|
|
39391
|
+
return true;
|
|
39392
|
+
}
|
|
39393
|
+
jsonError(res, "Failed to create invite", 500);
|
|
39394
|
+
return true;
|
|
39395
|
+
}
|
|
39396
|
+
const apiUrl = await resolveMcpBaseUrl();
|
|
39397
|
+
const appUrl = await resolveAppUrl() ?? DEFAULT_APP_URL;
|
|
39398
|
+
const inviteUrl = `${apiUrl}/invite/${invited.code}`;
|
|
39399
|
+
createInviteRoute.respond(res, 201, {
|
|
39400
|
+
user: await composeUser(invited.userId),
|
|
39401
|
+
inviteUrl,
|
|
39402
|
+
appUrl,
|
|
39403
|
+
expiresAt: invited.expiresAt,
|
|
39404
|
+
apiReachable: apiReachability(apiUrl)
|
|
39405
|
+
});
|
|
39406
|
+
return true;
|
|
39407
|
+
}
|
|
39408
|
+
if (openInviteRoute.match(req.method, pathSegments)) {
|
|
39409
|
+
const parsed = await openInviteRoute.parse(req, res, pathSegments, queryParams);
|
|
39410
|
+
if (!parsed)
|
|
39411
|
+
return true;
|
|
39412
|
+
const { code } = parsed.params;
|
|
39413
|
+
if (!/^[A-Za-z0-9_-]{16,256}$/.test(code)) {
|
|
39414
|
+
jsonError(res, "code_invalid", 404);
|
|
39415
|
+
return true;
|
|
39416
|
+
}
|
|
39417
|
+
const apiUrl = await resolveMcpBaseUrl();
|
|
39418
|
+
const appUrl = await resolveAppUrl() ?? DEFAULT_APP_URL;
|
|
39419
|
+
res.writeHead(302, {
|
|
39420
|
+
Location: `${appUrl}/join?swarm=${encodeURIComponent(apiUrl)}#code=${code}`,
|
|
39421
|
+
"Cache-Control": "no-store",
|
|
39422
|
+
"Referrer-Policy": "no-referrer"
|
|
39423
|
+
});
|
|
39424
|
+
res.end();
|
|
39425
|
+
return true;
|
|
39426
|
+
}
|
|
39427
|
+
if (exchangeInviteCodeRoute.match(req.method, pathSegments)) {
|
|
39428
|
+
if (!inviteExchangeRateLimiter.take(clientIp(req))) {
|
|
39429
|
+
res.setHeader("Retry-After", "6");
|
|
39430
|
+
jsonError(res, "rate_limited", 429);
|
|
39431
|
+
return true;
|
|
39432
|
+
}
|
|
39433
|
+
const parsed = await exchangeInviteCodeRoute.parse(req, res, pathSegments, queryParams);
|
|
39434
|
+
if (!parsed)
|
|
39435
|
+
return true;
|
|
39436
|
+
if (!CONNECTOR_CODE_PATTERN.test(parsed.body.code)) {
|
|
39437
|
+
jsonError(res, "code_invalid", 404);
|
|
39438
|
+
return true;
|
|
39439
|
+
}
|
|
39440
|
+
try {
|
|
39441
|
+
const result = await exchangeConnectorCode(parsed.body.code, { kind: "invite" });
|
|
39442
|
+
if (!result) {
|
|
39443
|
+
jsonError(res, "code_invalid", 404);
|
|
39444
|
+
return true;
|
|
39445
|
+
}
|
|
39446
|
+
exchangeInviteCodeRoute.respond(res, 200, result);
|
|
39447
|
+
} catch {
|
|
39448
|
+
jsonError(res, "Failed to exchange code", 500);
|
|
39449
|
+
}
|
|
39450
|
+
return true;
|
|
39451
|
+
}
|
|
38699
39452
|
if (revokeUserMcpTokenRoute.match(req.method, pathSegments)) {
|
|
38700
39453
|
const parsed = await revokeUserMcpTokenRoute.parse(req, res, pathSegments, queryParams);
|
|
38701
39454
|
if (!parsed)
|
|
@@ -41250,7 +42003,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
41250
42003
|
const semconv = httpServerSemconvAttributes(req);
|
|
41251
42004
|
const span = skipSpan ? null : startSpan2(spanName, {
|
|
41252
42005
|
"http.request.method": req.method ?? "",
|
|
41253
|
-
"url.path": reqPath,
|
|
42006
|
+
"url.path": redactShareTokenInPath(reqPath),
|
|
41254
42007
|
"url.scheme": semconv["url.scheme"],
|
|
41255
42008
|
"http.route": httpRoute,
|
|
41256
42009
|
"server.address": semconv["server.address"],
|
|
@@ -41294,6 +42047,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
41294
42047
|
() => handlePoolKeys(req, res, pathSegments, queryParams),
|
|
41295
42048
|
() => handleConfig(req, res, pathSegments, queryParams),
|
|
41296
42049
|
() => handleFs(req, res, pathSegments, queryParams, myAgentId),
|
|
42050
|
+
() => handleAgentFsProxy(req, res),
|
|
41297
42051
|
() => handleComb(req, res, pathSegments, queryParams, myAgentId),
|
|
41298
42052
|
() => handleKv(req, res, pathSegments, queryParams),
|
|
41299
42053
|
() => handleRooms(req, res, pathSegments, queryParams),
|
|
@@ -41368,7 +42122,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
41368
42122
|
});
|
|
41369
42123
|
});
|
|
41370
42124
|
var detachRealtime = attachRealtimeTransport(httpServer);
|
|
41371
|
-
var { attachControllerTransport } = await import("./controllers-connect-
|
|
42125
|
+
var { attachControllerTransport } = await import("./controllers-connect-ajvh9h0v.js");
|
|
41372
42126
|
var detachControllers = attachControllerTransport(httpServer);
|
|
41373
42127
|
var stopWorkerNudges = startWorkerNudgeListener();
|
|
41374
42128
|
var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
|
|
@@ -41397,7 +42151,7 @@ async function shutdown() {
|
|
|
41397
42151
|
sessionsProcessed: getServerSessionsProcessed()
|
|
41398
42152
|
});
|
|
41399
42153
|
if (hasCapability("scheduling")) {
|
|
41400
|
-
const { stopScheduler } = await import("./index-
|
|
42154
|
+
const { stopScheduler } = await import("./index-ch0c1xff.js");
|
|
41401
42155
|
stopScheduler();
|
|
41402
42156
|
}
|
|
41403
42157
|
stopHeartbeat2();
|
|
@@ -41409,7 +42163,7 @@ async function shutdown() {
|
|
|
41409
42163
|
await stopSlackApp2();
|
|
41410
42164
|
await stopChatSdk();
|
|
41411
42165
|
if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
|
|
41412
|
-
const { stopOAuthKeepalive } = await import("./keepalive-
|
|
42166
|
+
const { stopOAuthKeepalive } = await import("./keepalive-zzc3hks1.js");
|
|
41413
42167
|
await stopOAuthKeepalive();
|
|
41414
42168
|
}
|
|
41415
42169
|
stopOAuthPendingGc();
|
|
@@ -41489,21 +42243,21 @@ try {
|
|
|
41489
42243
|
console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
|
|
41490
42244
|
}
|
|
41491
42245
|
try {
|
|
41492
|
-
const { ensurePricingSeeded } = await import("./boot-seeds-
|
|
42246
|
+
const { ensurePricingSeeded } = await import("./boot-seeds-r3kwwv8b.js");
|
|
41493
42247
|
ensurePricingSeeded();
|
|
41494
|
-
const { startPricingRefreshLoop } = await import("./pricing-refresh-
|
|
42248
|
+
const { startPricingRefreshLoop } = await import("./pricing-refresh-wkpwhgvs.js");
|
|
41495
42249
|
startPricingRefreshLoop();
|
|
41496
42250
|
} catch (err) {
|
|
41497
42251
|
console.error("[startup] Failed to seed pricing rows:", err);
|
|
41498
42252
|
}
|
|
41499
42253
|
try {
|
|
41500
|
-
const { ensureModelProfilesSeeded } = await import("./boot-seeds-
|
|
42254
|
+
const { ensureModelProfilesSeeded } = await import("./boot-seeds-r3kwwv8b.js");
|
|
41501
42255
|
await ensureModelProfilesSeeded();
|
|
41502
42256
|
} catch (err) {
|
|
41503
42257
|
console.error("[startup] Failed to seed model profiles:", err);
|
|
41504
42258
|
}
|
|
41505
42259
|
try {
|
|
41506
|
-
const { ensureRbacSeeded } = await import("./boot-seeds-
|
|
42260
|
+
const { ensureRbacSeeded } = await import("./boot-seeds-r3kwwv8b.js");
|
|
41507
42261
|
ensureRbacSeeded();
|
|
41508
42262
|
} catch (err) {
|
|
41509
42263
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
@@ -41511,7 +42265,7 @@ try {
|
|
|
41511
42265
|
throw err;
|
|
41512
42266
|
}
|
|
41513
42267
|
try {
|
|
41514
|
-
const { runAllSeeders } = await import("./index-
|
|
42268
|
+
const { runAllSeeders } = await import("./index-ejqdkkqx.js");
|
|
41515
42269
|
await runAllSeeders({ scriptEmbeddingMode: "skip" });
|
|
41516
42270
|
} catch (err) {
|
|
41517
42271
|
console.error("[startup] Failed to seed built-in entities:", err);
|
|
@@ -41574,42 +42328,42 @@ httpServer.listen(port, async () => {
|
|
|
41574
42328
|
await initWorkflows2();
|
|
41575
42329
|
await startScriptRunSupervisor(getMcpBaseUrl());
|
|
41576
42330
|
if (hasCapability("scheduling")) {
|
|
41577
|
-
const { startScheduler } = await import("./index-
|
|
41578
|
-
const { getExecutorRegistry } = await import("./index-
|
|
42331
|
+
const { startScheduler } = await import("./index-ch0c1xff.js");
|
|
42332
|
+
const { getExecutorRegistry } = await import("./index-5nb266jr.js");
|
|
41579
42333
|
const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
|
|
41580
42334
|
startScheduler(getExecutorRegistry(), intervalMs, {
|
|
41581
42335
|
runId: globalState.__runId
|
|
41582
42336
|
});
|
|
41583
42337
|
}
|
|
41584
42338
|
if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
|
|
41585
|
-
const { startHeartbeat } = await import("./index-
|
|
42339
|
+
const { startHeartbeat } = await import("./index-ed8dy8rn.js");
|
|
41586
42340
|
const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
|
|
41587
42341
|
startHeartbeat(heartbeatMs);
|
|
41588
42342
|
}
|
|
41589
42343
|
startPoolRouting();
|
|
41590
42344
|
startRuntimeSupervisor();
|
|
41591
42345
|
if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
|
|
41592
|
-
const { startOAuthKeepalive } = await import("./keepalive-
|
|
42346
|
+
const { startOAuthKeepalive } = await import("./keepalive-zzc3hks1.js");
|
|
41593
42347
|
startOAuthKeepalive();
|
|
41594
42348
|
}
|
|
41595
42349
|
if (!isEnvFlagEnabled("OAUTH_REFRESH_SWEEP_DISABLE", false)) {
|
|
41596
|
-
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-
|
|
42350
|
+
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-7qt0g55z.js");
|
|
41597
42351
|
startOAuthRefreshSweep();
|
|
41598
42352
|
}
|
|
41599
42353
|
startOAuthPendingGc();
|
|
41600
42354
|
await startMemoryGc();
|
|
41601
42355
|
await startDbRetention();
|
|
41602
42356
|
startHumanFreeDrain();
|
|
41603
|
-
import("./boot-reembed-
|
|
42357
|
+
import("./boot-reembed-k5y5xbj0.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
41604
42358
|
console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
|
|
41605
42359
|
});
|
|
41606
|
-
import("./boot-reembed-
|
|
42360
|
+
import("./boot-reembed-fxfw17m2.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
41607
42361
|
console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
|
|
41608
42362
|
});
|
|
41609
|
-
import("./boot-scrub-sweep-
|
|
42363
|
+
import("./boot-scrub-sweep-se41j9fd.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
|
|
41610
42364
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
41611
42365
|
});
|
|
41612
|
-
import("./session-source-backfill-
|
|
42366
|
+
import("./session-source-backfill-62202awe.js").then(({ runSessionSourceBackfill }) => runSessionSourceBackfill()).catch((err) => {
|
|
41613
42367
|
console.error("[session-source-backfill] startup run failed (non-fatal):", err);
|
|
41614
42368
|
});
|
|
41615
42369
|
}).on("error", (err) => {
|