@desplega.ai/agent-swarm 2.6.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/README.md +2 -0
- package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
- package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
- package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
- package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
- package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-5sckpqcx.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-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
- package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
- package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
- package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
- package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
- package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
- package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
- package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
- package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
- package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
- package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
- package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
- package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
- package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
- package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
- package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
- package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-7enjwya9.js → cli-vz78s5wm.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-6y51ryva.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
- package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +31 -21
- package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
- package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
- package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-d4pdpb3r.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-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-y6nkzhnn.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-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
- package/dist/{index-k0yt1tvq.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-38t35h0n.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/lead-c0pjcaw4.js +79 -0
- package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
- package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
- package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
- package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
- package/dist/worker-d4q9sqbc.js +79 -0
- package/openapi.json +1536 -407
- package/package.json +7 -5
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/connector-codes.ts +65 -22
- package/src/be/db/api-keys.ts +9 -0
- package/src/be/db/workflow-runs.ts +36 -5
- package/src/be/db.ts +80 -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 +257 -0
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +4 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +9 -3
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/seed-skills/index.ts +7 -0
- 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 +117 -4
- 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/provider-credentials.ts +2 -1
- package/src/commands/runner.ts +14 -1
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +24 -9
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +7 -4
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/pages-public.ts +38 -5
- package/src/http/scripts.ts +27 -0
- 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/http/workflows.ts +10 -0
- package/src/http/x.ts +5 -0
- package/src/prompts/base-prompt.ts +45 -10
- package/src/prompts/session-templates.ts +43 -5
- 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/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +99 -14
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/scheduler.ts +2 -1
- package/src/scripts-runtime/eval-harness.ts +8 -1
- package/src/scripts-runtime/executors/native.ts +22 -16
- package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
- package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
- package/src/scripts-runtime/executors/quickjs.ts +7 -0
- package/src/scripts-runtime/executors/registry.ts +32 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/host-validated-args.ts +133 -0
- package/src/scripts-runtime/loader.ts +30 -3
- package/src/scripts-runtime/swarm-sdk.ts +9 -2
- package/src/scripts-runtime/types/stdlib.d.ts +9 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/base-prompt.test.ts +139 -4
- 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/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/credential-check.test.ts +4 -2
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/docs-index.test.ts +135 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/fetch-resolved-env.test.ts +16 -2
- 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/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -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/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +162 -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/opencode-adapter.test.ts +2 -1
- package/src/tests/pages-frame-ancestors.test.ts +162 -0
- package/src/tests/pi-codemode.test.ts +2 -1
- package/src/tests/pi-mono-adapter.test.ts +54 -0
- package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +20 -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/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/script-executor-conformance.test.ts +36 -1
- package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
- package/src/tests/script-executor-quickjs.test.ts +140 -0
- package/src/tests/script-host-validated-args-native.test.ts +196 -0
- package/src/tests/script-per-script-executor.test.ts +415 -0
- package/src/tests/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +3 -2
- package/src/tests/self-improvement.test.ts +4 -4
- 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 +381 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
- package/src/tests/workflow-run-finished-event.test.ts +263 -0
- package/src/tests/workflow-swarm-script.test.ts +34 -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/script-common.ts +1 -0
- package/src/tools/script-run.ts +7 -0
- package/src/tools/script-upsert.ts +7 -0
- package/src/tools/store-progress.ts +71 -1
- package/src/tools/utils.ts +12 -1
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/credentials.ts +5 -2
- package/src/utils/task-attachment-links.ts +15 -3
- package/src/workflows/engine.ts +18 -16
- package/src/workflows/executors/swarm-script.ts +5 -0
- package/src/workflows/foreach-driver.ts +5 -11
- package/src/workflows/node-timeout.ts +30 -0
- package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
- package/templates/community/linear-delivery-loop/README.md +38 -0
- package/templates/community/linear-delivery-loop/config.example.json +31 -0
- package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
- package/templates/community/linear-delivery-loop/schedule.json +10 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
- package/templates/skills/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/dist/handlers-eqsnmv95.js +0 -41
- package/dist/lead-23e7z59t.js +0 -79
- package/dist/worker-e3w7vye1.js +0 -79
- 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
|
@@ -288,12 +288,15 @@ import {
|
|
|
288
288
|
emitBudgetRefusalSideEffects,
|
|
289
289
|
workflowModelErrors,
|
|
290
290
|
patchWorkflowDefinition,
|
|
291
|
+
idempotencyKeyHeader,
|
|
292
|
+
readIdempotencyKey,
|
|
293
|
+
replayAcceptedOperation,
|
|
291
294
|
resolveScriptsOnlyMode,
|
|
292
295
|
ALL_CAPABILITIES,
|
|
293
296
|
hasCapability,
|
|
294
297
|
getEnabledCapabilities,
|
|
295
298
|
createServer
|
|
296
|
-
} from "./cli-
|
|
299
|
+
} from "./cli-pzafyqtk.js";
|
|
297
300
|
import {
|
|
298
301
|
chatThreadOfContextKey,
|
|
299
302
|
agentContextKey,
|
|
@@ -335,7 +338,9 @@ import {
|
|
|
335
338
|
init_asset_key_audit,
|
|
336
339
|
CredentialBindingSchema,
|
|
337
340
|
placeholderForConfigKey,
|
|
341
|
+
isPrivateAddress,
|
|
338
342
|
assertUrlSafe,
|
|
343
|
+
init_egress_guard,
|
|
339
344
|
init_credential_broker,
|
|
340
345
|
encryptSecret,
|
|
341
346
|
decryptSecret,
|
|
@@ -403,6 +408,7 @@ import {
|
|
|
403
408
|
getAllTasks2,
|
|
404
409
|
getTasksCount2,
|
|
405
410
|
getTaskStats2,
|
|
411
|
+
getInProgressTasksByContextKey2,
|
|
406
412
|
getLatestScriptRunStepTaskByContextKey2,
|
|
407
413
|
init_read,
|
|
408
414
|
MODEL_PROFILE_CONFIG_KEY,
|
|
@@ -454,7 +460,6 @@ import {
|
|
|
454
460
|
updateTaskProgress2,
|
|
455
461
|
IdempotencyKeyTakenError,
|
|
456
462
|
workflowRequestFingerprint,
|
|
457
|
-
findAcceptedWorkflowOperation,
|
|
458
463
|
assertWorkflowIdempotencyKeyUnused,
|
|
459
464
|
recordAcceptedWorkflowOperation,
|
|
460
465
|
init_workflow_idempotency,
|
|
@@ -521,6 +526,7 @@ import {
|
|
|
521
526
|
searchMemoryCandidates,
|
|
522
527
|
init_search_candidates,
|
|
523
528
|
getEmbeddingProvider2,
|
|
529
|
+
resetEmbeddingProvider2,
|
|
524
530
|
getMemoryStore2,
|
|
525
531
|
init_memory,
|
|
526
532
|
searchScripts,
|
|
@@ -763,7 +769,7 @@ import {
|
|
|
763
769
|
countScriptRunJournalSteps2,
|
|
764
770
|
countScriptRunJournalAgentTaskSteps2,
|
|
765
771
|
init_db3
|
|
766
|
-
} from "./cli-
|
|
772
|
+
} from "./cli-qvaa1gjp.js";
|
|
767
773
|
import {
|
|
768
774
|
PRIMARY_ROLE_NAMES2,
|
|
769
775
|
getUserGrant2,
|
|
@@ -774,22 +780,22 @@ import {
|
|
|
774
780
|
assertNotLastAdmin2,
|
|
775
781
|
setUserRoles2,
|
|
776
782
|
init_rbac_roles
|
|
777
|
-
} from "./cli-
|
|
778
|
-
import"./cli-
|
|
783
|
+
} from "./cli-hpkvgdad.js";
|
|
784
|
+
import"./cli-typdpe8g.js";
|
|
779
785
|
import {
|
|
780
786
|
normalizeModelKey
|
|
781
|
-
} from "./cli-
|
|
787
|
+
} from "./cli-2sjm3t4r.js";
|
|
782
788
|
import {
|
|
783
789
|
upsertModelCatalogOverlay,
|
|
784
790
|
deleteModelCatalogOverlay,
|
|
785
791
|
loadModelsCatalog,
|
|
786
792
|
requestModelCatalogRefresh2
|
|
787
|
-
} from "./cli-
|
|
793
|
+
} from "./cli-twjnyvs8.js";
|
|
788
794
|
import {
|
|
789
795
|
AMP_ESTIMATE_MODELS,
|
|
790
796
|
ampBillsCacheWrites,
|
|
791
797
|
resolveAmpModel
|
|
792
|
-
} from "./cli-
|
|
798
|
+
} from "./cli-cb50g9cd.js";
|
|
793
799
|
import {
|
|
794
800
|
workflowEventBus2,
|
|
795
801
|
init_event_bus
|
|
@@ -829,7 +835,7 @@ import {
|
|
|
829
835
|
generateEdges,
|
|
830
836
|
definitionNodeIds,
|
|
831
837
|
validateDefinition
|
|
832
|
-
} from "./cli-
|
|
838
|
+
} from "./cli-dv11pgpv.js";
|
|
833
839
|
import {
|
|
834
840
|
require_dist2,
|
|
835
841
|
isInvalidClientError,
|
|
@@ -889,8 +895,10 @@ import {
|
|
|
889
895
|
buildSandboxedCommand,
|
|
890
896
|
sandboxSpawnEnv,
|
|
891
897
|
readStreamCapped,
|
|
898
|
+
isExecutorAllowedByDeployment,
|
|
892
899
|
MIN_SCRIPT_WALL_CLOCK_MS,
|
|
893
900
|
MAX_SCRIPT_WALL_CLOCK_MS,
|
|
901
|
+
HOST_VALIDATED_ARGS,
|
|
894
902
|
require_typescript2,
|
|
895
903
|
checkImportAllowlist,
|
|
896
904
|
runScriptWithRunCredential,
|
|
@@ -909,14 +917,14 @@ import {
|
|
|
909
917
|
typecheckWithAmbient,
|
|
910
918
|
typecheckScript,
|
|
911
919
|
parseSkillContent
|
|
912
|
-
} from "./cli-
|
|
920
|
+
} from "./cli-kbtdftbw.js";
|
|
913
921
|
import {
|
|
914
922
|
buildAuthorizationUrl,
|
|
915
923
|
exchangeAuthorizationCode,
|
|
916
924
|
oauthAppRowToProviderConfig,
|
|
917
925
|
forceRefreshAuthorizationOrThrow,
|
|
918
926
|
forceRefreshTokenOrThrow
|
|
919
|
-
} from "./cli-
|
|
927
|
+
} from "./cli-rm66vqdz.js";
|
|
920
928
|
import {
|
|
921
929
|
signalProcessGroup,
|
|
922
930
|
detachedProcessGroup,
|
|
@@ -949,9 +957,9 @@ import {
|
|
|
949
957
|
getSlackApp2,
|
|
950
958
|
startSlackApp2,
|
|
951
959
|
stopSlackApp2
|
|
952
|
-
} from "./cli-
|
|
953
|
-
import"./cli-
|
|
954
|
-
import"./cli-
|
|
960
|
+
} from "./cli-thsaq4pd.js";
|
|
961
|
+
import"./cli-vhpckzvp.js";
|
|
962
|
+
import"./cli-ekptgk2m.js";
|
|
955
963
|
import {
|
|
956
964
|
_fetchHolder,
|
|
957
965
|
CLIENT_ID,
|
|
@@ -968,7 +976,7 @@ import {
|
|
|
968
976
|
import {
|
|
969
977
|
retryFailedRun2,
|
|
970
978
|
cancelWorkflowRun2
|
|
971
|
-
} from "./cli-
|
|
979
|
+
} from "./cli-3v2cyqb8.js";
|
|
972
980
|
import {
|
|
973
981
|
duplicateGuardedQueryKey,
|
|
974
982
|
warnIfCorsAllowsAnyOrigin,
|
|
@@ -978,6 +986,7 @@ import {
|
|
|
978
986
|
parseQueryParams,
|
|
979
987
|
getPathSegments,
|
|
980
988
|
wireHttpSpanLifecycle,
|
|
989
|
+
redactShareTokenInPath,
|
|
981
990
|
safeRequestUrlForLog,
|
|
982
991
|
agentWithCapacity,
|
|
983
992
|
RequestBodyTooLargeError,
|
|
@@ -1007,13 +1016,15 @@ import {
|
|
|
1007
1016
|
readableByFor2,
|
|
1008
1017
|
SESSION_WRITE_DENIED2,
|
|
1009
1018
|
hasSessionWrite2,
|
|
1019
|
+
NOT_YOUR_TASK_DENIED2,
|
|
1020
|
+
deniesNonOwnerTaskWrite2,
|
|
1010
1021
|
denyParentAttach2,
|
|
1011
1022
|
runtimeInstanceHeader,
|
|
1012
1023
|
bodyTask,
|
|
1013
1024
|
bodyListTask,
|
|
1014
1025
|
describeRequestRoute,
|
|
1015
1026
|
route
|
|
1016
|
-
} from "./cli-
|
|
1027
|
+
} from "./cli-hq2v4rr5.js";
|
|
1017
1028
|
import {
|
|
1018
1029
|
FilesError,
|
|
1019
1030
|
normalizeFilesError,
|
|
@@ -1041,7 +1052,7 @@ import {
|
|
|
1041
1052
|
resolvePoolDelivery2,
|
|
1042
1053
|
createPoolAgent2,
|
|
1043
1054
|
init_db
|
|
1044
|
-
} from "./cli-
|
|
1055
|
+
} from "./cli-6vwjsetx.js";
|
|
1045
1056
|
import {
|
|
1046
1057
|
RATING_MODEL_MAX_LENGTH,
|
|
1047
1058
|
REFERENCES_SOURCE_MAX_LENGTH,
|
|
@@ -1051,14 +1062,17 @@ import"./cli-737pyqxr.js";
|
|
|
1051
1062
|
import {
|
|
1052
1063
|
startPoolRouting,
|
|
1053
1064
|
stopPoolRouting,
|
|
1065
|
+
INVITE_CODE_TTL_MS,
|
|
1054
1066
|
DEFAULT_CONNECTOR_CODE_LABEL,
|
|
1067
|
+
DEFAULT_INVITE_CODE_LABEL,
|
|
1055
1068
|
createConnectorCode,
|
|
1056
1069
|
exchangeConnectorCode,
|
|
1070
|
+
deleteUnusedInviteCodes,
|
|
1057
1071
|
checkHeartbeatChecklist2,
|
|
1058
1072
|
runHeartbeatSweep2,
|
|
1059
1073
|
stopHeartbeat2
|
|
1060
|
-
} from "./cli-
|
|
1061
|
-
import"./cli-
|
|
1074
|
+
} from "./cli-5qt9vzmq.js";
|
|
1075
|
+
import"./cli-rg4b2g6j.js";
|
|
1062
1076
|
import {
|
|
1063
1077
|
import_websocket,
|
|
1064
1078
|
import_websocket_server
|
|
@@ -1067,7 +1081,7 @@ import {
|
|
|
1067
1081
|
runtimeSupervisorStatus,
|
|
1068
1082
|
startRuntimeSupervisor,
|
|
1069
1083
|
stopRuntimeSupervisor
|
|
1070
|
-
} from "./cli-
|
|
1084
|
+
} from "./cli-f3mzy662.js";
|
|
1071
1085
|
import"./cli-ns59dc6r.js";
|
|
1072
1086
|
import {
|
|
1073
1087
|
ACP_TARGET_IDS
|
|
@@ -1078,7 +1092,7 @@ import {
|
|
|
1078
1092
|
} from "./cli-c081bj3b.js";
|
|
1079
1093
|
import {
|
|
1080
1094
|
MAX_RATE_LIMIT_RESET_MS
|
|
1081
|
-
} from "./cli-
|
|
1095
|
+
} from "./cli-vz78s5wm.js";
|
|
1082
1096
|
import {
|
|
1083
1097
|
isLeadChannelMonitoringEnabled,
|
|
1084
1098
|
ORG_IDENTITY_KEYS,
|
|
@@ -1088,14 +1102,14 @@ import {
|
|
|
1088
1102
|
isUnknownModelError,
|
|
1089
1103
|
checkProviderPoolKey,
|
|
1090
1104
|
writeSkillsToFilesystem
|
|
1091
|
-
} from "./cli-
|
|
1105
|
+
} from "./cli-xbz6mcg4.js";
|
|
1092
1106
|
import {
|
|
1093
1107
|
inviteEmailToSharedOrg,
|
|
1094
1108
|
ensureAgentFsCredentialsForAgent
|
|
1095
|
-
} from "./cli-
|
|
1109
|
+
} from "./cli-x3cngb1h.js";
|
|
1096
1110
|
import {
|
|
1097
1111
|
Anthropic
|
|
1098
|
-
} from "./cli-
|
|
1112
|
+
} from "./cli-prcyzecg.js";
|
|
1099
1113
|
import {
|
|
1100
1114
|
isCodexOAuthConfigKey
|
|
1101
1115
|
} from "./cli-j4bwnpkm.js";
|
|
@@ -1112,6 +1126,7 @@ import {
|
|
|
1112
1126
|
registerSessionTokenConnection,
|
|
1113
1127
|
init_session_token_connections,
|
|
1114
1128
|
findUserById,
|
|
1129
|
+
findUserByEmail,
|
|
1115
1130
|
getUserIdentities,
|
|
1116
1131
|
listUserEvents,
|
|
1117
1132
|
listUserTokens,
|
|
@@ -1132,7 +1147,7 @@ import {
|
|
|
1132
1147
|
revokeAgentTokens,
|
|
1133
1148
|
fingerprintApiKey,
|
|
1134
1149
|
init_users
|
|
1135
|
-
} from "./cli-
|
|
1150
|
+
} from "./cli-nf6k60sn.js";
|
|
1136
1151
|
import"./cli-v41kn3cd.js";
|
|
1137
1152
|
import"./cli-wgtk7xxr.js";
|
|
1138
1153
|
import"./cli-1f0gp9vz.js";
|
|
@@ -1147,7 +1162,7 @@ import {
|
|
|
1147
1162
|
clearAuditSink,
|
|
1148
1163
|
can,
|
|
1149
1164
|
init_rbac
|
|
1150
|
-
} from "./cli-
|
|
1165
|
+
} from "./cli-k4awgvzm.js";
|
|
1151
1166
|
import {
|
|
1152
1167
|
ASSET_KEY_MAX_LENGTH,
|
|
1153
1168
|
normalizeAssetKey,
|
|
@@ -1265,6 +1280,7 @@ import {
|
|
|
1265
1280
|
PromptTemplateHistorySchema,
|
|
1266
1281
|
ScriptScopeSchema,
|
|
1267
1282
|
ScriptFsModeSchema,
|
|
1283
|
+
ScriptExecutorNameSchema,
|
|
1268
1284
|
ScriptRecordSchema,
|
|
1269
1285
|
ScriptVersionRecordSchema,
|
|
1270
1286
|
ExtensionManifestSchema,
|
|
@@ -1297,12 +1313,12 @@ import {
|
|
|
1297
1313
|
KvKeySchema,
|
|
1298
1314
|
KvEntrySchema,
|
|
1299
1315
|
init_types
|
|
1300
|
-
} from "./cli-
|
|
1316
|
+
} from "./cli-s504c3w7.js";
|
|
1301
1317
|
import {
|
|
1302
1318
|
IDENTITY_FIELD_BUDGETS,
|
|
1303
1319
|
IdentityFieldBudgetError,
|
|
1304
1320
|
init_identity_field_budget
|
|
1305
|
-
} from "./cli-
|
|
1321
|
+
} from "./cli-kj69b9tw.js";
|
|
1306
1322
|
import {
|
|
1307
1323
|
isSteeringEnabled2,
|
|
1308
1324
|
init_steering_enabled
|
|
@@ -1325,7 +1341,7 @@ import {
|
|
|
1325
1341
|
splitCredentialPool,
|
|
1326
1342
|
selectCredential,
|
|
1327
1343
|
init_credentials
|
|
1328
|
-
} from "./cli-
|
|
1344
|
+
} from "./cli-gt4h8cxw.js";
|
|
1329
1345
|
import {
|
|
1330
1346
|
getOpenRouterAttributionHeaders,
|
|
1331
1347
|
init_openrouter_base_url
|
|
@@ -1339,7 +1355,7 @@ import {
|
|
|
1339
1355
|
resolveTemplate,
|
|
1340
1356
|
init_resolver,
|
|
1341
1357
|
init_session_templates
|
|
1342
|
-
} from "./cli-
|
|
1358
|
+
} from "./cli-b41p764a.js";
|
|
1343
1359
|
import {
|
|
1344
1360
|
init_zod
|
|
1345
1361
|
} from "./cli-4chz1hye.js";
|
|
@@ -1374,25 +1390,28 @@ import {
|
|
|
1374
1390
|
import {
|
|
1375
1391
|
init_package,
|
|
1376
1392
|
package_default
|
|
1377
|
-
} from "./cli-
|
|
1378
|
-
import {
|
|
1379
|
-
realtimeBus,
|
|
1380
|
-
init_bus
|
|
1381
|
-
} from "./cli-syfdvhkc.js";
|
|
1393
|
+
} from "./cli-swkx9jm2.js";
|
|
1382
1394
|
import {
|
|
1383
1395
|
MAX_PROFILE_FILE_LENGTH,
|
|
1396
|
+
DEFAULT_APP_URL,
|
|
1384
1397
|
getConfiguredAppUrls,
|
|
1385
1398
|
getAppUrl,
|
|
1386
1399
|
getMcpBaseUrl,
|
|
1387
1400
|
getPublicMcpBaseUrl,
|
|
1388
1401
|
agentFsFileRoute,
|
|
1402
|
+
isCombEnabled,
|
|
1403
|
+
isAgentFsProxyEnabled,
|
|
1389
1404
|
init_constants2
|
|
1390
|
-
} from "./cli-
|
|
1405
|
+
} from "./cli-3cywb44q.js";
|
|
1391
1406
|
import {
|
|
1392
1407
|
parseEnvFlag,
|
|
1393
1408
|
isEnvFlagEnabled,
|
|
1394
1409
|
init_env_flag
|
|
1395
1410
|
} from "./cli-cebvbc9t.js";
|
|
1411
|
+
import {
|
|
1412
|
+
realtimeBus,
|
|
1413
|
+
init_bus
|
|
1414
|
+
} from "./cli-syfdvhkc.js";
|
|
1396
1415
|
import {
|
|
1397
1416
|
getApiKey,
|
|
1398
1417
|
init_api_key
|
|
@@ -4278,6 +4297,353 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
|
|
|
4278
4297
|
return false;
|
|
4279
4298
|
}
|
|
4280
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
|
+
|
|
4281
4647
|
// src/http/agents.ts
|
|
4282
4648
|
init_src2();
|
|
4283
4649
|
init_zod();
|
|
@@ -5651,7 +6017,11 @@ var getAvailable = route({
|
|
|
5651
6017
|
totalKeys: exports_coerce.number().int().min(1),
|
|
5652
6018
|
scope: string2().optional(),
|
|
5653
6019
|
scopeId: string2().optional(),
|
|
5654
|
-
model: _enum2(["fable", "opus", "sonnet", "haiku"]).optional()
|
|
6020
|
+
model: _enum2(["fable", "opus", "sonnet", "haiku"]).optional(),
|
|
6021
|
+
keySuffixes: string2().transform((value) => value.split(",")).optional()
|
|
6022
|
+
}).refine((query) => !query.keySuffixes || query.keySuffixes.length === query.totalKeys, {
|
|
6023
|
+
message: "keySuffixes must list exactly totalKeys suffixes",
|
|
6024
|
+
path: ["keySuffixes"]
|
|
5655
6025
|
}),
|
|
5656
6026
|
responses: {
|
|
5657
6027
|
200: {
|
|
@@ -5986,9 +6356,9 @@ async function handleApiKeys(req, res, pathSegments, queryParams) {
|
|
|
5986
6356
|
const parsed = await getAvailable.parse(req, res, pathSegments, queryParams);
|
|
5987
6357
|
if (!parsed)
|
|
5988
6358
|
return true;
|
|
5989
|
-
const { keyType, totalKeys, scope, scopeId, model } = parsed.query;
|
|
6359
|
+
const { keyType, totalKeys, scope, scopeId, model, keySuffixes } = parsed.query;
|
|
5990
6360
|
try {
|
|
5991
|
-
const result = await getAvailableKeyIndices2(keyType, totalKeys, scope, scopeId ?? null, model);
|
|
6361
|
+
const result = await getAvailableKeyIndices2(keyType, totalKeys, scope, scopeId ?? null, model, keySuffixes);
|
|
5992
6362
|
getAvailable.respond(res, 200, {
|
|
5993
6363
|
success: true,
|
|
5994
6364
|
availableIndices: result.availableIndices,
|
|
@@ -8374,6 +8744,7 @@ var OnboardingStepIdSchema = _enum2([
|
|
|
8374
8744
|
"agents",
|
|
8375
8745
|
"memory",
|
|
8376
8746
|
"integrations",
|
|
8747
|
+
"files",
|
|
8377
8748
|
"first_task"
|
|
8378
8749
|
]);
|
|
8379
8750
|
var OnboardingErrorClassSchema = _enum2([
|
|
@@ -8409,6 +8780,14 @@ var OnboardingMemoryPresetSchema = _enum2([
|
|
|
8409
8780
|
"custom",
|
|
8410
8781
|
"existing"
|
|
8411
8782
|
]);
|
|
8783
|
+
var OnboardingMemoryProviderSchema = _enum2([
|
|
8784
|
+
"local",
|
|
8785
|
+
"openai",
|
|
8786
|
+
"openrouter",
|
|
8787
|
+
"vercel",
|
|
8788
|
+
"azure",
|
|
8789
|
+
"custom"
|
|
8790
|
+
]);
|
|
8412
8791
|
var OnboardingIntegrationMethodSchema = _enum2([
|
|
8413
8792
|
"slack",
|
|
8414
8793
|
"github",
|
|
@@ -8417,6 +8796,7 @@ var OnboardingIntegrationMethodSchema = _enum2([
|
|
|
8417
8796
|
"linear_oauth",
|
|
8418
8797
|
"jira_oauth"
|
|
8419
8798
|
]);
|
|
8799
|
+
var OnboardingFilesMethodSchema = _enum2(["created", "connected"]);
|
|
8420
8800
|
var OnboardingFirstTaskMethodSchema = _enum2(["suggestion", "free_form"]);
|
|
8421
8801
|
var OnboardingStepMethodSchema = union2([
|
|
8422
8802
|
OnboardingConnectMethodSchema,
|
|
@@ -8425,6 +8805,7 @@ var OnboardingStepMethodSchema = union2([
|
|
|
8425
8805
|
OnboardingAgentsMethodSchema,
|
|
8426
8806
|
OnboardingMemoryPresetSchema,
|
|
8427
8807
|
OnboardingIntegrationMethodSchema,
|
|
8808
|
+
OnboardingFilesMethodSchema,
|
|
8428
8809
|
OnboardingFirstTaskMethodSchema
|
|
8429
8810
|
]);
|
|
8430
8811
|
function onboardingStepStateSchema(method) {
|
|
@@ -8446,6 +8827,7 @@ var OnboardingAiStepStateSchema = onboardingStepStateSchema(OnboardingAiMethodSc
|
|
|
8446
8827
|
var OnboardingAgentsStepStateSchema = onboardingStepStateSchema(OnboardingAgentsMethodSchema);
|
|
8447
8828
|
var OnboardingMemoryStepStateSchema = onboardingStepStateSchema(OnboardingMemoryPresetSchema);
|
|
8448
8829
|
var OnboardingIntegrationStepStateSchema = onboardingStepStateSchema(OnboardingIntegrationMethodSchema);
|
|
8830
|
+
var OnboardingFilesStepStateSchema = onboardingStepStateSchema(OnboardingFilesMethodSchema);
|
|
8449
8831
|
var OnboardingFirstTaskStepStateSchema = onboardingStepStateSchema(OnboardingFirstTaskMethodSchema);
|
|
8450
8832
|
var OnboardingStateSchema = object2({
|
|
8451
8833
|
version: literal2(1),
|
|
@@ -8463,6 +8845,7 @@ var OnboardingStateSchema = object2({
|
|
|
8463
8845
|
agents: OnboardingAgentsStepStateSchema,
|
|
8464
8846
|
memory: OnboardingMemoryStepStateSchema,
|
|
8465
8847
|
integrations: OnboardingIntegrationStepStateSchema,
|
|
8848
|
+
files: OnboardingFilesStepStateSchema,
|
|
8466
8849
|
first_task: OnboardingFirstTaskStepStateSchema
|
|
8467
8850
|
})
|
|
8468
8851
|
});
|
|
@@ -8477,7 +8860,11 @@ var OnboardingSignalsSchema = object2({
|
|
|
8477
8860
|
})),
|
|
8478
8861
|
embeddings: object2({
|
|
8479
8862
|
configured: boolean(),
|
|
8480
|
-
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()
|
|
8481
8868
|
}),
|
|
8482
8869
|
integrations: object2({
|
|
8483
8870
|
slack: boolean(),
|
|
@@ -8491,7 +8878,8 @@ var OnboardingSignalsSchema = object2({
|
|
|
8491
8878
|
leadsOnline: number2().int().nonnegative(),
|
|
8492
8879
|
workersOnline: number2().int().nonnegative()
|
|
8493
8880
|
}),
|
|
8494
|
-
firstTask: object2({ id: string2(), status: string2() }).nullable()
|
|
8881
|
+
firstTask: object2({ id: string2(), status: string2() }).nullable(),
|
|
8882
|
+
files: object2({ enabled: boolean() })
|
|
8495
8883
|
});
|
|
8496
8884
|
var OnboardingResponseSchema = object2({
|
|
8497
8885
|
state: OnboardingStateSchema,
|
|
@@ -8524,9 +8912,14 @@ var OnboardingActionSchema = union2([
|
|
|
8524
8912
|
step: literal2("integrations"),
|
|
8525
8913
|
method: OnboardingIntegrationMethodSchema
|
|
8526
8914
|
}).strict(),
|
|
8915
|
+
object2({
|
|
8916
|
+
action: literal2("complete"),
|
|
8917
|
+
step: literal2("files"),
|
|
8918
|
+
method: OnboardingFilesMethodSchema
|
|
8919
|
+
}).strict(),
|
|
8527
8920
|
object2({
|
|
8528
8921
|
action: literal2("skip"),
|
|
8529
|
-
step: _enum2(["name", "ai", "agents", "memory", "integrations", "first_task"])
|
|
8922
|
+
step: _enum2(["name", "ai", "agents", "memory", "integrations", "files", "first_task"])
|
|
8530
8923
|
}).strict(),
|
|
8531
8924
|
object2({
|
|
8532
8925
|
action: literal2("fail"),
|
|
@@ -8555,8 +8948,12 @@ var OnboardingMemoryRequestSchema = object2({
|
|
|
8555
8948
|
baseUrl: OnboardingHttpUrlSchema.optional(),
|
|
8556
8949
|
model: string2().trim().min(1).optional(),
|
|
8557
8950
|
apiKey: string2().min(1).optional(),
|
|
8558
|
-
reuseKey: _enum2(["OPENAI_API_KEY", "OPENROUTER_API_KEY"]).optional()
|
|
8559
|
-
|
|
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
|
+
});
|
|
8560
8957
|
var OnboardingMemoryResponseSchema = object2({
|
|
8561
8958
|
ok: boolean(),
|
|
8562
8959
|
dimensions: number2().int().positive().optional(),
|
|
@@ -8584,6 +8981,7 @@ function freshState(now, autoCompleted) {
|
|
|
8584
8981
|
agents: emptyStep(),
|
|
8585
8982
|
memory: emptyStep(),
|
|
8586
8983
|
integrations: emptyStep(),
|
|
8984
|
+
files: emptyStep(),
|
|
8587
8985
|
first_task: emptyStep()
|
|
8588
8986
|
}
|
|
8589
8987
|
};
|
|
@@ -8742,6 +9140,14 @@ function deriveState(state, signals, funnelEvents) {
|
|
|
8742
9140
|
}
|
|
8743
9141
|
return changed;
|
|
8744
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
|
+
}
|
|
8745
9151
|
|
|
8746
9152
|
class OnboardingTaskNotFoundError extends Error {
|
|
8747
9153
|
}
|
|
@@ -8811,7 +9217,7 @@ async function readOrUpdateOnboarding(loadSignals, action) {
|
|
|
8811
9217
|
readOnly: true
|
|
8812
9218
|
});
|
|
8813
9219
|
if (!read.dirty)
|
|
8814
|
-
return { state: read.state, signals: read.signals };
|
|
9220
|
+
return { state: presentState(read.state, read.signals), signals: read.signals };
|
|
8815
9221
|
}
|
|
8816
9222
|
return await client.transaction(async () => {
|
|
8817
9223
|
const events = [];
|
|
@@ -8826,20 +9232,30 @@ async function readOrUpdateOnboarding(loadSignals, action) {
|
|
|
8826
9232
|
if (dirty)
|
|
8827
9233
|
await persistState(state);
|
|
8828
9234
|
queueTelemetry(events);
|
|
8829
|
-
return { state, signals };
|
|
9235
|
+
return { state: presentState(state, signals), signals };
|
|
8830
9236
|
});
|
|
8831
9237
|
}
|
|
8832
|
-
|
|
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) {
|
|
8833
9249
|
return await getDbClient2().transaction(async () => {
|
|
8834
9250
|
const events = [];
|
|
8835
9251
|
const loaded = await loadState(events);
|
|
8836
9252
|
let dirty = loaded.dirty;
|
|
8837
9253
|
const eventsForState = loaded.state.autoCompleted ? [] : events;
|
|
8838
|
-
if (
|
|
9254
|
+
if (change?.kind === "save") {
|
|
8839
9255
|
const configValues = [
|
|
8840
|
-
["EMBEDDING_API_BASE_URL",
|
|
8841
|
-
["EMBEDDING_MODEL",
|
|
8842
|
-
...
|
|
9256
|
+
["EMBEDDING_API_BASE_URL", change.baseUrl],
|
|
9257
|
+
["EMBEDDING_MODEL", change.model],
|
|
9258
|
+
...change.apiKey ? [["EMBEDDING_API_KEY", change.apiKey]] : []
|
|
8843
9259
|
];
|
|
8844
9260
|
for (const [key, value] of configValues) {
|
|
8845
9261
|
const validationError = validateConfigValue(key, value);
|
|
@@ -8849,17 +9265,26 @@ async function updateOnboardingMemory(preset, result, saveConfig) {
|
|
|
8849
9265
|
await upsertSwarmConfig2({
|
|
8850
9266
|
scope: "global",
|
|
8851
9267
|
key: "EMBEDDING_API_BASE_URL",
|
|
8852
|
-
value:
|
|
9268
|
+
value: change.baseUrl
|
|
8853
9269
|
});
|
|
8854
|
-
await upsertSwarmConfig2({ scope: "global", key: "EMBEDDING_MODEL", value:
|
|
8855
|
-
if (
|
|
9270
|
+
await upsertSwarmConfig2({ scope: "global", key: "EMBEDDING_MODEL", value: change.model });
|
|
9271
|
+
if (change.apiKey) {
|
|
8856
9272
|
await upsertSwarmConfig2({
|
|
8857
9273
|
scope: "global",
|
|
8858
9274
|
key: "EMBEDDING_API_KEY",
|
|
8859
|
-
value:
|
|
9275
|
+
value: change.apiKey,
|
|
8860
9276
|
isSecret: true
|
|
8861
9277
|
});
|
|
8862
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);
|
|
8863
9288
|
}
|
|
8864
9289
|
if (result.ok) {
|
|
8865
9290
|
dirty = completeStep(loaded.state, "memory", preset, false, eventsForState) || dirty;
|
|
@@ -8869,7 +9294,7 @@ async function updateOnboardingMemory(preset, result, saveConfig) {
|
|
|
8869
9294
|
if (dirty)
|
|
8870
9295
|
await persistState(loaded.state);
|
|
8871
9296
|
queueTelemetry(events);
|
|
8872
|
-
return
|
|
9297
|
+
return change !== undefined;
|
|
8873
9298
|
});
|
|
8874
9299
|
}
|
|
8875
9300
|
async function updateOnboardingAiFromCodexDevice(result) {
|
|
@@ -9143,6 +9568,9 @@ async function completeFlow(flowId, credentials) {
|
|
|
9143
9568
|
isSecret: true,
|
|
9144
9569
|
description: `Codex ChatGPT OAuth credentials slot ${slot} (stored by dashboard device login)`
|
|
9145
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 });
|
|
9146
9574
|
await updateOnboardingAiFromCodexDevice({ status: "complete" });
|
|
9147
9575
|
state.status = "complete";
|
|
9148
9576
|
state.slot = slot;
|
|
@@ -14031,6 +14459,7 @@ ${block}`, new Date().toISOString(), taskId]);
|
|
|
14031
14459
|
}
|
|
14032
14460
|
|
|
14033
14461
|
// src/http/fs.ts
|
|
14462
|
+
init_rbac_roles();
|
|
14034
14463
|
init_rbac();
|
|
14035
14464
|
init_types();
|
|
14036
14465
|
|
|
@@ -14076,7 +14505,11 @@ var agentFsCredentialStateSchema = object2({
|
|
|
14076
14505
|
});
|
|
14077
14506
|
var agentFsInviteStateSchema = object2({
|
|
14078
14507
|
orgId: string2(),
|
|
14079
|
-
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.")
|
|
14080
14513
|
});
|
|
14081
14514
|
var signedUrlResponseSchema = object2({
|
|
14082
14515
|
url: string2(),
|
|
@@ -14116,21 +14549,23 @@ var inviteMemberRoute = route({
|
|
|
14116
14549
|
path: "/api/fs/members/invite",
|
|
14117
14550
|
pattern: ["api", "fs", "members", "invite"],
|
|
14118
14551
|
summary: "Invite an external member into the agent-fs shared org",
|
|
14119
|
-
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.",
|
|
14120
14553
|
tags: ["FS"],
|
|
14121
14554
|
body: object2({
|
|
14122
14555
|
email: email(),
|
|
14123
14556
|
role: _enum2(["viewer", "editor", "admin"]).default("editor")
|
|
14124
14557
|
}),
|
|
14125
14558
|
responses: {
|
|
14126
|
-
200: {
|
|
14559
|
+
200: {
|
|
14560
|
+
description: "Invite state ({ orgId, invited, status, acceptUrl? })",
|
|
14561
|
+
schema: agentFsInviteStateSchema
|
|
14562
|
+
},
|
|
14127
14563
|
400: { description: "Invalid body" },
|
|
14564
|
+
403: { description: "A non-admin user token named an email other than its own" },
|
|
14128
14565
|
500: { description: "Provisioning or invite failed" }
|
|
14129
14566
|
},
|
|
14130
14567
|
auth: { apiKey: true },
|
|
14131
|
-
rbac: {
|
|
14132
|
-
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."
|
|
14133
|
-
}
|
|
14568
|
+
rbac: { permission: "fs.join.own" }
|
|
14134
14569
|
});
|
|
14135
14570
|
var listTaskFilesRoute = route({
|
|
14136
14571
|
method: "get",
|
|
@@ -14229,6 +14664,27 @@ var deleteTaskFileRoute = route({
|
|
|
14229
14664
|
auth: { apiKey: true, agentId: true },
|
|
14230
14665
|
rbac: { permission: "task.fs.mutate" }
|
|
14231
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
|
+
}
|
|
14232
14688
|
async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
14233
14689
|
if (ensureAgentCredentialsRoute.match(req.method, pathSegments)) {
|
|
14234
14690
|
const parsed = await ensureAgentCredentialsRoute.parse(req, res, pathSegments, queryParams);
|
|
@@ -14253,8 +14709,13 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
14253
14709
|
const parsed = await inviteMemberRoute.parse(req, res, pathSegments, queryParams);
|
|
14254
14710
|
if (!parsed)
|
|
14255
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
|
+
}
|
|
14256
14717
|
try {
|
|
14257
|
-
const result = await inviteEmailToSharedOrg(
|
|
14718
|
+
const result = await inviteEmailToSharedOrg(invite.email, invite.role);
|
|
14258
14719
|
inviteMemberRoute.respond(res, 200, result);
|
|
14259
14720
|
} catch (error) {
|
|
14260
14721
|
const message = scrubSecrets(error instanceof Error ? error.message : String(error));
|
|
@@ -14591,7 +15052,10 @@ async function handleHeartbeat(req, res, pathSegments) {
|
|
|
14591
15052
|
// src/http/inbox-state.ts
|
|
14592
15053
|
init_zod();
|
|
14593
15054
|
init_db3();
|
|
15055
|
+
init_rbac_roles();
|
|
15056
|
+
init_rbac();
|
|
14594
15057
|
init_types();
|
|
15058
|
+
init_request_auth_context();
|
|
14595
15059
|
var listState = route({
|
|
14596
15060
|
method: "get",
|
|
14597
15061
|
path: "/api/inbox-state",
|
|
@@ -14634,9 +15098,11 @@ var upsertState = route({
|
|
|
14634
15098
|
schema: object2({ item: InboxItemStateSchema })
|
|
14635
15099
|
},
|
|
14636
15100
|
400: { description: "Validation error" },
|
|
14637
|
-
401: { description: "Unauthorized" }
|
|
15101
|
+
401: { description: "Unauthorized" },
|
|
15102
|
+
403: { description: "A non-admin user token wrote another user's state" }
|
|
14638
15103
|
},
|
|
14639
|
-
auth: { apiKey: true }
|
|
15104
|
+
auth: { apiKey: true },
|
|
15105
|
+
rbac: { permission: "favorite.write.own" }
|
|
14640
15106
|
});
|
|
14641
15107
|
var bulkUpsertState = route({
|
|
14642
15108
|
method: "patch",
|
|
@@ -14654,11 +15120,19 @@ var bulkUpsertState = route({
|
|
|
14654
15120
|
schema: object2({ items: array2(InboxItemStateSchema) })
|
|
14655
15121
|
},
|
|
14656
15122
|
400: { description: "Validation error" },
|
|
14657
|
-
401: { description: "Unauthorized" }
|
|
15123
|
+
401: { description: "Unauthorized" },
|
|
15124
|
+
403: { description: "A non-admin user token wrote another user's state" }
|
|
14658
15125
|
},
|
|
14659
15126
|
auth: { apiKey: true },
|
|
14660
|
-
rbac: {
|
|
15127
|
+
rbac: { permission: "favorite.write.own" }
|
|
14661
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
|
+
}
|
|
14662
15136
|
async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
14663
15137
|
if (listState.match(req.method, pathSegments)) {
|
|
14664
15138
|
const parsed = await listState.parse(req, res, pathSegments, queryParams);
|
|
@@ -14676,6 +15150,10 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
|
14676
15150
|
const parsed = await bulkUpsertState.parse(req, res, pathSegments, queryParams);
|
|
14677
15151
|
if (!parsed)
|
|
14678
15152
|
return true;
|
|
15153
|
+
if (await writesOtherUsersState(req, parsed.body.userId)) {
|
|
15154
|
+
jsonError(res, OTHER_USER_STATE_DENIED, 403);
|
|
15155
|
+
return true;
|
|
15156
|
+
}
|
|
14679
15157
|
try {
|
|
14680
15158
|
const items = await getDbClient2().transaction(async () => {
|
|
14681
15159
|
const rows = [];
|
|
@@ -14694,6 +15172,10 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
|
14694
15172
|
const parsed = await upsertState.parse(req, res, pathSegments, queryParams);
|
|
14695
15173
|
if (!parsed)
|
|
14696
15174
|
return true;
|
|
15175
|
+
if (await writesOtherUsersState(req, parsed.body.userId)) {
|
|
15176
|
+
jsonError(res, OTHER_USER_STATE_DENIED, 403);
|
|
15177
|
+
return true;
|
|
15178
|
+
}
|
|
14697
15179
|
try {
|
|
14698
15180
|
const item = await upsertInboxState2({
|
|
14699
15181
|
userId: parsed.body.userId,
|
|
@@ -15815,7 +16297,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
15815
16297
|
// node_modules/@hono/node-server/dist/index.mjs
|
|
15816
16298
|
import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
|
|
15817
16299
|
import { Http2ServerRequest } from "http2";
|
|
15818
|
-
import { Readable } from "stream";
|
|
16300
|
+
import { Readable as Readable2 } from "stream";
|
|
15819
16301
|
import crypto2 from "crypto";
|
|
15820
16302
|
var RequestError = class extends Error {
|
|
15821
16303
|
constructor(message, options) {
|
|
@@ -15882,7 +16364,7 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
|
|
|
15882
16364
|
init.body = new ReadableStream({
|
|
15883
16365
|
async pull(controller) {
|
|
15884
16366
|
try {
|
|
15885
|
-
reader ||=
|
|
16367
|
+
reader ||= Readable2.toWeb(incoming).getReader();
|
|
15886
16368
|
const { done, value } = await reader.read();
|
|
15887
16369
|
if (done) {
|
|
15888
16370
|
controller.close();
|
|
@@ -15895,7 +16377,7 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
|
|
|
15895
16377
|
}
|
|
15896
16378
|
});
|
|
15897
16379
|
} else {
|
|
15898
|
-
init.body =
|
|
16380
|
+
init.body = Readable2.toWeb(incoming);
|
|
15899
16381
|
}
|
|
15900
16382
|
}
|
|
15901
16383
|
return new Request2(url, init);
|
|
@@ -19795,7 +20277,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
19795
20277
|
try {
|
|
19796
20278
|
const provider = getEmbeddingProvider2();
|
|
19797
20279
|
const store = getMemoryStore2();
|
|
19798
|
-
const queryEmbedding = await provider.embed(query);
|
|
20280
|
+
const queryEmbedding = await provider.embed(query, { kind: "query" });
|
|
19799
20281
|
const candidateLimit = Math.min(limit, 20) * CANDIDATE_SET_MULTIPLIER;
|
|
19800
20282
|
const candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), myAgentId, {
|
|
19801
20283
|
scope,
|
|
@@ -19876,7 +20358,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
19876
20358
|
try {
|
|
19877
20359
|
if (query && query.trim().length > 0) {
|
|
19878
20360
|
const provider = getEmbeddingProvider2();
|
|
19879
|
-
const queryEmbedding = await provider.embed(query.trim());
|
|
20361
|
+
const queryEmbedding = await provider.embed(query.trim(), { kind: "query" });
|
|
19880
20362
|
const candidateLimit = Math.min(4096, Math.max(offset + pageLimit, pageLimit) * CANDIDATE_SET_MULTIPLIER);
|
|
19881
20363
|
let candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), viewer.agentId, {
|
|
19882
20364
|
scope,
|
|
@@ -20091,7 +20573,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
20091
20573
|
});
|
|
20092
20574
|
if (result.changed) {
|
|
20093
20575
|
const provider = getEmbeddingProvider2();
|
|
20094
|
-
const embedding = await provider.embed(result.memory.content);
|
|
20576
|
+
const embedding = await provider.embed(result.memory.content, { kind: "document" });
|
|
20095
20577
|
if (embedding)
|
|
20096
20578
|
await store.updateEmbedding(result.memory.id, embedding, provider.name);
|
|
20097
20579
|
try {
|
|
@@ -20169,7 +20651,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
|
|
|
20169
20651
|
for (let i = 0;i < memories.length; i += batchSize) {
|
|
20170
20652
|
const batch = memories.slice(i, i + batchSize);
|
|
20171
20653
|
try {
|
|
20172
|
-
const embeddings = await provider.embedBatch(batch.map((m) => m.content));
|
|
20654
|
+
const embeddings = await provider.embedBatch(batch.map((m) => m.content), { kind: "document" });
|
|
20173
20655
|
for (let j = 0;j < embeddings.length; j++) {
|
|
20174
20656
|
if (embeddings[j]) {
|
|
20175
20657
|
await store.updateEmbedding(batch[j].id, embeddings[j], provider.name);
|
|
@@ -21900,7 +22382,9 @@ init_db3();
|
|
|
21900
22382
|
init_memory();
|
|
21901
22383
|
init_constants();
|
|
21902
22384
|
init_types();
|
|
22385
|
+
init_constants2();
|
|
21903
22386
|
init_credentials();
|
|
22387
|
+
init_egress_guard();
|
|
21904
22388
|
init_openrouter_base_url();
|
|
21905
22389
|
|
|
21906
22390
|
// src/http/status.ts
|
|
@@ -22492,6 +22976,58 @@ async function handleStatus(req, res, pathSegments, queryParams) {
|
|
|
22492
22976
|
}
|
|
22493
22977
|
|
|
22494
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
|
+
}
|
|
22495
23031
|
async function buildSignals(state) {
|
|
22496
23032
|
const providers = [];
|
|
22497
23033
|
for (const entry of await getAgentHarnessProviders2()) {
|
|
@@ -22512,11 +23048,16 @@ async function buildSignals(state) {
|
|
|
22512
23048
|
const integrations = await getAutomationSetupStates();
|
|
22513
23049
|
const agentCounts = await getReadyAgentCounts2();
|
|
22514
23050
|
const firstTask = state.firstTaskId ? await getTaskById2(state.firstTaskId) : null;
|
|
23051
|
+
const embeddingsOn = getEmbeddingProvider2().isConfigured();
|
|
22515
23052
|
return {
|
|
22516
23053
|
providers,
|
|
22517
23054
|
embeddings: {
|
|
22518
|
-
configured:
|
|
22519
|
-
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()
|
|
22520
23061
|
},
|
|
22521
23062
|
integrations: {
|
|
22522
23063
|
slack: integrations.slack === "configured",
|
|
@@ -22530,7 +23071,8 @@ async function buildSignals(state) {
|
|
|
22530
23071
|
leadsOnline: agentCounts.leads,
|
|
22531
23072
|
workersOnline: agentCounts.workers
|
|
22532
23073
|
},
|
|
22533
|
-
firstTask: firstTask ? { id: firstTask.id, status: firstTask.status } : null
|
|
23074
|
+
firstTask: firstTask ? { id: firstTask.id, status: firstTask.status } : null,
|
|
23075
|
+
files: { enabled: isCombEnabled() }
|
|
22534
23076
|
};
|
|
22535
23077
|
}
|
|
22536
23078
|
var getOnboarding = route({
|
|
@@ -22627,19 +23169,34 @@ function memoryErrorMessage(errorClass, status) {
|
|
|
22627
23169
|
return `The endpoint returned an error${statusSuffix}.`;
|
|
22628
23170
|
}
|
|
22629
23171
|
}
|
|
22630
|
-
async function updateMemoryState(preset, result,
|
|
22631
|
-
const
|
|
22632
|
-
if (
|
|
22633
|
-
|
|
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
|
+
}));
|
|
22634
23188
|
}
|
|
22635
23189
|
async function handleMemoryProbe(body) {
|
|
22636
23190
|
const explicitKey = body.apiKey;
|
|
22637
23191
|
const preset = body.preset === "openai" || body.preset === "openrouter" || body.preset === "vercel" ? MEMORY_PRESETS[body.preset] : undefined;
|
|
22638
|
-
const
|
|
22639
|
-
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");
|
|
22640
23197
|
let endpoint;
|
|
22641
23198
|
try {
|
|
22642
|
-
endpoint = assertUrlSafe(baseUrl ?? "", publicEndpointSsrfOptions());
|
|
23199
|
+
endpoint = configuredEndpoint ? new URL(baseUrl ?? "") : assertUrlSafe(baseUrl ?? "", publicEndpointSsrfOptions());
|
|
22643
23200
|
} catch {
|
|
22644
23201
|
return {
|
|
22645
23202
|
ok: false,
|
|
@@ -22651,7 +23208,7 @@ async function handleMemoryProbe(body) {
|
|
|
22651
23208
|
const reuseHost = body.reuseKey === "OPENAI_API_KEY" ? "api.openai.com" : body.reuseKey === "OPENROUTER_API_KEY" ? "openrouter.ai" : undefined;
|
|
22652
23209
|
const canReuseKey = reuseHost === undefined || endpoint.hostname === reuseHost;
|
|
22653
23210
|
const reusedKey = !explicitKey && body.reuseKey && canReuseKey ? process.env[body.reuseKey] : undefined;
|
|
22654
|
-
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;
|
|
22655
23212
|
const apiKey = splitCredentialPool(explicitKey ?? reusedKey ?? implicitKey ?? "")[0];
|
|
22656
23213
|
if (!apiKey) {
|
|
22657
23214
|
return {
|
|
@@ -22689,10 +23246,12 @@ async function handleMemoryProbe(body) {
|
|
|
22689
23246
|
};
|
|
22690
23247
|
}
|
|
22691
23248
|
const keyToSave = explicitKey || reusedKey ? apiKey : undefined;
|
|
22692
|
-
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",
|
|
22693
23251
|
baseUrl: baseUrl ?? "https://api.openai.com/v1",
|
|
22694
23252
|
model,
|
|
22695
|
-
apiKey: keyToSave
|
|
23253
|
+
apiKey: keyToSave,
|
|
23254
|
+
prefixes: prefixChanges(model)
|
|
22696
23255
|
});
|
|
22697
23256
|
return { ok: true, dimensions, latencyMs };
|
|
22698
23257
|
} catch (error) {
|
|
@@ -22971,6 +23530,7 @@ init_request_auth_context();
|
|
|
22971
23530
|
init_zod();
|
|
22972
23531
|
init_db3();
|
|
22973
23532
|
init_model_profiles();
|
|
23533
|
+
init_rbac_roles();
|
|
22974
23534
|
init_steering();
|
|
22975
23535
|
init_users();
|
|
22976
23536
|
init_rbac();
|
|
@@ -23147,6 +23707,7 @@ var createTask = route({
|
|
|
23147
23707
|
path: "/api/tasks",
|
|
23148
23708
|
pattern: ["api", "tasks"],
|
|
23149
23709
|
session: bodyTask("parentTaskId"),
|
|
23710
|
+
rbac: { permission: "task.create.own" },
|
|
23150
23711
|
summary: "Create a new task",
|
|
23151
23712
|
tags: ["Tasks"],
|
|
23152
23713
|
body: object2({
|
|
@@ -23253,6 +23814,7 @@ var cancelTaskRoute = route({
|
|
|
23253
23814
|
path: "/api/tasks/{id}/cancel",
|
|
23254
23815
|
pattern: ["api", "tasks", null, "cancel"],
|
|
23255
23816
|
session: { taskParam: "id" },
|
|
23817
|
+
rbac: { permission: "task.cancel.own" },
|
|
23256
23818
|
summary: "Cancel a pending or in-progress task",
|
|
23257
23819
|
tags: ["Tasks"],
|
|
23258
23820
|
params: object2({ id: string2() }),
|
|
@@ -23503,6 +24065,7 @@ var pauseTaskRoute = route({
|
|
|
23503
24065
|
path: "/api/tasks/{id}/pause",
|
|
23504
24066
|
pattern: ["api", "tasks", null, "pause"],
|
|
23505
24067
|
session: { taskParam: "id" },
|
|
24068
|
+
rbac: { permission: "task.action.own" },
|
|
23506
24069
|
summary: "Pause an in-progress task",
|
|
23507
24070
|
tags: ["Tasks"],
|
|
23508
24071
|
params: object2({ id: string2() }),
|
|
@@ -23518,6 +24081,7 @@ var resumeTaskRoute = route({
|
|
|
23518
24081
|
path: "/api/tasks/{id}/resume",
|
|
23519
24082
|
pattern: ["api", "tasks", null, "resume"],
|
|
23520
24083
|
session: { taskParam: "id" },
|
|
24084
|
+
rbac: { permission: "task.action.own" },
|
|
23521
24085
|
summary: "Resume a paused task",
|
|
23522
24086
|
tags: ["Tasks"],
|
|
23523
24087
|
params: object2({ id: string2() }),
|
|
@@ -23587,9 +24151,7 @@ var updateTaskTitleRoute = route({
|
|
|
23587
24151
|
404: { description: "Task not found" }
|
|
23588
24152
|
},
|
|
23589
24153
|
auth: { apiKey: true },
|
|
23590
|
-
rbac: {
|
|
23591
|
-
ungated: "mirrors the pre-RBAC PATCH /api/tasks/{id}/vcs sibling route: bearer auth only"
|
|
23592
|
-
}
|
|
24154
|
+
rbac: { permission: "task.action.own" }
|
|
23593
24155
|
});
|
|
23594
24156
|
async function canSteerTask(req, myAgentId, task, sessionAccess = "none") {
|
|
23595
24157
|
const auth = getRequestAuth(req);
|
|
@@ -23659,6 +24221,34 @@ async function resolveTaskWritePrincipal(req, myAgentId) {
|
|
|
23659
24221
|
const agent = await getAgentById2(agentId);
|
|
23660
24222
|
return { kind: "agent", agentId, isLead: agent?.isLead === true };
|
|
23661
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
|
+
}
|
|
23662
24252
|
async function resolveTaskRequester(req, myAgentId, bodyUserId, trustedUserId) {
|
|
23663
24253
|
const trusted = trustedUserId === undefined ? await resolveHttpAuditUserId(req, myAgentId) : trustedUserId;
|
|
23664
24254
|
if (trusted)
|
|
@@ -23774,8 +24364,12 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23774
24364
|
if (parentTask && await denyParentAttach2(req, res, myAgentId, parentTask, principal)) {
|
|
23775
24365
|
return true;
|
|
23776
24366
|
}
|
|
23777
|
-
if (
|
|
23778
|
-
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);
|
|
23779
24373
|
return true;
|
|
23780
24374
|
}
|
|
23781
24375
|
let assetKey;
|
|
@@ -23895,6 +24489,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23895
24489
|
jsonError(res, "Task not found", 404);
|
|
23896
24490
|
return true;
|
|
23897
24491
|
}
|
|
24492
|
+
if (await deniesNonOwnerTaskWrite2(req, task)) {
|
|
24493
|
+
jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
|
|
24494
|
+
return true;
|
|
24495
|
+
}
|
|
23898
24496
|
const principal = await resolveTaskWritePrincipal(req, myAgentId);
|
|
23899
24497
|
if (principal.kind === "agent" && !can({
|
|
23900
24498
|
principal,
|
|
@@ -24386,6 +24984,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24386
24984
|
jsonError(res, "Task not found", 404);
|
|
24387
24985
|
return true;
|
|
24388
24986
|
}
|
|
24987
|
+
if (await deniesNonOwnerTaskWrite2(req, task)) {
|
|
24988
|
+
jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
|
|
24989
|
+
return true;
|
|
24990
|
+
}
|
|
24389
24991
|
if (myAgentId && task.agentId !== myAgentId) {
|
|
24390
24992
|
jsonError(res, "Task belongs to another agent", 403);
|
|
24391
24993
|
return true;
|
|
@@ -24446,6 +25048,15 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24446
25048
|
const parsed = await updateTaskTitleRoute.parse(req, res, pathSegments, queryParams);
|
|
24447
25049
|
if (!parsed)
|
|
24448
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
|
+
}
|
|
24449
25060
|
const task = await updateTaskTitle2(parsed.params.id, parsed.body.title);
|
|
24450
25061
|
if (!task) {
|
|
24451
25062
|
jsonError(res, "Task not found", 404);
|
|
@@ -24463,6 +25074,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24463
25074
|
jsonError(res, "Task not found", 404);
|
|
24464
25075
|
return true;
|
|
24465
25076
|
}
|
|
25077
|
+
if (await deniesNonOwnerTaskWrite2(req, task)) {
|
|
25078
|
+
jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
|
|
25079
|
+
return true;
|
|
25080
|
+
}
|
|
24466
25081
|
if (myAgentId && task.agentId !== myAgentId) {
|
|
24467
25082
|
jsonError(res, "Task belongs to another agent", 403);
|
|
24468
25083
|
return true;
|
|
@@ -26840,7 +27455,26 @@ var LOCAL_PAGE_APP_URL = "http://localhost:5274";
|
|
|
26840
27455
|
function getAppBaseUrl2() {
|
|
26841
27456
|
return getAppUrl(LOCAL_PAGE_APP_URL);
|
|
26842
27457
|
}
|
|
26843
|
-
|
|
27458
|
+
var FRAME_ANCESTOR_SOURCE_RE = /^(?:https?:\/\/)?(?:\*\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*(?::(?:\d{1,5}|\*))?$/i;
|
|
27459
|
+
var cachedFrameAncestors = null;
|
|
27460
|
+
function getPageFrameAncestors() {
|
|
27461
|
+
const raw = process.env.PAGES_FRAME_ANCESTORS ?? "";
|
|
27462
|
+
if (cachedFrameAncestors?.raw === raw)
|
|
27463
|
+
return cachedFrameAncestors.origins;
|
|
27464
|
+
const origins = [];
|
|
27465
|
+
for (const entry of raw.split(/[\s,]+/)) {
|
|
27466
|
+
const value = entry.replace(/\/+$/, "");
|
|
27467
|
+
if (!value)
|
|
27468
|
+
continue;
|
|
27469
|
+
if (FRAME_ANCESTOR_SOURCE_RE.test(value))
|
|
27470
|
+
origins.push(value);
|
|
27471
|
+
else
|
|
27472
|
+
console.warn(`[pages] ignoring invalid PAGES_FRAME_ANCESTORS entry: ${entry}`);
|
|
27473
|
+
}
|
|
27474
|
+
cachedFrameAncestors = { raw, origins };
|
|
27475
|
+
return origins;
|
|
27476
|
+
}
|
|
27477
|
+
function buildCsp(page) {
|
|
26844
27478
|
const configured = getConfiguredAppUrls();
|
|
26845
27479
|
const devFallbacks = getNodeEnv() === "production" ? [] : [
|
|
26846
27480
|
"http://localhost:5274",
|
|
@@ -26850,7 +27484,8 @@ function buildCsp() {
|
|
|
26850
27484
|
"https://*.localhost",
|
|
26851
27485
|
"http://*.localhost"
|
|
26852
27486
|
];
|
|
26853
|
-
const
|
|
27487
|
+
const embedders = page.authMode === "public" ? getPageFrameAncestors() : [];
|
|
27488
|
+
const ancestors = Array.from(new Set([...configured, ...devFallbacks, ...embedders]));
|
|
26854
27489
|
return [
|
|
26855
27490
|
"default-src 'self'",
|
|
26856
27491
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com https://cdn.jsdelivr.net https://unpkg.com",
|
|
@@ -27016,7 +27651,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
27016
27651
|
const headers = {
|
|
27017
27652
|
"Content-Type": "text/html; charset=utf-8",
|
|
27018
27653
|
"Cache-Control": "no-store",
|
|
27019
|
-
"Content-Security-Policy": buildCsp(),
|
|
27654
|
+
"Content-Security-Policy": buildCsp(page),
|
|
27020
27655
|
"X-Content-Type-Options": "nosniff"
|
|
27021
27656
|
};
|
|
27022
27657
|
if (inlineSetCookie.length > 0)
|
|
@@ -27040,7 +27675,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
27040
27675
|
const headers = {
|
|
27041
27676
|
"Content-Type": "text/html; charset=utf-8",
|
|
27042
27677
|
"Cache-Control": "no-store",
|
|
27043
|
-
"Content-Security-Policy": buildCsp(),
|
|
27678
|
+
"Content-Security-Policy": buildCsp(page),
|
|
27044
27679
|
"X-Content-Type-Options": "nosniff"
|
|
27045
27680
|
};
|
|
27046
27681
|
if (inlineSetCookie.length > 0)
|
|
@@ -32852,7 +33487,8 @@ var upsertBodySchema = object2({
|
|
|
32852
33487
|
description: string2().default(""),
|
|
32853
33488
|
intent: string2().default(""),
|
|
32854
33489
|
scope: ScriptScopeSchema.default("agent"),
|
|
32855
|
-
fsMode: ScriptFsModeSchema.default("none")
|
|
33490
|
+
fsMode: ScriptFsModeSchema.default("none"),
|
|
33491
|
+
executor: ScriptExecutorNameSchema.nullable().optional()
|
|
32856
33492
|
});
|
|
32857
33493
|
var runBodySchema = object2({
|
|
32858
33494
|
name: scriptNameSchema.optional(),
|
|
@@ -32861,6 +33497,7 @@ var runBodySchema = object2({
|
|
|
32861
33497
|
intent: string2().default(""),
|
|
32862
33498
|
scope: ScriptScopeSchema.optional(),
|
|
32863
33499
|
fsMode: ScriptFsModeSchema.default("none"),
|
|
33500
|
+
executor: ScriptExecutorNameSchema.nullable().optional(),
|
|
32864
33501
|
idempotencyKey: string2().max(200).optional()
|
|
32865
33502
|
}).refine((body) => Boolean(body.name) !== Boolean(body.source), {
|
|
32866
33503
|
message: "Provide exactly one of name or source"
|
|
@@ -33309,6 +33946,10 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
33309
33946
|
return true;
|
|
33310
33947
|
}
|
|
33311
33948
|
}
|
|
33949
|
+
if (parsed.body.executor && !isExecutorAllowedByDeployment(parsed.body.executor)) {
|
|
33950
|
+
jsonError(res, 'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"', 400);
|
|
33951
|
+
return true;
|
|
33952
|
+
}
|
|
33312
33953
|
const secretRefusal = refuseSourceWithSecrets(parsed.body.source);
|
|
33313
33954
|
if (secretRefusal) {
|
|
33314
33955
|
json(res, secretRefusal, 400);
|
|
@@ -33336,6 +33977,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
33336
33977
|
signatureJson: signatureJsonFor(parsed.body.source),
|
|
33337
33978
|
argsJsonSchema,
|
|
33338
33979
|
fsMode: parsed.body.fsMode,
|
|
33980
|
+
executor: parsed.body.executor,
|
|
33339
33981
|
agentId: agent.id,
|
|
33340
33982
|
isScratch: false,
|
|
33341
33983
|
typeChecked: true,
|
|
@@ -33373,6 +34015,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
33373
34015
|
}
|
|
33374
34016
|
let source = parsed.body.source;
|
|
33375
34017
|
let fsMode = parsed.body.fsMode;
|
|
34018
|
+
let executor = parsed.body.executor ?? null;
|
|
33376
34019
|
let namedScript = null;
|
|
33377
34020
|
if (parsed.body.name) {
|
|
33378
34021
|
const script = await resolveScript(parsed.body.name, agent.id, parsed.body.scope);
|
|
@@ -33383,11 +34026,16 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
33383
34026
|
namedScript = script;
|
|
33384
34027
|
source = script.source;
|
|
33385
34028
|
fsMode = script.fsMode;
|
|
34029
|
+
executor = parsed.body.executor !== undefined ? parsed.body.executor : script.executor;
|
|
33386
34030
|
}
|
|
33387
34031
|
if (fsMode === "workspace-rw") {
|
|
33388
34032
|
jsonError(res, "workspace-rw scripts are not supported by /api/scripts/run in v1", 501);
|
|
33389
34033
|
return true;
|
|
33390
34034
|
}
|
|
34035
|
+
if (executor && !isExecutorAllowedByDeployment(executor)) {
|
|
34036
|
+
jsonError(res, 'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"', 400);
|
|
34037
|
+
return true;
|
|
34038
|
+
}
|
|
33391
34039
|
const startedAt = new Date().toISOString();
|
|
33392
34040
|
const runStartTouch = namedScript?.isScratch ? await touchScratchScriptLastUsed(namedScript.id) : null;
|
|
33393
34041
|
const credentials = await buildScriptCredentialBindingsWithFailures({ agentId: agent.id });
|
|
@@ -33395,6 +34043,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
33395
34043
|
source,
|
|
33396
34044
|
args: parsed.body.args,
|
|
33397
34045
|
fsMode,
|
|
34046
|
+
executor,
|
|
33398
34047
|
agentId: agent.id,
|
|
33399
34048
|
runtimeInstanceId,
|
|
33400
34049
|
egressSecrets: credentials.egressSecrets,
|
|
@@ -33523,6 +34172,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
33523
34172
|
isScratch: script.isScratch,
|
|
33524
34173
|
typeChecked: script.typeChecked,
|
|
33525
34174
|
fsMode: script.fsMode,
|
|
34175
|
+
executor: script.executor,
|
|
33526
34176
|
createdByAgentId: script.createdByAgentId,
|
|
33527
34177
|
createdAt: script.createdAt,
|
|
33528
34178
|
updatedAt: script.updatedAt
|
|
@@ -34775,7 +35425,7 @@ var triageSessionListRoute = route({
|
|
|
34775
35425
|
401: { description: "Unauthorized" }
|
|
34776
35426
|
},
|
|
34777
35427
|
auth: { apiKey: true },
|
|
34778
|
-
rbac: {
|
|
35428
|
+
rbac: { permission: "favorite.write.own" }
|
|
34779
35429
|
});
|
|
34780
35430
|
var SESSION_MOVE_LIMIT = 200;
|
|
34781
35431
|
var SESSION_MOVE_OWNER_ONLY = "Only the session owner can move this group";
|
|
@@ -34784,9 +35434,7 @@ var SessionMoveResultSchema = object2({
|
|
|
34784
35434
|
moved: array2(string2()),
|
|
34785
35435
|
skipped: array2(object2({ rootTaskId: string2(), reason: string2() }))
|
|
34786
35436
|
});
|
|
34787
|
-
var sessionMoveRbac = {
|
|
34788
|
-
ungated: "per-session access, per-task task.fs.mutate, and asset-key write checks in the handler, as PATCH /api/assets/task/{id}/key"
|
|
34789
|
-
};
|
|
35437
|
+
var sessionMoveRbac = { permission: "task.fs.mutate" };
|
|
34790
35438
|
var sessionMoveResponses = {
|
|
34791
35439
|
400: { description: "Invalid namespace or request" },
|
|
34792
35440
|
403: { description: "Personal destination not authorized" },
|
|
@@ -34858,6 +35506,8 @@ async function checkSessionMove(rootTaskId, actor, req, myAgentId, options = {})
|
|
|
34858
35506
|
return { reason: SESSION_MOVE_UNAVAILABLE };
|
|
34859
35507
|
if (!hasSessionAccess(access, "write"))
|
|
34860
35508
|
return { reason: "Session write access required" };
|
|
35509
|
+
if (await deniesNonOwnerTaskWrite2(req, root))
|
|
35510
|
+
return { reason: NOT_YOUR_TASK_DENIED2 };
|
|
34861
35511
|
}
|
|
34862
35512
|
if (root.parentTaskId)
|
|
34863
35513
|
return { reason: "Task is not a session root" };
|
|
@@ -36945,7 +37595,7 @@ var uiEvent = route({
|
|
|
36945
37595
|
401: { description: "Unauthorized" },
|
|
36946
37596
|
403: { description: "Guest page sessions cannot send events" }
|
|
36947
37597
|
},
|
|
36948
|
-
rbac: {
|
|
37598
|
+
rbac: { permission: "favorite.write.own" }
|
|
36949
37599
|
});
|
|
36950
37600
|
var enableTelemetry = route({
|
|
36951
37601
|
method: "post",
|
|
@@ -37869,76 +38519,11 @@ init_zod();
|
|
|
37869
38519
|
init_db3();
|
|
37870
38520
|
init_rbac_roles();
|
|
37871
38521
|
init_users();
|
|
38522
|
+
init_rbac();
|
|
37872
38523
|
init_types();
|
|
38524
|
+
init_constants2();
|
|
38525
|
+
init_egress_guard();
|
|
37873
38526
|
init_request_auth_context();
|
|
37874
|
-
|
|
37875
|
-
// src/http/ip-rate-limit.ts
|
|
37876
|
-
var PRUNE_THRESHOLD = 1e4;
|
|
37877
|
-
var PRUNE_INTERVAL_MS = 60000;
|
|
37878
|
-
var MAX_BUCKETS = 50000;
|
|
37879
|
-
function isPrivateAddress(address) {
|
|
37880
|
-
const ip = address.startsWith("::ffff:") ? address.slice(7) : address;
|
|
37881
|
-
if (ip === "::1" || ip.startsWith("127.") || ip.startsWith("10."))
|
|
37882
|
-
return true;
|
|
37883
|
-
if (ip.startsWith("192.168.") || ip.startsWith("169.254."))
|
|
37884
|
-
return true;
|
|
37885
|
-
const match = /^172\.(\d+)\./.exec(ip);
|
|
37886
|
-
if (match && Number(match[1]) >= 16 && Number(match[1]) <= 31)
|
|
37887
|
-
return true;
|
|
37888
|
-
const lower = ip.toLowerCase();
|
|
37889
|
-
return lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
|
|
37890
|
-
}
|
|
37891
|
-
function clientIp(req) {
|
|
37892
|
-
const peer = req.socket.remoteAddress ?? "unknown";
|
|
37893
|
-
if (!isPrivateAddress(peer))
|
|
37894
|
-
return peer;
|
|
37895
|
-
const raw = req.headers["x-forwarded-for"];
|
|
37896
|
-
const header = Array.isArray(raw) ? raw[raw.length - 1] : raw;
|
|
37897
|
-
const hop = header?.split(",").pop()?.trim();
|
|
37898
|
-
return hop || peer;
|
|
37899
|
-
}
|
|
37900
|
-
function createIpRateLimiter(options) {
|
|
37901
|
-
const buckets = new Map;
|
|
37902
|
-
let lastPruneAt = 0;
|
|
37903
|
-
function refill(bucket, now) {
|
|
37904
|
-
const elapsed = now - bucket.updatedAt;
|
|
37905
|
-
bucket.tokens = Math.min(options.capacity, bucket.tokens + elapsed * options.refillPerMs);
|
|
37906
|
-
bucket.updatedAt = now;
|
|
37907
|
-
}
|
|
37908
|
-
return {
|
|
37909
|
-
take(ip, now = Date.now()) {
|
|
37910
|
-
if (buckets.size > PRUNE_THRESHOLD && now - lastPruneAt >= PRUNE_INTERVAL_MS) {
|
|
37911
|
-
lastPruneAt = now;
|
|
37912
|
-
for (const [key, bucket] of buckets) {
|
|
37913
|
-
refill(bucket, now);
|
|
37914
|
-
if (bucket.tokens >= options.capacity)
|
|
37915
|
-
buckets.delete(key);
|
|
37916
|
-
}
|
|
37917
|
-
}
|
|
37918
|
-
let bucket = buckets.get(ip);
|
|
37919
|
-
if (!bucket) {
|
|
37920
|
-
if (buckets.size >= MAX_BUCKETS) {
|
|
37921
|
-
const oldest = buckets.keys().next().value;
|
|
37922
|
-
if (oldest !== undefined)
|
|
37923
|
-
buckets.delete(oldest);
|
|
37924
|
-
}
|
|
37925
|
-
bucket = { tokens: options.capacity, updatedAt: now };
|
|
37926
|
-
buckets.set(ip, bucket);
|
|
37927
|
-
}
|
|
37928
|
-
refill(bucket, now);
|
|
37929
|
-
if (bucket.tokens < 1)
|
|
37930
|
-
return false;
|
|
37931
|
-
bucket.tokens -= 1;
|
|
37932
|
-
return true;
|
|
37933
|
-
},
|
|
37934
|
-
reset() {
|
|
37935
|
-
buckets.clear();
|
|
37936
|
-
lastPruneAt = 0;
|
|
37937
|
-
}
|
|
37938
|
-
};
|
|
37939
|
-
}
|
|
37940
|
-
|
|
37941
|
-
// src/http/users.ts
|
|
37942
38527
|
async function composeUser(userId, recentEventLimit = 5) {
|
|
37943
38528
|
const user = await getUserById2(userId);
|
|
37944
38529
|
if (!user)
|
|
@@ -38010,7 +38595,12 @@ var whoamiRoute = route({
|
|
|
38010
38595
|
description: "Authenticated principal",
|
|
38011
38596
|
schema: object2({
|
|
38012
38597
|
kind: _enum2(["operator", "user"]),
|
|
38013
|
-
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()
|
|
38014
38604
|
})
|
|
38015
38605
|
},
|
|
38016
38606
|
401: { description: "Unauthorized" }
|
|
@@ -38259,6 +38849,72 @@ var exchangeConnectorCodeRoute = route({
|
|
|
38259
38849
|
ungated: "the single-use code is the credential; the minted token carries the user's own grant and is admitted on use"
|
|
38260
38850
|
}
|
|
38261
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
|
+
});
|
|
38262
38918
|
var connectorDiscoveryRoute = route({
|
|
38263
38919
|
method: "get",
|
|
38264
38920
|
path: "/api/connector/discovery",
|
|
@@ -38439,6 +39095,26 @@ var connectorExchangeRateLimiter = createIpRateLimiter({
|
|
|
38439
39095
|
capacity: 10,
|
|
38440
39096
|
refillPerMs: 10 / 60000
|
|
38441
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
|
+
}
|
|
38442
39118
|
async function handleUsers(req, res, pathSegments, queryParams) {
|
|
38443
39119
|
if (whoamiRoute.match(req.method, pathSegments)) {
|
|
38444
39120
|
const parsed = await whoamiRoute.parse(req, res, pathSegments, queryParams);
|
|
@@ -38446,9 +39122,26 @@ async function handleUsers(req, res, pathSegments, queryParams) {
|
|
|
38446
39122
|
return true;
|
|
38447
39123
|
const auth = getRequestAuth(req);
|
|
38448
39124
|
if (auth?.kind === "user") {
|
|
38449
|
-
|
|
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
|
+
});
|
|
38450
39139
|
} else {
|
|
38451
|
-
whoamiRoute.respond(res, 200, {
|
|
39140
|
+
whoamiRoute.respond(res, 200, {
|
|
39141
|
+
kind: "operator",
|
|
39142
|
+
user: null,
|
|
39143
|
+
grants: { grantsAll: true, verbs: [], roles: [] }
|
|
39144
|
+
});
|
|
38452
39145
|
}
|
|
38453
39146
|
return true;
|
|
38454
39147
|
}
|
|
@@ -38652,6 +39345,110 @@ async function handleUsers(req, res, pathSegments, queryParams) {
|
|
|
38652
39345
|
}
|
|
38653
39346
|
return true;
|
|
38654
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
|
+
}
|
|
38655
39452
|
if (revokeUserMcpTokenRoute.match(req.method, pathSegments)) {
|
|
38656
39453
|
const parsed = await revokeUserMcpTokenRoute.parse(req, res, pathSegments, queryParams);
|
|
38657
39454
|
if (!parsed)
|
|
@@ -40006,63 +40803,6 @@ init_db3();
|
|
|
40006
40803
|
init_workflow_idempotency();
|
|
40007
40804
|
init_workflow_runs();
|
|
40008
40805
|
init_types();
|
|
40009
|
-
|
|
40010
|
-
// src/http/idempotency-key.ts
|
|
40011
|
-
init_zod();
|
|
40012
|
-
init_workflow_idempotency();
|
|
40013
|
-
init_request_auth_context();
|
|
40014
|
-
var IDEMPOTENCY_KEY_PATTERN = /^[\x21-\x7E]{1,255}$/;
|
|
40015
|
-
var IDEMPOTENT_REPLAYED_HEADER = "Idempotent-Replayed";
|
|
40016
|
-
var idempotencyKeyHeader = (accepted) => object2({
|
|
40017
|
-
"Idempotency-Key": string2().regex(IDEMPOTENCY_KEY_PATTERN).optional().describe("Optional client-generated key, 1-255 visible ASCII characters (a UUID works). " + `The first request the server accepts under a key ${accepted}; a retry with the same key ` + "and an equivalent body gets that request's status and body back (with " + `\`${IDEMPOTENT_REPLAYED_HEADER}: true\`) and changes nothing. The key is scoped to the ` + "caller's identity and this operation; it is not a credential. The same key with a " + "different body is a 409. A request rejected before it is accepted (validation, auth, " + "not found, disabled) does not use up the key. Accepted keys do not expire.")
|
|
40018
|
-
});
|
|
40019
|
-
function idempotencyPrincipal(req, myAgentId) {
|
|
40020
|
-
const auth = getRequestAuth(req);
|
|
40021
|
-
switch (auth?.kind) {
|
|
40022
|
-
case "user":
|
|
40023
|
-
return `user:${auth.userId}`;
|
|
40024
|
-
case "agent":
|
|
40025
|
-
return `agent:${auth.agentId}`;
|
|
40026
|
-
case "controller":
|
|
40027
|
-
return `controller:${auth.controllerId}`;
|
|
40028
|
-
case "guest":
|
|
40029
|
-
return null;
|
|
40030
|
-
default:
|
|
40031
|
-
return myAgentId ? `agent:${myAgentId}` : "operator";
|
|
40032
|
-
}
|
|
40033
|
-
}
|
|
40034
|
-
function readIdempotencyKey(req, res, myAgentId) {
|
|
40035
|
-
const raw = req.headers["idempotency-key"];
|
|
40036
|
-
if (raw === undefined)
|
|
40037
|
-
return;
|
|
40038
|
-
const key = Array.isArray(raw) ? raw.join(", ") : raw;
|
|
40039
|
-
if (!IDEMPOTENCY_KEY_PATTERN.test(key)) {
|
|
40040
|
-
jsonError(res, "Idempotency-Key must be 1-255 visible ASCII characters", 400);
|
|
40041
|
-
return null;
|
|
40042
|
-
}
|
|
40043
|
-
const principal = idempotencyPrincipal(req, myAgentId);
|
|
40044
|
-
if (!principal) {
|
|
40045
|
-
jsonError(res, "Idempotency-Key needs an identified caller", 400);
|
|
40046
|
-
return null;
|
|
40047
|
-
}
|
|
40048
|
-
return { principal, key };
|
|
40049
|
-
}
|
|
40050
|
-
async function replayAcceptedOperation(res, claim, send) {
|
|
40051
|
-
const accepted = await findAcceptedWorkflowOperation(claim);
|
|
40052
|
-
if (!accepted)
|
|
40053
|
-
return false;
|
|
40054
|
-
if (accepted.fingerprint !== claim.fingerprint) {
|
|
40055
|
-
jsonError(res, "Idempotency-Key was already used for a different request", 409);
|
|
40056
|
-
} else if (accepted.resourceDeletedAt !== null) {
|
|
40057
|
-
jsonError(res, "The workflow this Idempotency-Key created or triggered was deleted; send a new key to start over", 410);
|
|
40058
|
-
} else {
|
|
40059
|
-
res.setHeader(IDEMPOTENT_REPLAYED_HEADER, "true");
|
|
40060
|
-
send(accepted.body);
|
|
40061
|
-
}
|
|
40062
|
-
return true;
|
|
40063
|
-
}
|
|
40064
|
-
|
|
40065
|
-
// src/http/workflows.ts
|
|
40066
40806
|
var WorkflowWithFavoriteSchema = WorkflowSchema.extend({ favorite: boolean() });
|
|
40067
40807
|
var WorkflowSaveResponseSchema = WorkflowSchema.extend({
|
|
40068
40808
|
warnings: array2(string2()).optional()
|
|
@@ -40256,6 +40996,9 @@ var triggerWorkflowRoute = route({
|
|
|
40256
40996
|
description: "With an `Idempotency-Key`, a retry returns the accepted run's `runId` and `skipped` without starting, resuming or waiting for anything, even after the workflow was disabled or edited and whatever state the run is in now.",
|
|
40257
40997
|
tags: ["Workflows"],
|
|
40258
40998
|
params: object2({ id: string2() }),
|
|
40999
|
+
query: object2({
|
|
41000
|
+
wait: _enum2(["true", "false"]).optional().describe("Default true: respond after the run's instant steps ran, when it finishes or parks at its first async step. " + "false: respond as soon as the run is committed and walk it in the background.")
|
|
41001
|
+
}),
|
|
40259
41002
|
headers: idempotencyKeyHeader("starts one run"),
|
|
40260
41003
|
responses: {
|
|
40261
41004
|
201: {
|
|
@@ -40854,6 +41597,7 @@ async function handleWorkflows(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
40854
41597
|
runId = await startWorkflowExecution2(workflow, body, getExecutorRegistry2(), {
|
|
40855
41598
|
triggerType: "api",
|
|
40856
41599
|
requestedByUserId: await resolveHttpAuditUserId(req, myAgentId) ?? undefined,
|
|
41600
|
+
detach: parsed.query.wait === "false",
|
|
40857
41601
|
accept: claim ? async (run) => {
|
|
40858
41602
|
const response = { runId: run.runId, skipped: run.skipped };
|
|
40859
41603
|
await recordAcceptedWorkflowOperation(claim, {
|
|
@@ -41098,6 +41842,7 @@ async function handleX(req, res, pathSegments) {
|
|
|
41098
41842
|
jsonError(res, "workspace-rw scripts are not supported", 501);
|
|
41099
41843
|
return true;
|
|
41100
41844
|
}
|
|
41845
|
+
let hostValidatedArgs = false;
|
|
41101
41846
|
if (script.argsJsonSchema) {
|
|
41102
41847
|
try {
|
|
41103
41848
|
const schema = JSON.parse(script.argsJsonSchema);
|
|
@@ -41111,6 +41856,7 @@ async function handleX(req, res, pathSegments) {
|
|
|
41111
41856
|
});
|
|
41112
41857
|
return true;
|
|
41113
41858
|
}
|
|
41859
|
+
hostValidatedArgs = true;
|
|
41114
41860
|
} catch {}
|
|
41115
41861
|
}
|
|
41116
41862
|
const timeoutMs = resolveTimeoutMs(req);
|
|
@@ -41122,7 +41868,9 @@ async function handleX(req, res, pathSegments) {
|
|
|
41122
41868
|
const output = await runScriptWithRunCredential({
|
|
41123
41869
|
source: script.source,
|
|
41124
41870
|
args: args ?? null,
|
|
41871
|
+
[HOST_VALIDATED_ARGS]: hostValidatedArgs,
|
|
41125
41872
|
fsMode: "none",
|
|
41873
|
+
executor: script.executor,
|
|
41126
41874
|
agentId: endpoint.agentId,
|
|
41127
41875
|
timeoutMs,
|
|
41128
41876
|
egressSecrets: credentials.egressSecrets,
|
|
@@ -41255,7 +42003,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
41255
42003
|
const semconv = httpServerSemconvAttributes(req);
|
|
41256
42004
|
const span = skipSpan ? null : startSpan2(spanName, {
|
|
41257
42005
|
"http.request.method": req.method ?? "",
|
|
41258
|
-
"url.path": reqPath,
|
|
42006
|
+
"url.path": redactShareTokenInPath(reqPath),
|
|
41259
42007
|
"url.scheme": semconv["url.scheme"],
|
|
41260
42008
|
"http.route": httpRoute,
|
|
41261
42009
|
"server.address": semconv["server.address"],
|
|
@@ -41299,6 +42047,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
41299
42047
|
() => handlePoolKeys(req, res, pathSegments, queryParams),
|
|
41300
42048
|
() => handleConfig(req, res, pathSegments, queryParams),
|
|
41301
42049
|
() => handleFs(req, res, pathSegments, queryParams, myAgentId),
|
|
42050
|
+
() => handleAgentFsProxy(req, res),
|
|
41302
42051
|
() => handleComb(req, res, pathSegments, queryParams, myAgentId),
|
|
41303
42052
|
() => handleKv(req, res, pathSegments, queryParams),
|
|
41304
42053
|
() => handleRooms(req, res, pathSegments, queryParams),
|
|
@@ -41373,7 +42122,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
41373
42122
|
});
|
|
41374
42123
|
});
|
|
41375
42124
|
var detachRealtime = attachRealtimeTransport(httpServer);
|
|
41376
|
-
var { attachControllerTransport } = await import("./controllers-connect-
|
|
42125
|
+
var { attachControllerTransport } = await import("./controllers-connect-ajvh9h0v.js");
|
|
41377
42126
|
var detachControllers = attachControllerTransport(httpServer);
|
|
41378
42127
|
var stopWorkerNudges = startWorkerNudgeListener();
|
|
41379
42128
|
var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
|
|
@@ -41402,7 +42151,7 @@ async function shutdown() {
|
|
|
41402
42151
|
sessionsProcessed: getServerSessionsProcessed()
|
|
41403
42152
|
});
|
|
41404
42153
|
if (hasCapability("scheduling")) {
|
|
41405
|
-
const { stopScheduler } = await import("./index-
|
|
42154
|
+
const { stopScheduler } = await import("./index-ch0c1xff.js");
|
|
41406
42155
|
stopScheduler();
|
|
41407
42156
|
}
|
|
41408
42157
|
stopHeartbeat2();
|
|
@@ -41413,8 +42162,8 @@ async function shutdown() {
|
|
|
41413
42162
|
await stopScriptRunSupervisor();
|
|
41414
42163
|
await stopSlackApp2();
|
|
41415
42164
|
await stopChatSdk();
|
|
41416
|
-
if (
|
|
41417
|
-
const { stopOAuthKeepalive } = await import("./keepalive-
|
|
42165
|
+
if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
|
|
42166
|
+
const { stopOAuthKeepalive } = await import("./keepalive-zzc3hks1.js");
|
|
41418
42167
|
await stopOAuthKeepalive();
|
|
41419
42168
|
}
|
|
41420
42169
|
stopOAuthPendingGc();
|
|
@@ -41494,21 +42243,21 @@ try {
|
|
|
41494
42243
|
console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
|
|
41495
42244
|
}
|
|
41496
42245
|
try {
|
|
41497
|
-
const { ensurePricingSeeded } = await import("./boot-seeds-
|
|
42246
|
+
const { ensurePricingSeeded } = await import("./boot-seeds-r3kwwv8b.js");
|
|
41498
42247
|
ensurePricingSeeded();
|
|
41499
|
-
const { startPricingRefreshLoop } = await import("./pricing-refresh-
|
|
42248
|
+
const { startPricingRefreshLoop } = await import("./pricing-refresh-wkpwhgvs.js");
|
|
41500
42249
|
startPricingRefreshLoop();
|
|
41501
42250
|
} catch (err) {
|
|
41502
42251
|
console.error("[startup] Failed to seed pricing rows:", err);
|
|
41503
42252
|
}
|
|
41504
42253
|
try {
|
|
41505
|
-
const { ensureModelProfilesSeeded } = await import("./boot-seeds-
|
|
42254
|
+
const { ensureModelProfilesSeeded } = await import("./boot-seeds-r3kwwv8b.js");
|
|
41506
42255
|
await ensureModelProfilesSeeded();
|
|
41507
42256
|
} catch (err) {
|
|
41508
42257
|
console.error("[startup] Failed to seed model profiles:", err);
|
|
41509
42258
|
}
|
|
41510
42259
|
try {
|
|
41511
|
-
const { ensureRbacSeeded } = await import("./boot-seeds-
|
|
42260
|
+
const { ensureRbacSeeded } = await import("./boot-seeds-r3kwwv8b.js");
|
|
41512
42261
|
ensureRbacSeeded();
|
|
41513
42262
|
} catch (err) {
|
|
41514
42263
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
@@ -41516,7 +42265,7 @@ try {
|
|
|
41516
42265
|
throw err;
|
|
41517
42266
|
}
|
|
41518
42267
|
try {
|
|
41519
|
-
const { runAllSeeders } = await import("./index-
|
|
42268
|
+
const { runAllSeeders } = await import("./index-ejqdkkqx.js");
|
|
41520
42269
|
await runAllSeeders({ scriptEmbeddingMode: "skip" });
|
|
41521
42270
|
} catch (err) {
|
|
41522
42271
|
console.error("[startup] Failed to seed built-in entities:", err);
|
|
@@ -41579,42 +42328,42 @@ httpServer.listen(port, async () => {
|
|
|
41579
42328
|
await initWorkflows2();
|
|
41580
42329
|
await startScriptRunSupervisor(getMcpBaseUrl());
|
|
41581
42330
|
if (hasCapability("scheduling")) {
|
|
41582
|
-
const { startScheduler } = await import("./index-
|
|
41583
|
-
const { getExecutorRegistry } = await import("./index-
|
|
42331
|
+
const { startScheduler } = await import("./index-ch0c1xff.js");
|
|
42332
|
+
const { getExecutorRegistry } = await import("./index-5nb266jr.js");
|
|
41584
42333
|
const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
|
|
41585
42334
|
startScheduler(getExecutorRegistry(), intervalMs, {
|
|
41586
42335
|
runId: globalState.__runId
|
|
41587
42336
|
});
|
|
41588
42337
|
}
|
|
41589
42338
|
if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
|
|
41590
|
-
const { startHeartbeat } = await import("./index-
|
|
42339
|
+
const { startHeartbeat } = await import("./index-ed8dy8rn.js");
|
|
41591
42340
|
const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
|
|
41592
42341
|
startHeartbeat(heartbeatMs);
|
|
41593
42342
|
}
|
|
41594
42343
|
startPoolRouting();
|
|
41595
42344
|
startRuntimeSupervisor();
|
|
41596
|
-
if (
|
|
41597
|
-
const { startOAuthKeepalive } = await import("./keepalive-
|
|
42345
|
+
if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
|
|
42346
|
+
const { startOAuthKeepalive } = await import("./keepalive-zzc3hks1.js");
|
|
41598
42347
|
startOAuthKeepalive();
|
|
41599
42348
|
}
|
|
41600
|
-
if (
|
|
41601
|
-
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-
|
|
42349
|
+
if (!isEnvFlagEnabled("OAUTH_REFRESH_SWEEP_DISABLE", false)) {
|
|
42350
|
+
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-7qt0g55z.js");
|
|
41602
42351
|
startOAuthRefreshSweep();
|
|
41603
42352
|
}
|
|
41604
42353
|
startOAuthPendingGc();
|
|
41605
42354
|
await startMemoryGc();
|
|
41606
42355
|
await startDbRetention();
|
|
41607
42356
|
startHumanFreeDrain();
|
|
41608
|
-
import("./boot-reembed-
|
|
42357
|
+
import("./boot-reembed-k5y5xbj0.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
41609
42358
|
console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
|
|
41610
42359
|
});
|
|
41611
|
-
import("./boot-reembed-
|
|
42360
|
+
import("./boot-reembed-fxfw17m2.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
41612
42361
|
console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
|
|
41613
42362
|
});
|
|
41614
|
-
import("./boot-scrub-sweep-
|
|
42363
|
+
import("./boot-scrub-sweep-se41j9fd.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
|
|
41615
42364
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
41616
42365
|
});
|
|
41617
|
-
import("./session-source-backfill-
|
|
42366
|
+
import("./session-source-backfill-62202awe.js").then(({ runSessionSourceBackfill }) => runSessionSourceBackfill()).catch((err) => {
|
|
41618
42367
|
console.error("[session-source-backfill] startup run failed (non-fatal):", err);
|
|
41619
42368
|
});
|
|
41620
42369
|
}).on("error", (err) => {
|