agor-live 0.23.1 → 0.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/claude/index.cjs +51 -27
- package/dist/core/claude/index.js +51 -27
- package/dist/core/claude-cli/index.cjs +13 -0
- package/dist/core/claude-cli/index.js +13 -0
- package/dist/core/claude-cli/pricing.d.ts.map +1 -1
- package/dist/core/client/index.cjs +17 -0
- package/dist/core/client/index.js +16 -0
- package/dist/core/db/index.cjs +618 -421
- package/dist/core/db/index.js +550 -353
- package/dist/core/db/repositories/boards.d.ts +27 -0
- package/dist/core/db/repositories/boards.d.ts.map +1 -1
- package/dist/core/db/repositories/branches.d.ts +11 -1
- package/dist/core/db/repositories/branches.d.ts.map +1 -1
- package/dist/core/db/repositories/gateway-channels.d.ts +9 -0
- package/dist/core/db/repositories/gateway-channels.d.ts.map +1 -1
- package/dist/core/db/repositories/sessions.d.ts +7 -0
- package/dist/core/db/repositories/sessions.d.ts.map +1 -1
- package/dist/core/environment/render-snapshot.cjs +6 -2
- package/dist/core/environment/render-snapshot.d.ts +2 -0
- package/dist/core/environment/render-snapshot.d.ts.map +1 -1
- package/dist/core/environment/render-snapshot.js +6 -2
- package/dist/core/environment/variable-resolver.cjs +3 -1
- package/dist/core/environment/variable-resolver.js +3 -1
- package/dist/core/index.cjs +684 -486
- package/dist/core/index.js +684 -486
- package/dist/core/lib/feathers-validation.cjs +3 -0
- package/dist/core/lib/feathers-validation.d.ts +1 -0
- package/dist/core/lib/feathers-validation.d.ts.map +1 -1
- package/dist/core/lib/feathers-validation.js +3 -0
- package/dist/core/local-actions/index.cjs +123 -56
- package/dist/core/local-actions/index.js +121 -54
- package/dist/core/mcp/index.cjs +21 -3
- package/dist/core/mcp/index.d.ts +1 -1
- package/dist/core/mcp/index.d.ts.map +1 -1
- package/dist/core/mcp/index.js +18 -3
- package/dist/core/mcp/template-patterns.d.ts +15 -0
- package/dist/core/mcp/template-patterns.d.ts.map +1 -0
- package/dist/core/mcp/template-resolver.d.ts +13 -0
- package/dist/core/mcp/template-resolver.d.ts.map +1 -1
- package/dist/core/models/browser.cjs +6 -0
- package/dist/core/models/browser.js +6 -0
- package/dist/core/models/claude.d.ts.map +1 -1
- package/dist/core/models/index.cjs +6 -0
- package/dist/core/models/index.js +6 -0
- package/dist/core/seed/index.cjs +360 -216
- package/dist/core/seed/index.js +329 -185
- package/dist/core/templates/handlebars-helpers.cjs +3 -1
- package/dist/core/templates/handlebars-helpers.d.ts +5 -0
- package/dist/core/templates/handlebars-helpers.d.ts.map +1 -1
- package/dist/core/templates/handlebars-helpers.js +3 -1
- package/dist/core/tools/mcp/auth-secrets.cjs +3460 -3
- package/dist/core/tools/mcp/auth-secrets.d.ts.map +1 -1
- package/dist/core/tools/mcp/auth-secrets.js +3477 -3
- package/dist/core/tools/mcp/http-headers.cjs +17 -1
- package/dist/core/tools/mcp/http-headers.d.ts.map +1 -1
- package/dist/core/tools/mcp/http-headers.js +15 -1
- package/dist/core/tools/mcp/oauth-refresh.cjs +67 -43
- package/dist/core/tools/mcp/oauth-refresh.js +65 -41
- package/dist/core/types/board.d.ts +12 -0
- package/dist/core/types/board.d.ts.map +1 -1
- package/dist/core/types/gateway.d.ts +14 -0
- package/dist/core/types/gateway.d.ts.map +1 -1
- package/dist/core/types/index.cjs +17 -0
- package/dist/core/types/index.js +16 -0
- package/dist/core/unix/index.cjs +67 -0
- package/dist/core/unix/index.js +67 -0
- package/dist/daemon/auth/executor-runtime-scope.d.ts.map +1 -1
- package/dist/daemon/auth/executor-runtime-scope.js +19 -5
- package/dist/daemon/auth/executor-session-token.d.ts +15 -0
- package/dist/daemon/auth/executor-session-token.d.ts.map +1 -0
- package/dist/daemon/auth/executor-session-token.js +17 -0
- package/dist/daemon/auth/issue-browser-tokens-hook.d.ts +24 -0
- package/dist/daemon/auth/issue-browser-tokens-hook.d.ts.map +1 -0
- package/dist/daemon/auth/issue-browser-tokens-hook.js +95 -0
- package/dist/daemon/auth/service-jwt-strategy.d.ts.map +1 -1
- package/dist/daemon/auth/service-jwt-strategy.js +14 -2
- package/dist/daemon/declarations.d.ts +1 -0
- package/dist/daemon/declarations.d.ts.map +1 -1
- package/dist/daemon/index.js +1365 -418
- package/dist/daemon/main.js +1365 -418
- package/dist/daemon/mcp/server.js +294 -31
- package/dist/daemon/mcp/tools/analytics.js +73 -24
- package/dist/daemon/mcp/tools/artifacts.js +73 -24
- package/dist/daemon/mcp/tools/boards.js +73 -24
- package/dist/daemon/mcp/tools/branches.js +73 -24
- package/dist/daemon/mcp/tools/card-types.js +73 -24
- package/dist/daemon/mcp/tools/cards.js +73 -24
- package/dist/daemon/mcp/tools/environment.js +73 -24
- package/dist/daemon/mcp/tools/gateway-channels.d.ts.map +1 -1
- package/dist/daemon/mcp/tools/gateway-channels.js +190 -38
- package/dist/daemon/mcp/tools/knowledge.js +73 -24
- package/dist/daemon/mcp/tools/mcp-servers.js +73 -24
- package/dist/daemon/mcp/tools/messages.js +73 -24
- package/dist/daemon/mcp/tools/proxies.js +73 -24
- package/dist/daemon/mcp/tools/repos.js +73 -24
- package/dist/daemon/mcp/tools/schedules.js +73 -24
- package/dist/daemon/mcp/tools/search.js +73 -24
- package/dist/daemon/mcp/tools/sessions.js +73 -24
- package/dist/daemon/mcp/tools/tasks.js +73 -24
- package/dist/daemon/mcp/tools/users.js +76 -27
- package/dist/daemon/mcp/tools/widgets.d.ts.map +1 -1
- package/dist/daemon/mcp/tools/widgets.js +257 -99
- package/dist/daemon/oauth-auth-helpers.d.ts +35 -0
- package/dist/daemon/oauth-auth-helpers.d.ts.map +1 -0
- package/dist/daemon/oauth-auth-helpers.js +14 -0
- package/dist/daemon/register-hooks.d.ts +1 -0
- package/dist/daemon/register-hooks.d.ts.map +1 -1
- package/dist/daemon/register-hooks.js +304 -53
- package/dist/daemon/register-routes.d.ts.map +1 -1
- package/dist/daemon/register-routes.js +738 -288
- package/dist/daemon/register-services.d.ts.map +1 -1
- package/dist/daemon/register-services.js +386 -155
- package/dist/daemon/services/boards.d.ts +5 -0
- package/dist/daemon/services/boards.d.ts.map +1 -1
- package/dist/daemon/services/boards.js +7 -0
- package/dist/daemon/services/branches.d.ts.map +1 -1
- package/dist/daemon/services/branches.js +31 -2
- package/dist/daemon/services/claude-models.d.ts.map +1 -1
- package/dist/daemon/services/claude-models.js +4 -1
- package/dist/daemon/services/gateway.d.ts +15 -0
- package/dist/daemon/services/gateway.d.ts.map +1 -1
- package/dist/daemon/services/gateway.js +34 -0
- package/dist/daemon/services/health-monitor.d.ts.map +1 -1
- package/dist/daemon/services/health-monitor.js +10 -7
- package/dist/daemon/services/session-streams.d.ts +29 -0
- package/dist/daemon/services/session-streams.d.ts.map +1 -0
- package/dist/daemon/services/session-streams.js +84 -0
- package/dist/daemon/services/session-token-service.d.ts.map +1 -1
- package/dist/daemon/services/session-token-service.js +8 -4
- package/dist/daemon/setup/index.js +43 -11
- package/dist/daemon/setup/socketio.d.ts.map +1 -1
- package/dist/daemon/setup/socketio.js +43 -11
- package/dist/daemon/startup.d.ts.map +1 -1
- package/dist/daemon/startup.js +37 -16
- package/dist/daemon/utils/authorization.js +19 -5
- package/dist/daemon/utils/emit-service-event.d.ts +56 -0
- package/dist/daemon/utils/emit-service-event.d.ts.map +1 -0
- package/dist/daemon/utils/emit-service-event.js +23 -0
- package/dist/daemon/utils/mcp-header-secrets.d.ts +3 -5
- package/dist/daemon/utils/mcp-header-secrets.d.ts.map +1 -1
- package/dist/daemon/utils/mcp-header-secrets.js +31 -2
- package/dist/daemon/utils/realtime-access-cache.d.ts +9 -0
- package/dist/daemon/utils/realtime-access-cache.d.ts.map +1 -1
- package/dist/daemon/utils/realtime-access-cache.js +24 -0
- package/dist/daemon/utils/realtime-publish.d.ts +24 -0
- package/dist/daemon/utils/realtime-publish.d.ts.map +1 -1
- package/dist/daemon/utils/realtime-publish.js +120 -2
- package/dist/daemon/utils/session-queue-tenant-scope.d.ts +24 -0
- package/dist/daemon/utils/session-queue-tenant-scope.d.ts.map +1 -0
- package/dist/daemon/utils/session-queue-tenant-scope.js +133 -0
- package/dist/daemon/widgets/gateway-token/index.d.ts +114 -0
- package/dist/daemon/widgets/gateway-token/index.d.ts.map +1 -0
- package/dist/daemon/widgets/gateway-token/index.js +14711 -0
- package/dist/daemon/widgets/index.d.ts.map +1 -1
- package/dist/daemon/widgets/index.js +190 -16
- package/dist/daemon/widgets/registry.d.ts +7 -0
- package/dist/daemon/widgets/registry.d.ts.map +1 -1
- package/dist/daemon/widgets/submissions.js +12 -9
- package/dist/executor/commands/git.d.ts.map +1 -1
- package/dist/executor/commands/git.js +2 -0
- package/dist/executor/db/feathers-repositories.d.ts +19 -1
- package/dist/executor/db/feathers-repositories.d.ts.map +1 -1
- package/dist/executor/db/feathers-repositories.js +31 -1
- package/dist/executor/handlers/sdk/claude.d.ts.map +1 -1
- package/dist/executor/handlers/sdk/claude.js +1 -1
- package/dist/executor/handlers/sdk/codex.d.ts.map +1 -1
- package/dist/executor/handlers/sdk/codex.js +1 -1
- package/dist/executor/handlers/sdk/copilot.d.ts.map +1 -1
- package/dist/executor/handlers/sdk/copilot.js +1 -1
- package/dist/executor/handlers/sdk/cursor.d.ts.map +1 -1
- package/dist/executor/handlers/sdk/cursor.js +1 -0
- package/dist/executor/handlers/sdk/gemini.d.ts.map +1 -1
- package/dist/executor/handlers/sdk/gemini.js +1 -1
- package/dist/executor/handlers/sdk/opencode.d.ts.map +1 -1
- package/dist/executor/handlers/sdk/opencode.js +1 -1
- package/dist/executor/payload-types.d.ts +5 -5
- package/dist/executor/sdk-handlers/base/mcp-scoping.d.ts +8 -1
- package/dist/executor/sdk-handlers/base/mcp-scoping.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/base/mcp-scoping.js +58 -6
- package/dist/executor/sdk-handlers/claude/claude-tool.d.ts +2 -2
- package/dist/executor/sdk-handlers/claude/claude-tool.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/claude/claude-tool.js +2 -2
- package/dist/executor/sdk-handlers/claude/prompt-service.d.ts +13 -3
- package/dist/executor/sdk-handlers/claude/prompt-service.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/claude/prompt-service.js +16 -3
- package/dist/executor/sdk-handlers/claude/query-builder.d.ts +2 -1
- package/dist/executor/sdk-handlers/claude/query-builder.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/claude/query-builder.js +2 -1
- package/dist/executor/sdk-handlers/codex/codex-tool.d.ts +2 -2
- package/dist/executor/sdk-handlers/codex/codex-tool.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/codex/codex-tool.js +2 -2
- package/dist/executor/sdk-handlers/codex/prompt-service.d.ts +3 -2
- package/dist/executor/sdk-handlers/codex/prompt-service.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/codex/prompt-service.js +70 -6
- package/dist/executor/sdk-handlers/copilot/copilot-tool.d.ts +2 -2
- package/dist/executor/sdk-handlers/copilot/copilot-tool.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/copilot/copilot-tool.js +2 -2
- package/dist/executor/sdk-handlers/copilot/prompt-service.d.ts +3 -2
- package/dist/executor/sdk-handlers/copilot/prompt-service.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/copilot/prompt-service.js +4 -1
- package/dist/executor/sdk-handlers/gemini/gemini-tool.d.ts +2 -2
- package/dist/executor/sdk-handlers/gemini/gemini-tool.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/gemini/gemini-tool.js +2 -2
- package/dist/executor/sdk-handlers/gemini/prompt-service.d.ts +3 -2
- package/dist/executor/sdk-handlers/gemini/prompt-service.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/gemini/prompt-service.js +4 -1
- package/dist/executor/sdk-handlers/opencode/opencode-tool.d.ts +3 -2
- package/dist/executor/sdk-handlers/opencode/opencode-tool.d.ts.map +1 -1
- package/dist/executor/sdk-handlers/opencode/opencode-tool.js +4 -1
- package/dist/executor/services/feathers-client.d.ts.map +1 -1
- package/dist/executor/services/feathers-client.js +119 -20
- package/dist/ui/assets/AgorEmojiPickerInner-leeNywMc.js +1 -0
- package/dist/ui/assets/App-Dpl4Cc0U.js +4 -0
- package/dist/ui/assets/App-Dpl4Cc0U.js.gz +0 -0
- package/dist/ui/assets/AppNode-wN3UZlFK.js +1 -0
- package/dist/ui/assets/AppNode-wN3UZlFK.js.gz +0 -0
- package/dist/ui/assets/{ArtifactConsentModal-oMMzRn8L.js → ArtifactConsentModal-DjzXcP_L.js} +1 -1
- package/dist/ui/assets/ArtifactConsentModal-DjzXcP_L.js.gz +0 -0
- package/dist/ui/assets/ArtifactFullscreenPage-B3b_EZ0F.js +9 -0
- package/dist/ui/assets/ArtifactFullscreenPage-B3b_EZ0F.js.gz +0 -0
- package/dist/ui/assets/ArtifactNode-DISy2T-6.js +9 -0
- package/dist/ui/assets/ArtifactNode-DISy2T-6.js.gz +0 -0
- package/dist/ui/assets/AutocompleteTextarea-DUpsoEGr.js +19 -0
- package/dist/ui/assets/AutocompleteTextarea-DUpsoEGr.js.gz +0 -0
- package/dist/ui/assets/BoardObjectNodes-BakeGpIX.js +34 -0
- package/dist/ui/assets/BoardObjectNodes-BakeGpIX.js.gz +0 -0
- package/dist/ui/assets/{CodeEditor.inner-Cuj6RuLL.js → CodeEditor.inner-HmSdTLW5.js} +1 -1
- package/dist/ui/assets/CodeEditor.inner-HmSdTLW5.js.gz +0 -0
- package/dist/ui/assets/ConversationView-mWaA-B1f.js +1 -0
- package/dist/ui/assets/ConversationView-mWaA-B1f.js.gz +0 -0
- package/dist/ui/assets/{EmbeddedTerminal-Lx5I9EOJ.js → EmbeddedTerminal-NNoyPA81.js} +1 -1
- package/dist/ui/assets/EmbeddedTerminal-NNoyPA81.js.gz +0 -0
- package/dist/ui/assets/KnowledgePage-CkxVnRvz.js +24 -0
- package/dist/ui/assets/KnowledgePage-CkxVnRvz.js.gz +0 -0
- package/dist/ui/assets/{MarketingScreenshotPage-CmDPBHOl.js → MarketingScreenshotPage-B86Gs8jx.js} +2 -2
- package/dist/ui/assets/MarketingScreenshotPage-B86Gs8jx.js.gz +0 -0
- package/dist/ui/assets/MobileApp-BPs-SXvz.js +1 -0
- package/dist/ui/assets/MobileApp-BPs-SXvz.js.gz +0 -0
- package/dist/ui/assets/SessionCanvas-xVfDoEtz.js +13 -0
- package/dist/ui/assets/SessionCanvas-xVfDoEtz.js.gz +0 -0
- package/dist/ui/assets/{StreamdownDemoPage-okKKR2QH.js → StreamdownDemoPage-CvWLzVsv.js} +2 -2
- package/dist/ui/assets/StreamdownDemoPage-CvWLzVsv.js.gz +0 -0
- package/dist/ui/assets/{TerminalModal-D9jMGZXv.js → TerminalModal-ExOgsuOP.js} +2 -2
- package/dist/ui/assets/TerminalModal-ExOgsuOP.js.gz +0 -0
- package/dist/ui/assets/ThemeSwitcher-Cgyp5ruu.js +1 -0
- package/dist/ui/assets/ThemeSwitcher-Cgyp5ruu.js.gz +0 -0
- package/dist/ui/assets/{ThemedSyntaxHighlighter.inner-C1FdIHn0.js → ThemedSyntaxHighlighter.inner-rpCN9S6K.js} +1 -1
- package/dist/ui/assets/{antd-DT5ackd0.js → antd-BCqimh1J.js} +5 -5
- package/dist/ui/assets/{antd-DT5ackd0.js.gz → antd-BCqimh1J.js.gz} +0 -0
- package/dist/ui/assets/architecture-U656AL7Q-BxSQIjrE.js +1 -0
- package/dist/ui/assets/{architectureDiagram-VXUJARFQ-DwTJGGoj.js → architectureDiagram-VXUJARFQ-pDpGHSqT.js} +1 -1
- package/dist/ui/assets/architectureDiagram-VXUJARFQ-pDpGHSqT.js.gz +0 -0
- package/dist/ui/assets/{blockDiagram-VD42YOAC-Dh1_TVW2.js → blockDiagram-VD42YOAC-uP8JTJpK.js} +1 -1
- package/dist/ui/assets/blockDiagram-VD42YOAC-uP8JTJpK.js.gz +0 -0
- package/dist/ui/assets/{c4Diagram-YG6GDRKO-B1e96AcB.js → c4Diagram-YG6GDRKO-qQe72Mm7.js} +1 -1
- package/dist/ui/assets/c4Diagram-YG6GDRKO-qQe72Mm7.js.gz +0 -0
- package/dist/ui/assets/channel-CoUBs6Zn.js +1 -0
- package/dist/ui/assets/{chunk-4BX2VUAB-BODpCjJx.js → chunk-4BX2VUAB-sz-YE8Nt.js} +1 -1
- package/dist/ui/assets/chunk-4ZMWKKQ3-3oKtfWXp.js +3 -0
- package/dist/ui/assets/chunk-4ZMWKKQ3-3oKtfWXp.js.gz +0 -0
- package/dist/ui/assets/chunk-55IACEB6-BS04dgKF.js +1 -0
- package/dist/ui/assets/{chunk-ABZYJK2D-DCTXWSzV.js → chunk-ABZYJK2D-t3Gkphds.js} +3 -3
- package/dist/ui/assets/chunk-ABZYJK2D-t3Gkphds.js.gz +0 -0
- package/dist/ui/assets/{chunk-AGHRB4JF-CbNphBjE.js → chunk-AGHRB4JF-D_QmWxbY.js} +1 -1
- package/dist/ui/assets/chunk-AGHRB4JF-D_QmWxbY.js.gz +0 -0
- package/dist/ui/assets/{chunk-ATLVNIR6-rWg3qxof.js → chunk-ATLVNIR6-Bmg7-Ufz.js} +1 -1
- package/dist/ui/assets/chunk-ATLVNIR6-Bmg7-Ufz.js.gz +0 -0
- package/dist/ui/assets/{chunk-B4BG7PRW-CgtLCbpE.js → chunk-B4BG7PRW-dnSEE1u-.js} +1 -1
- package/dist/ui/assets/chunk-B4BG7PRW-dnSEE1u-.js.gz +0 -0
- package/dist/ui/assets/{chunk-CVBHYZKI-DTL6Zgr0.js → chunk-CVBHYZKI-BStaYnF0.js} +1 -1
- package/dist/ui/assets/{chunk-DI55MBZ5-Dwvx2Kva.js → chunk-DI55MBZ5-28dvx6o3.js} +1 -1
- package/dist/ui/assets/chunk-DI55MBZ5-28dvx6o3.js.gz +0 -0
- package/dist/ui/assets/chunk-EXTU4WIE-X-FURkEh.js +1 -0
- package/dist/ui/assets/{chunk-FMBD7UC4-BogA_9CV.js → chunk-FMBD7UC4-C_63gC9S.js} +1 -1
- package/dist/ui/assets/{chunk-HN2XXSSU-2blQO3Gb.js → chunk-HN2XXSSU-UFD374KI.js} +1 -1
- package/dist/ui/assets/chunk-HN2XXSSU-UFD374KI.js.gz +0 -0
- package/dist/ui/assets/{chunk-JA3XYJ7Z-Cr1rmOui.js → chunk-JA3XYJ7Z-B60hLNeR.js} +1 -1
- package/dist/ui/assets/chunk-JA3XYJ7Z-B60hLNeR.js.gz +0 -0
- package/dist/ui/assets/{chunk-JZLCHNYA-BsgZ79Fa.js → chunk-JZLCHNYA-ButC6pwN.js} +1 -1
- package/dist/ui/assets/chunk-JZLCHNYA-ButC6pwN.js.gz +0 -0
- package/dist/ui/assets/{chunk-MI3HLSF2-dwYeUy5S.js → chunk-MI3HLSF2-BivYzoSV.js} +1 -1
- package/dist/ui/assets/chunk-MI3HLSF2-BivYzoSV.js.gz +0 -0
- package/dist/ui/assets/chunk-N4CR4FBY-mVWzo5wJ.js +2 -0
- package/dist/ui/assets/chunk-N4CR4FBY-mVWzo5wJ.js.gz +0 -0
- package/dist/ui/assets/{chunk-QN33PNHL-DxdIDCAa.js → chunk-QN33PNHL-tXtHZpaG.js} +1 -1
- package/dist/ui/assets/{chunk-QXUST7PY-BCK2V0i-.js → chunk-QXUST7PY-DATib_Vv.js} +1 -1
- package/dist/ui/assets/chunk-QXUST7PY-DATib_Vv.js.gz +0 -0
- package/dist/ui/assets/chunk-QZHKN3VN-y6l249e5.js +1 -0
- package/dist/ui/assets/{chunk-S3R3BYOJ-GP3zIxrB.js → chunk-S3R3BYOJ-BEtfmflI.js} +1 -1
- package/dist/ui/assets/chunk-S3R3BYOJ-BEtfmflI.js.gz +0 -0
- package/dist/ui/assets/{chunk-TZMSLE5B-dX5RaM6g.js → chunk-TZMSLE5B-BY1ZtogK.js} +1 -1
- package/dist/ui/assets/chunk-TZMSLE5B-BY1ZtogK.js.gz +0 -0
- package/dist/ui/assets/classDiagram-2ON5EDUG-BYvq-uPY.js +1 -0
- package/dist/ui/assets/classDiagram-v2-WZHVMYZB-BYvq-uPY.js +1 -0
- package/dist/ui/assets/constants-CYzQKfD1.js +1 -0
- package/dist/ui/assets/{cose-bilkent-S5V4N54A-Dx0BJVDl.js → cose-bilkent-S5V4N54A-DPvYrqGG.js} +1 -1
- package/dist/ui/assets/cose-bilkent-S5V4N54A-DPvYrqGG.js.gz +0 -0
- package/dist/ui/assets/{dagre-6UL2VRFP-DiQSUxqN.js → dagre-6UL2VRFP-D800J6qb.js} +1 -1
- package/dist/ui/assets/dagre-6UL2VRFP-D800J6qb.js.gz +0 -0
- package/dist/ui/assets/{dagre-Bekd9Vdj.js → dagre-TNc4wezg.js} +1 -1
- package/dist/ui/assets/dagre-TNc4wezg.js.gz +0 -0
- package/dist/ui/assets/{diagram-PSM6KHXK-DtJnDQjc.js → diagram-PSM6KHXK-CGdP9F-X.js} +1 -1
- package/dist/ui/assets/diagram-PSM6KHXK-CGdP9F-X.js.gz +0 -0
- package/dist/ui/assets/{diagram-QEK2KX5R-CobkCRdj.js → diagram-QEK2KX5R-BD5lOCe5.js} +1 -1
- package/dist/ui/assets/diagram-QEK2KX5R-BD5lOCe5.js.gz +0 -0
- package/dist/ui/assets/{diagram-S2PKOQOG-DHEZYivc.js → diagram-S2PKOQOG-cp3l22vN.js} +1 -1
- package/dist/ui/assets/diagram-S2PKOQOG-cp3l22vN.js.gz +0 -0
- package/dist/ui/assets/editor-Cr0W5BQI.js +39 -0
- package/dist/ui/assets/editor-Cr0W5BQI.js.gz +0 -0
- package/dist/ui/assets/{emoji-B0hLmSLZ.js → emoji-Bv3m24sO.js} +1 -1
- package/dist/ui/assets/emoji-Bv3m24sO.js.gz +0 -0
- package/dist/ui/assets/{erDiagram-Q2GNP2WA-BVNeTZPQ.js → erDiagram-Q2GNP2WA-CQSXRgqr.js} +1 -1
- package/dist/ui/assets/erDiagram-Q2GNP2WA-CQSXRgqr.js.gz +0 -0
- package/dist/ui/assets/{flowDiagram-NV44I4VS-C73VFdyk.js → flowDiagram-NV44I4VS-CKcKpkph.js} +1 -1
- package/dist/ui/assets/flowDiagram-NV44I4VS-CKcKpkph.js.gz +0 -0
- package/dist/ui/assets/{ganttDiagram-LVOFAZNH-B_RosuD1.js → ganttDiagram-LVOFAZNH-DlAHzXO5.js} +1 -1
- package/dist/ui/assets/ganttDiagram-LVOFAZNH-DlAHzXO5.js.gz +0 -0
- package/dist/ui/assets/{gitGraph-F6HP7TQM-DMFIqn4h.js → gitGraph-F6HP7TQM-DeuXy4B1.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-NY62KEGX-hxbgwNA1.js → gitGraphDiagram-NY62KEGX-aB-AI4Vq.js} +1 -1
- package/dist/ui/assets/gitGraphDiagram-NY62KEGX-aB-AI4Vq.js.gz +0 -0
- package/dist/ui/assets/index-B-qHc0o5.js +63 -0
- package/dist/ui/assets/index-B-qHc0o5.js.gz +0 -0
- package/dist/ui/assets/{info-NVLQJR56-DZ3K-w48.js → info-NVLQJR56-B9PG7xsc.js} +1 -1
- package/dist/ui/assets/{infoDiagram-ER5ION4S-CxnBJQu6.js → infoDiagram-ER5ION4S-C5Wpoqim.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-XKPGCS4Q-CHYNj-f8.js → journeyDiagram-XKPGCS4Q-CHLziqfS.js} +1 -1
- package/dist/ui/assets/journeyDiagram-XKPGCS4Q-CHLziqfS.js.gz +0 -0
- package/dist/ui/assets/{kanban-definition-3W4ZIXB7-DQPCCsj2.js → kanban-definition-3W4ZIXB7-Dq7GzQE_.js} +1 -1
- package/dist/ui/assets/kanban-definition-3W4ZIXB7-Dq7GzQE_.js.gz +0 -0
- package/dist/ui/assets/{line-DBRjKmfa.js → line-DHKNg83I.js} +1 -1
- package/dist/ui/assets/{linear-DFao5Wyp.js → linear-BvUfn0I8.js} +1 -1
- package/dist/ui/assets/linear-BvUfn0I8.js.gz +0 -0
- package/dist/ui/assets/{mermaid-parser.core-DYCfacHj.js → mermaid-parser.core-C6d9tKoo.js} +2 -2
- package/dist/ui/assets/mermaid-parser.core-C6d9tKoo.js.gz +0 -0
- package/dist/ui/assets/{mermaid.core-C6xGFSdE.js → mermaid.core-CxE5KezR.js} +3 -3
- package/dist/ui/assets/mermaid.core-CxE5KezR.js.gz +0 -0
- package/dist/ui/assets/{mindmap-definition-VGOIOE7T-L3Pw-YM7.js → mindmap-definition-VGOIOE7T-DmPoQ48w.js} +1 -1
- package/dist/ui/assets/mindmap-definition-VGOIOE7T-DmPoQ48w.js.gz +0 -0
- package/dist/ui/assets/{packet-BFZMPI3H-CpL6iNoa.js → packet-BFZMPI3H-JZEkVZxU.js} +1 -1
- package/dist/ui/assets/{particles-CT8hcJOA.js → particles-CxMmUw86.js} +1 -1
- package/dist/ui/assets/particles-CxMmUw86.js.gz +0 -0
- package/dist/ui/assets/{pie-7BOR55EZ-hTqChbTo.js → pie-7BOR55EZ-_1II5pYQ.js} +1 -1
- package/dist/ui/assets/{pieDiagram-ADFJNKIX-Bf4Nou75.js → pieDiagram-ADFJNKIX-DrNkH33i.js} +1 -1
- package/dist/ui/assets/pieDiagram-ADFJNKIX-DrNkH33i.js.gz +0 -0
- package/dist/ui/assets/{quadrantDiagram-AYHSOK5B-DpjUTLB1.js → quadrantDiagram-AYHSOK5B-DOpC-HEF.js} +1 -1
- package/dist/ui/assets/quadrantDiagram-AYHSOK5B-DOpC-HEF.js.gz +0 -0
- package/dist/ui/assets/{radar-NHE76QYJ-myy7OENC.js → radar-NHE76QYJ-DDmqhzEZ.js} +1 -1
- package/dist/ui/assets/{reactflow-Cubtnk8i.js → reactflow-D98uPOMq.js} +1 -1
- package/dist/ui/assets/reactflow-D98uPOMq.js.gz +0 -0
- package/dist/ui/assets/{requirementDiagram-UZGBJVZJ-Bfrfh-0-.js → requirementDiagram-UZGBJVZJ-Bzx0iEPP.js} +1 -1
- package/dist/ui/assets/requirementDiagram-UZGBJVZJ-Bzx0iEPP.js.gz +0 -0
- package/dist/ui/assets/{sandpack-C58bwfIS.js → sandpack-059Z4UWP.js} +1 -1
- package/dist/ui/assets/sandpack-059Z4UWP.js.gz +0 -0
- package/dist/ui/assets/sandpackDefaults-CPMNDsw7.js +1 -0
- package/dist/ui/assets/sandpackDefaults-CPMNDsw7.js.gz +0 -0
- package/dist/ui/assets/{sankeyDiagram-TZEHDZUN-ClZAZ6va.js → sankeyDiagram-TZEHDZUN-Do9pvfDv.js} +1 -1
- package/dist/ui/assets/sankeyDiagram-TZEHDZUN-Do9pvfDv.js.gz +0 -0
- package/dist/ui/assets/{sequenceDiagram-WL72ISMW-CNgly63p.js → sequenceDiagram-WL72ISMW-CN8neUTA.js} +1 -1
- package/dist/ui/assets/sequenceDiagram-WL72ISMW-CN8neUTA.js.gz +0 -0
- package/dist/ui/assets/{src-DAeyG22O.js → src-DEbsv3pp.js} +2 -2
- package/dist/ui/assets/src-DEbsv3pp.js.gz +0 -0
- package/dist/ui/assets/{stateDiagram-FKZM4ZOC-BrEAoDpE.js → stateDiagram-FKZM4ZOC-BluWJi8n.js} +1 -1
- package/dist/ui/assets/stateDiagram-FKZM4ZOC-BluWJi8n.js.gz +0 -0
- package/dist/ui/assets/stateDiagram-v2-4FDKWEC3-DeyqBMDR.js +1 -0
- package/dist/ui/assets/{streamdown-BdAVfkeF.js → streamdown-BT1twtFc.js} +5 -5
- package/dist/ui/assets/streamdown-BT1twtFc.js.gz +0 -0
- package/dist/ui/assets/{syntax-BOfTnQiy.js → syntax-CBu6nLfm.js} +1 -1
- package/dist/ui/assets/syntax-CBu6nLfm.js.gz +0 -0
- package/dist/ui/assets/{timeline-definition-IT6M3QCI-CaRH3k4l.js → timeline-definition-IT6M3QCI-WdUNkiRj.js} +1 -1
- package/dist/ui/assets/timeline-definition-IT6M3QCI-WdUNkiRj.js.gz +0 -0
- package/dist/ui/assets/{treemap-KMMF4GRG-CUaLOV_B.js → treemap-KMMF4GRG-CoriU4Jl.js} +1 -1
- package/dist/ui/assets/{useUserLocalStorage-C8sgXgXR.js → useUserLocalStorage-GidNiOPC.js} +1 -1
- package/dist/ui/assets/useUserLocalStorage-GidNiOPC.js.gz +0 -0
- package/dist/ui/assets/{xychartDiagram-PRI3JC2R-BlhkI9T_.js → xychartDiagram-PRI3JC2R-CgBAKNt2.js} +1 -1
- package/dist/ui/assets/xychartDiagram-PRI3JC2R-CgBAKNt2.js.gz +0 -0
- package/dist/ui/index.html +9 -8
- package/dist/ui/index.html.gz +0 -0
- package/package.json +2 -2
- package/dist/ui/assets/AgorEmojiPickerInner-DQjr8M3_.js +0 -1
- package/dist/ui/assets/App-hJtcOxHv.js +0 -5
- package/dist/ui/assets/App-hJtcOxHv.js.gz +0 -0
- package/dist/ui/assets/AppNode-BhuMpn8O.js +0 -1
- package/dist/ui/assets/AppNode-BhuMpn8O.js.gz +0 -0
- package/dist/ui/assets/ArtifactConsentModal-oMMzRn8L.js.gz +0 -0
- package/dist/ui/assets/ArtifactFullscreenPage-B3wT8t8f.js +0 -9
- package/dist/ui/assets/ArtifactFullscreenPage-B3wT8t8f.js.gz +0 -0
- package/dist/ui/assets/ArtifactNode-BzQcrwFS.js +0 -9
- package/dist/ui/assets/ArtifactNode-BzQcrwFS.js.gz +0 -0
- package/dist/ui/assets/AutocompleteTextarea-Dau-V_30.js +0 -19
- package/dist/ui/assets/AutocompleteTextarea-Dau-V_30.js.gz +0 -0
- package/dist/ui/assets/BoardObjectNodes-DdKWv_ZT.js +0 -34
- package/dist/ui/assets/BoardObjectNodes-DdKWv_ZT.js.gz +0 -0
- package/dist/ui/assets/CodeEditor.inner-Cuj6RuLL.js.gz +0 -0
- package/dist/ui/assets/ConversationView-9-3ma1u-.js +0 -1
- package/dist/ui/assets/ConversationView-9-3ma1u-.js.gz +0 -0
- package/dist/ui/assets/EmbeddedTerminal-Lx5I9EOJ.js.gz +0 -0
- package/dist/ui/assets/KnowledgePage-Dz8YAMYC.js +0 -24
- package/dist/ui/assets/KnowledgePage-Dz8YAMYC.js.gz +0 -0
- package/dist/ui/assets/MarketingScreenshotPage-CmDPBHOl.js.gz +0 -0
- package/dist/ui/assets/MobileApp-BpgSG_gz.js +0 -1
- package/dist/ui/assets/MobileApp-BpgSG_gz.js.gz +0 -0
- package/dist/ui/assets/SessionCanvas-B6_mtlxK.js +0 -13
- package/dist/ui/assets/SessionCanvas-B6_mtlxK.js.gz +0 -0
- package/dist/ui/assets/StreamdownDemoPage-okKKR2QH.js.gz +0 -0
- package/dist/ui/assets/TerminalModal-D9jMGZXv.js.gz +0 -0
- package/dist/ui/assets/ThemeSwitcher-D9HgT37l.js +0 -1
- package/dist/ui/assets/ThemeSwitcher-D9HgT37l.js.gz +0 -0
- package/dist/ui/assets/architecture-U656AL7Q-DMnxkQRZ.js +0 -1
- package/dist/ui/assets/architectureDiagram-VXUJARFQ-DwTJGGoj.js.gz +0 -0
- package/dist/ui/assets/blockDiagram-VD42YOAC-Dh1_TVW2.js.gz +0 -0
- package/dist/ui/assets/c4Diagram-YG6GDRKO-B1e96AcB.js.gz +0 -0
- package/dist/ui/assets/channel-Jv4dT4S5.js +0 -1
- package/dist/ui/assets/chunk-55IACEB6-lJXvUquf.js +0 -1
- package/dist/ui/assets/chunk-ABZYJK2D-DCTXWSzV.js.gz +0 -0
- package/dist/ui/assets/chunk-AGHRB4JF-CbNphBjE.js.gz +0 -0
- package/dist/ui/assets/chunk-ATLVNIR6-rWg3qxof.js.gz +0 -0
- package/dist/ui/assets/chunk-B4BG7PRW-CgtLCbpE.js.gz +0 -0
- package/dist/ui/assets/chunk-DI55MBZ5-Dwvx2Kva.js.gz +0 -0
- package/dist/ui/assets/chunk-EXTU4WIE-DHyaqTsP.js +0 -1
- package/dist/ui/assets/chunk-HN2XXSSU-2blQO3Gb.js.gz +0 -0
- package/dist/ui/assets/chunk-JA3XYJ7Z-Cr1rmOui.js.gz +0 -0
- package/dist/ui/assets/chunk-JZLCHNYA-BsgZ79Fa.js.gz +0 -0
- package/dist/ui/assets/chunk-MI3HLSF2-dwYeUy5S.js.gz +0 -0
- package/dist/ui/assets/chunk-N4CR4FBY-DEMgSRVe.js +0 -2
- package/dist/ui/assets/chunk-N4CR4FBY-DEMgSRVe.js.gz +0 -0
- package/dist/ui/assets/chunk-QXUST7PY-BCK2V0i-.js.gz +0 -0
- package/dist/ui/assets/chunk-QZHKN3VN-CALjGrEE.js +0 -1
- package/dist/ui/assets/chunk-S3R3BYOJ-GP3zIxrB.js.gz +0 -0
- package/dist/ui/assets/chunk-TZMSLE5B-dX5RaM6g.js.gz +0 -0
- package/dist/ui/assets/classDiagram-2ON5EDUG-C3_RVP70.js +0 -1
- package/dist/ui/assets/classDiagram-v2-WZHVMYZB-C3_RVP70.js +0 -1
- package/dist/ui/assets/constants-GjIDNVyt.js +0 -1
- package/dist/ui/assets/cose-bilkent-S5V4N54A-Dx0BJVDl.js.gz +0 -0
- package/dist/ui/assets/dagre-6UL2VRFP-DiQSUxqN.js.gz +0 -0
- package/dist/ui/assets/dagre-Bekd9Vdj.js.gz +0 -0
- package/dist/ui/assets/diagram-PSM6KHXK-DtJnDQjc.js.gz +0 -0
- package/dist/ui/assets/diagram-QEK2KX5R-CobkCRdj.js.gz +0 -0
- package/dist/ui/assets/diagram-S2PKOQOG-DHEZYivc.js.gz +0 -0
- package/dist/ui/assets/editor-DQIG-JYZ.js +0 -43
- package/dist/ui/assets/editor-DQIG-JYZ.js.gz +0 -0
- package/dist/ui/assets/emoji-B0hLmSLZ.js.gz +0 -0
- package/dist/ui/assets/erDiagram-Q2GNP2WA-BVNeTZPQ.js.gz +0 -0
- package/dist/ui/assets/flowDiagram-NV44I4VS-C73VFdyk.js.gz +0 -0
- package/dist/ui/assets/ganttDiagram-LVOFAZNH-B_RosuD1.js.gz +0 -0
- package/dist/ui/assets/gitGraphDiagram-NY62KEGX-hxbgwNA1.js.gz +0 -0
- package/dist/ui/assets/index-DTz1Dif6.js +0 -64
- package/dist/ui/assets/index-DTz1Dif6.js.gz +0 -0
- package/dist/ui/assets/journeyDiagram-XKPGCS4Q-CHYNj-f8.js.gz +0 -0
- package/dist/ui/assets/kanban-definition-3W4ZIXB7-DQPCCsj2.js.gz +0 -0
- package/dist/ui/assets/linear-DFao5Wyp.js.gz +0 -0
- package/dist/ui/assets/mermaid-parser.core-DYCfacHj.js.gz +0 -0
- package/dist/ui/assets/mermaid.core-C6xGFSdE.js.gz +0 -0
- package/dist/ui/assets/mindmap-definition-VGOIOE7T-L3Pw-YM7.js.gz +0 -0
- package/dist/ui/assets/particles-CT8hcJOA.js.gz +0 -0
- package/dist/ui/assets/pieDiagram-ADFJNKIX-Bf4Nou75.js.gz +0 -0
- package/dist/ui/assets/quadrantDiagram-AYHSOK5B-DpjUTLB1.js.gz +0 -0
- package/dist/ui/assets/reactflow-Cubtnk8i.js.gz +0 -0
- package/dist/ui/assets/requirementDiagram-UZGBJVZJ-Bfrfh-0-.js.gz +0 -0
- package/dist/ui/assets/sandpack-C58bwfIS.js.gz +0 -0
- package/dist/ui/assets/sandpackDefaults-Bkga8XAE.js +0 -1
- package/dist/ui/assets/sankeyDiagram-TZEHDZUN-ClZAZ6va.js.gz +0 -0
- package/dist/ui/assets/sequenceDiagram-WL72ISMW-CNgly63p.js.gz +0 -0
- package/dist/ui/assets/src-DAeyG22O.js.gz +0 -0
- package/dist/ui/assets/stateDiagram-FKZM4ZOC-BrEAoDpE.js.gz +0 -0
- package/dist/ui/assets/stateDiagram-v2-4FDKWEC3-BNL52_oN.js +0 -1
- package/dist/ui/assets/streamdown-BdAVfkeF.js.gz +0 -0
- package/dist/ui/assets/syntax-BOfTnQiy.js.gz +0 -0
- package/dist/ui/assets/timeline-definition-IT6M3QCI-CaRH3k4l.js.gz +0 -0
- package/dist/ui/assets/useUserLocalStorage-C8sgXgXR.js.gz +0 -0
- package/dist/ui/assets/xychartDiagram-PRI3JC2R-BlhkI9T_.js.gz +0 -0
package/dist/daemon/main.js
CHANGED
|
@@ -31,6 +31,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
mod
|
|
32
32
|
));
|
|
33
33
|
|
|
34
|
+
// src/auth/executor-session-token.ts
|
|
35
|
+
function isExecutorSessionTokenPayload(payload) {
|
|
36
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return false;
|
|
37
|
+
const record2 = payload;
|
|
38
|
+
return record2.type === EXECUTOR_SESSION_TOKEN_TYPE && record2.purpose === EXECUTOR_SESSION_TOKEN_PURPOSE;
|
|
39
|
+
}
|
|
40
|
+
function getExecutorSessionTokenSessionId(payload) {
|
|
41
|
+
return payload.session_id ?? payload.sessionId;
|
|
42
|
+
}
|
|
43
|
+
var EXECUTOR_SESSION_TOKEN_TYPE, EXECUTOR_SESSION_TOKEN_PURPOSE;
|
|
44
|
+
var init_executor_session_token = __esm({
|
|
45
|
+
"src/auth/executor-session-token.ts"() {
|
|
46
|
+
"use strict";
|
|
47
|
+
EXECUTOR_SESSION_TOKEN_TYPE = "executor-session";
|
|
48
|
+
EXECUTOR_SESSION_TOKEN_PURPOSE = "executor-task";
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
34
52
|
// src/utils/branch-authorization.ts
|
|
35
53
|
import { shortId } from "@agor/core/db";
|
|
36
54
|
import { Forbidden as Forbidden2, NotAuthenticated, NotFound } from "@agor/core/feathers";
|
|
@@ -2648,7 +2666,7 @@ import {
|
|
|
2648
2666
|
MCP_TOKEN_AUDIENCE,
|
|
2649
2667
|
MCP_TOKEN_ISSUER
|
|
2650
2668
|
} from "@agor/core/types";
|
|
2651
|
-
import
|
|
2669
|
+
import jwt4 from "jsonwebtoken";
|
|
2652
2670
|
import { MCP_TOKEN_AUDIENCE as MCP_TOKEN_AUDIENCE2, MCP_TOKEN_ISSUER as MCP_TOKEN_ISSUER2 } from "@agor/core/types";
|
|
2653
2671
|
function mcpTokenDebug(...args) {
|
|
2654
2672
|
if (DEBUG_MCP_TOKENS) {
|
|
@@ -2720,7 +2738,7 @@ async function generateSessionToken2(app, sessionId, userId) {
|
|
|
2720
2738
|
exp: expSec,
|
|
2721
2739
|
jti
|
|
2722
2740
|
};
|
|
2723
|
-
const token =
|
|
2741
|
+
const token = jwt4.sign(payload, jwtSecret, { algorithm: "HS256" });
|
|
2724
2742
|
s.tokenCache.set(cacheKey, { token, expiresAtMs });
|
|
2725
2743
|
mcpTokenDebug(
|
|
2726
2744
|
`\u{1F3AB} MCP token issued: session=${shortId3(sessionId)} jti=${jti.substring(0, 8)} exp=+${Math.floor(s.expirationMs / 1e3)}s`
|
|
@@ -2736,15 +2754,15 @@ async function validateSessionToken(app, token) {
|
|
|
2736
2754
|
}
|
|
2737
2755
|
let payload;
|
|
2738
2756
|
try {
|
|
2739
|
-
payload =
|
|
2757
|
+
payload = jwt4.verify(token, jwtSecret, {
|
|
2740
2758
|
audience: MCP_TOKEN_AUDIENCE,
|
|
2741
2759
|
issuer: MCP_TOKEN_ISSUER,
|
|
2742
2760
|
algorithms: ["HS256"]
|
|
2743
2761
|
});
|
|
2744
2762
|
} catch (err) {
|
|
2745
|
-
if (err instanceof
|
|
2763
|
+
if (err instanceof jwt4.TokenExpiredError) {
|
|
2746
2764
|
console.warn("[mcp-tokens] token rejected: expired");
|
|
2747
|
-
} else if (err instanceof
|
|
2765
|
+
} else if (err instanceof jwt4.JsonWebTokenError) {
|
|
2748
2766
|
console.warn(`[mcp-tokens] token rejected: ${err.message}`);
|
|
2749
2767
|
} else {
|
|
2750
2768
|
console.error("[mcp-tokens] token verify error:", err);
|
|
@@ -3213,12 +3231,12 @@ import {
|
|
|
3213
3231
|
permissionModeForCli,
|
|
3214
3232
|
slugForCwd
|
|
3215
3233
|
} from "@agor/core/claude-cli";
|
|
3216
|
-
import { resolveMultiTenancyConfig as
|
|
3234
|
+
import { resolveMultiTenancyConfig as resolveMultiTenancyConfig3 } from "@agor/core/config";
|
|
3217
3235
|
import {
|
|
3218
3236
|
generateId as generateId4,
|
|
3219
3237
|
getCurrentTenantId as getCurrentTenantId3,
|
|
3220
3238
|
runWithoutTenantDatabaseScope as runWithoutTenantDatabaseScope2,
|
|
3221
|
-
runWithTenantDatabaseScope as
|
|
3239
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope4,
|
|
3222
3240
|
SessionRepository as SessionRepository3,
|
|
3223
3241
|
shortId as shortId4,
|
|
3224
3242
|
TaskRepository
|
|
@@ -3238,7 +3256,7 @@ function getDb(app) {
|
|
|
3238
3256
|
}
|
|
3239
3257
|
function getCliFallbackTenantId(app) {
|
|
3240
3258
|
const config2 = app.get("config");
|
|
3241
|
-
return
|
|
3259
|
+
return resolveMultiTenancyConfig3(config2 ?? {}).static_tenant_id;
|
|
3242
3260
|
}
|
|
3243
3261
|
function captureCliWatcherTenantId(app) {
|
|
3244
3262
|
return getCurrentTenantId3() ?? getCliFallbackTenantId(app);
|
|
@@ -3250,7 +3268,7 @@ async function runCliCallbackDatabaseScope(app, sessionId, work) {
|
|
|
3250
3268
|
const db = getDb(app);
|
|
3251
3269
|
if (!db) return work();
|
|
3252
3270
|
const tenantId = cliWatcherTenantBySession.get(sessionId) ?? captureCliWatcherTenantId(app);
|
|
3253
|
-
return
|
|
3271
|
+
return runWithTenantDatabaseScope4(db, tenantId, work);
|
|
3254
3272
|
}
|
|
3255
3273
|
async function persistActiveTurnSnapshot(app, sessionId, turn) {
|
|
3256
3274
|
const db = getDb(app);
|
|
@@ -4229,7 +4247,7 @@ __export(service_jwt_strategy_exports, {
|
|
|
4229
4247
|
ServiceJWTStrategy: () => ServiceJWTStrategy
|
|
4230
4248
|
});
|
|
4231
4249
|
import { JWTStrategy } from "@agor/core/feathers";
|
|
4232
|
-
import
|
|
4250
|
+
import jwt8 from "jsonwebtoken";
|
|
4233
4251
|
function persistExecutorJwtPayloadOnConnection(params, accessToken, payload) {
|
|
4234
4252
|
const connection = params?.connection;
|
|
4235
4253
|
if (!connection) return;
|
|
@@ -4258,6 +4276,7 @@ var init_service_jwt_strategy = __esm({
|
|
|
4258
4276
|
"src/auth/service-jwt-strategy.ts"() {
|
|
4259
4277
|
"use strict";
|
|
4260
4278
|
init_users();
|
|
4279
|
+
init_executor_session_token();
|
|
4261
4280
|
init_runtime_tokens();
|
|
4262
4281
|
init_token_invalidation();
|
|
4263
4282
|
ServiceJWTStrategy = class extends JWTStrategy {
|
|
@@ -4299,7 +4318,7 @@ var init_service_jwt_strategy = __esm({
|
|
|
4299
4318
|
*/
|
|
4300
4319
|
// biome-ignore lint/suspicious/noExplicitAny: Feathers type compatibility
|
|
4301
4320
|
async authenticate(authentication, params) {
|
|
4302
|
-
const decoded =
|
|
4321
|
+
const decoded = jwt8.decode(authentication?.accessToken);
|
|
4303
4322
|
propagateTenantFromJwtPayload(params, decoded, this.tenantClaim);
|
|
4304
4323
|
const result = await super.authenticate(authentication, params);
|
|
4305
4324
|
const payload = result.authentication?.payload;
|
|
@@ -4318,14 +4337,14 @@ var init_service_jwt_strategy = __esm({
|
|
|
4318
4337
|
};
|
|
4319
4338
|
}
|
|
4320
4339
|
if (payload?.type === "executor-session") {
|
|
4321
|
-
if (payload
|
|
4340
|
+
if (!isExecutorSessionTokenPayload(payload)) {
|
|
4322
4341
|
throw new Error("Invalid executor token purpose");
|
|
4323
4342
|
}
|
|
4324
4343
|
const token = authentication?.accessToken;
|
|
4325
4344
|
if (!token || !this.sessionTokenService) {
|
|
4326
4345
|
throw new Error("Executor token validation unavailable");
|
|
4327
4346
|
}
|
|
4328
|
-
const sessionId = payload
|
|
4347
|
+
const sessionId = getExecutorSessionTokenSessionId(payload);
|
|
4329
4348
|
const sessionInfo = await this.sessionTokenService.validateToken(token, {
|
|
4330
4349
|
sessionId,
|
|
4331
4350
|
taskId: payload.task_id,
|
|
@@ -17672,7 +17691,7 @@ __export(schemas_exports2, {
|
|
|
17672
17691
|
ipv4: () => ipv42,
|
|
17673
17692
|
ipv6: () => ipv62,
|
|
17674
17693
|
json: () => json,
|
|
17675
|
-
jwt: () =>
|
|
17694
|
+
jwt: () => jwt9,
|
|
17676
17695
|
keyof: () => keyof,
|
|
17677
17696
|
ksuid: () => ksuid2,
|
|
17678
17697
|
lazy: () => lazy,
|
|
@@ -17839,7 +17858,7 @@ function base64url2(params) {
|
|
|
17839
17858
|
function e1642(params) {
|
|
17840
17859
|
return _e164(ZodE164, params);
|
|
17841
17860
|
}
|
|
17842
|
-
function
|
|
17861
|
+
function jwt9(params) {
|
|
17843
17862
|
return _jwt(ZodJWT, params);
|
|
17844
17863
|
}
|
|
17845
17864
|
function stringFormat(format, fnOrRegex, _params = {}) {
|
|
@@ -19729,7 +19748,7 @@ __export(external_exports, {
|
|
|
19729
19748
|
ipv6: () => ipv62,
|
|
19730
19749
|
iso: () => iso_exports2,
|
|
19731
19750
|
json: () => json,
|
|
19732
|
-
jwt: () =>
|
|
19751
|
+
jwt: () => jwt9,
|
|
19733
19752
|
keyof: () => keyof,
|
|
19734
19753
|
ksuid: () => ksuid2,
|
|
19735
19754
|
lazy: () => lazy,
|
|
@@ -22863,11 +22882,15 @@ import {
|
|
|
22863
22882
|
ThreadSessionMapRepository
|
|
22864
22883
|
} from "@agor/core/db";
|
|
22865
22884
|
import {
|
|
22866
|
-
|
|
22885
|
+
buildSlackManifest,
|
|
22886
|
+
getConnector,
|
|
22887
|
+
requiredBotEvents,
|
|
22888
|
+
requiredBotScopes
|
|
22867
22889
|
} from "@agor/core/gateway";
|
|
22868
22890
|
import {
|
|
22869
22891
|
GATEWAY_REDACTED_SENTINEL as GATEWAY_REDACTED_SENTINEL2,
|
|
22870
22892
|
GATEWAY_SENSITIVE_CONFIG_FIELDS as GATEWAY_SENSITIVE_CONFIG_FIELDS2,
|
|
22893
|
+
getRequiredSecretFields,
|
|
22871
22894
|
hasMinimumRole as hasMinimumRole10,
|
|
22872
22895
|
ROLES as ROLES12
|
|
22873
22896
|
} from "@agor/core/types";
|
|
@@ -22876,6 +22899,19 @@ function requireAdmin3(ctx, action) {
|
|
|
22876
22899
|
throw new Error(`Access denied: admin role required to ${action}`);
|
|
22877
22900
|
}
|
|
22878
22901
|
}
|
|
22902
|
+
async function resolveCallerSessionBranchId(ctx) {
|
|
22903
|
+
if (!ctx.sessionId) return null;
|
|
22904
|
+
const session = await new SessionRepository6(ctx.db).findById(ctx.sessionId);
|
|
22905
|
+
if (!session) {
|
|
22906
|
+
throw new Error("Gateway access denied: calling session not found");
|
|
22907
|
+
}
|
|
22908
|
+
return session.branch_id;
|
|
22909
|
+
}
|
|
22910
|
+
function sessionBranchReadDeniedError() {
|
|
22911
|
+
return new Error(
|
|
22912
|
+
"Gateway read denied: this Slack thread belongs to a gateway channel targeting a different branch than the calling session's. Sessions can read Slack thread history only through gateway channels whose target branch matches their own."
|
|
22913
|
+
);
|
|
22914
|
+
}
|
|
22879
22915
|
async function canUseGatewayOutbound(ctx, branchRepo, branch) {
|
|
22880
22916
|
if (hasMinimumRole10(ctx.authenticatedUser?.role, ROLES12.ADMIN)) return true;
|
|
22881
22917
|
const userId = ctx.userId;
|
|
@@ -22956,16 +22992,23 @@ async function resolveSlackThreadHistoryTarget(ctx, args) {
|
|
|
22956
22992
|
const channelRepo = new GatewayChannelRepository2(ctx.db);
|
|
22957
22993
|
const threadMapRepo = new ThreadSessionMapRepository(ctx.db);
|
|
22958
22994
|
const branchRepo = new BranchRepository5(ctx.db);
|
|
22995
|
+
const callerSessionBranchId = await resolveCallerSessionBranchId(ctx);
|
|
22959
22996
|
if (args.sessionId) {
|
|
22960
22997
|
const session = await ctx.app.service("sessions").get(args.sessionId, ctx.baseServiceParams);
|
|
22961
22998
|
const mapping2 = await threadMapRepo.findBySession(session.session_id);
|
|
22962
22999
|
if (!mapping2) {
|
|
22963
23000
|
throw new Error(`No gateway thread mapping found for session ${session.session_id}.`);
|
|
22964
23001
|
}
|
|
23002
|
+
if (callerSessionBranchId && mapping2.branch_id !== callerSessionBranchId) {
|
|
23003
|
+
throw sessionBranchReadDeniedError();
|
|
23004
|
+
}
|
|
22965
23005
|
const channel2 = await channelRepo.findById(mapping2.channel_id);
|
|
22966
23006
|
if (!channel2) {
|
|
22967
23007
|
throw new Error(`Gateway channel not found for session mapping ${mapping2.id}.`);
|
|
22968
23008
|
}
|
|
23009
|
+
if (callerSessionBranchId && channel2.target_branch_id !== callerSessionBranchId) {
|
|
23010
|
+
throw sessionBranchReadDeniedError();
|
|
23011
|
+
}
|
|
22969
23012
|
const branch2 = await branchRepo.findById(mapping2.branch_id);
|
|
22970
23013
|
return {
|
|
22971
23014
|
channel: channel2,
|
|
@@ -22980,6 +23023,9 @@ async function resolveSlackThreadHistoryTarget(ctx, args) {
|
|
|
22980
23023
|
if (!channel) {
|
|
22981
23024
|
throw new Error(`Gateway channel not found: ${args.gatewayChannelId}`);
|
|
22982
23025
|
}
|
|
23026
|
+
if (callerSessionBranchId && channel.target_branch_id !== callerSessionBranchId) {
|
|
23027
|
+
throw sessionBranchReadDeniedError();
|
|
23028
|
+
}
|
|
22983
23029
|
const mapping = await threadMapRepo.findByChannelAndThread(channel.id, args.threadId);
|
|
22984
23030
|
if (mapping) {
|
|
22985
23031
|
const branch2 = await branchRepo.findById(mapping.branch_id);
|
|
@@ -23073,6 +23119,34 @@ function toServiceUpdateData(args) {
|
|
|
23073
23119
|
}
|
|
23074
23120
|
return updates;
|
|
23075
23121
|
}
|
|
23122
|
+
function toSlackWizardOptions(args) {
|
|
23123
|
+
return {
|
|
23124
|
+
appName: args.appName,
|
|
23125
|
+
...args.botDisplayName ? { botDisplayName: args.botDisplayName } : {},
|
|
23126
|
+
publicChannels: args.publicChannels,
|
|
23127
|
+
privateChannels: args.privateChannels,
|
|
23128
|
+
groupDms: args.groupDms,
|
|
23129
|
+
alignUsers: args.alignUsers,
|
|
23130
|
+
outbound: args.outbound
|
|
23131
|
+
};
|
|
23132
|
+
}
|
|
23133
|
+
function toCreateChannelConfigHint(args) {
|
|
23134
|
+
const config2 = {
|
|
23135
|
+
connection_mode: "socket",
|
|
23136
|
+
enable_channels: args.publicChannels,
|
|
23137
|
+
enable_groups: args.privateChannels,
|
|
23138
|
+
enable_mpim: args.groupDms,
|
|
23139
|
+
align_slack_users: args.alignUsers,
|
|
23140
|
+
outbound_enabled: args.outbound
|
|
23141
|
+
};
|
|
23142
|
+
if (args.restrictToChannelIds && args.restrictToChannelIds.length > 0) {
|
|
23143
|
+
config2.allowed_channel_ids = args.restrictToChannelIds;
|
|
23144
|
+
}
|
|
23145
|
+
return { channelType: "slack", config: config2 };
|
|
23146
|
+
}
|
|
23147
|
+
function identityModeSetupStep(alignUsers) {
|
|
23148
|
+
return alignUsers ? "Identity: the channel is set to align Slack users \u2014 each Slack user runs as their matched Agor account and unmatched users are rejected, so no run-as user is needed. Tell me if you would rather every message run as one fixed Agor user (that requires picking a user and passing agorUserId)." : "Identity: the channel is set to run every message as one fixed Agor user, which requires passing agorUserId to agor_gateway_channels_create. Tell me if you would rather align Slack users so each runs as their own matched Agor account instead.";
|
|
23149
|
+
}
|
|
23076
23150
|
function registerGatewayChannelTools(server, ctx) {
|
|
23077
23151
|
server.registerTool(
|
|
23078
23152
|
"agor_gateway_channels_list",
|
|
@@ -23118,7 +23192,7 @@ function registerGatewayChannelTools(server, ctx) {
|
|
|
23118
23192
|
server.registerTool(
|
|
23119
23193
|
"agor_gateway_channels_create",
|
|
23120
23194
|
{
|
|
23121
|
-
description: 'Create a gateway channel definition (admin-only) through the same gateway-channels service used by the UI. Current connectors: Slack, GitHub, Teams. Slack example config: { bot_token, app_token, connection_mode:"socket", enable_channels:true, require_mention:true, allowed_channel_ids:["C123"] }. Secrets are encrypted by the service and returned redacted
|
|
23195
|
+
description: 'Create a gateway channel definition (admin-only) through the same gateway-channels service used by the UI. Current connectors: Slack, GitHub, Teams. For interactive/agent-driven setup, create the channel disabled and without secrets (enabled:false, no tokens), then collect credentials with agor_widgets_request_gateway_token so the user enters them in a secure inline form \u2014 raw secrets passed in tool arguments leak into the MCP transcript. Passing secrets directly here is for programmatic/non-interactive use only. Non-interactive Slack example config: { bot_token, app_token, connection_mode:"socket", enable_channels:true, require_mention:true, allowed_channel_ids:["C123"] }. Secrets are encrypted by the service and returned redacted.',
|
|
23122
23196
|
annotations: { destructiveHint: false, idempotentHint: false },
|
|
23123
23197
|
inputSchema: gatewayChannelCreateSchema
|
|
23124
23198
|
},
|
|
@@ -23128,12 +23202,50 @@ function registerGatewayChannelTools(server, ctx) {
|
|
|
23128
23202
|
return textResult({
|
|
23129
23203
|
gateway_channel: redactGatewayChannel(created),
|
|
23130
23204
|
next_steps: [
|
|
23205
|
+
"If this channel was created disabled without tokens (the recommended interactive path), collect its credentials by calling agor_widgets_request_gateway_token for this channel so the user enters the tokens in the secure form that appears at the end of this message. Do NOT ask the user to paste xoxb-/xapp- tokens into the chat, and do NOT pass tokens as agor_gateway_channels_create arguments during interactive setup.",
|
|
23206
|
+
...created.channel_type === "slack" ? [
|
|
23207
|
+
identityModeSetupStep(
|
|
23208
|
+
created.config?.align_slack_users === true
|
|
23209
|
+
)
|
|
23210
|
+
] : [],
|
|
23131
23211
|
"Verify the channel in Settings > Gateway Channels or with agor_gateway_channels_list.",
|
|
23132
23212
|
"Channel credentials, env vars, and inbound channel keys are intentionally redacted from MCP responses."
|
|
23133
23213
|
]
|
|
23134
23214
|
});
|
|
23135
23215
|
}
|
|
23136
23216
|
);
|
|
23217
|
+
server.registerTool(
|
|
23218
|
+
"agor_gateway_slack_manifest_generate",
|
|
23219
|
+
{
|
|
23220
|
+
description: "Generate a ready-to-install Slack app manifest from desired gateway capabilities (admin-only). The agent-driven equivalent of the Slack setup wizard, backed by the same core generator. Returns the manifest JSON, the derived bot scopes/events, ordered setup steps, and the channel config to pass to agor_gateway_channels_create. This is pure: it creates no Slack app, no Agor channel, and validates no tokens.",
|
|
23221
|
+
annotations: { readOnlyHint: true },
|
|
23222
|
+
inputSchema: slackManifestGenerateSchema
|
|
23223
|
+
},
|
|
23224
|
+
async (args) => {
|
|
23225
|
+
requireAdmin3(ctx, "generate Slack app manifests");
|
|
23226
|
+
const options = toSlackWizardOptions(args);
|
|
23227
|
+
return textResult({
|
|
23228
|
+
manifest: buildSlackManifest(options),
|
|
23229
|
+
bot_scopes: requiredBotScopes(options),
|
|
23230
|
+
bot_events: requiredBotEvents(options),
|
|
23231
|
+
setup_steps: [
|
|
23232
|
+
'Open https://api.slack.com/apps?new_app=1 and choose "Create New App".',
|
|
23233
|
+
'Select "From a manifest", pick the target workspace, paste the manifest JSON below, and click "Create".',
|
|
23234
|
+
'Install the app to the workspace, then open OAuth & Permissions and copy the Bot User OAuth Token (starts with "xoxb-").',
|
|
23235
|
+
'Open Basic Information \u2192 App-Level Tokens \u2192 Generate Token and Scopes, add the connections:write scope, generate it, and copy the App-Level Token (starts with "xapp-").',
|
|
23236
|
+
'Once the app is installed and you hold the xoxb-/xapp- tokens, call agor_gateway_channels_create with channelType "slack", enabled:false, no tokens, and create_channel_config_hint below to create the channel as a draft.',
|
|
23237
|
+
"Then call agor_widgets_request_gateway_token for that channel so the user enters the xoxb-/xapp- tokens in the secure form that appears at the end of this message, which enables the channel. Do NOT ask the user to paste xoxb-/xapp- tokens into the chat, and do NOT pass tokens as agor_gateway_channels_create arguments.",
|
|
23238
|
+
identityModeSetupStep(args.alignUsers)
|
|
23239
|
+
],
|
|
23240
|
+
create_channel_config_hint: toCreateChannelConfigHint(args),
|
|
23241
|
+
caveats: [
|
|
23242
|
+
"GENERATED ONLY \u2014 no Slack app created, no Agor channel created, no tokens validated, no event delivery verified.",
|
|
23243
|
+
"The app-level connections:write token is generated manually and is NOT part of the manifest bot scopes.",
|
|
23244
|
+
"restrictToChannelIds maps to config.allowed_channel_ids when you call create \u2014 it does NOT change the manifest scopes or events."
|
|
23245
|
+
]
|
|
23246
|
+
});
|
|
23247
|
+
}
|
|
23248
|
+
);
|
|
23137
23249
|
server.registerTool(
|
|
23138
23250
|
"agor_gateway_channels_update",
|
|
23139
23251
|
{
|
|
@@ -23157,7 +23269,7 @@ function registerGatewayChannelTools(server, ctx) {
|
|
|
23157
23269
|
server.registerTool(
|
|
23158
23270
|
"agor_gateway_outbound_targets_list",
|
|
23159
23271
|
{
|
|
23160
|
-
description: "List Slack gateway outbound targets the caller can use. Returns only outbound-enabled channels where the caller has branch all permission or admin access. Secrets and inbound channel keys are never returned.",
|
|
23272
|
+
description: "List Slack gateway outbound targets the caller can use. Returns only outbound-enabled channels where the caller has branch all permission or admin access; when called from a session, results are additionally scoped to channels targeting the session's branch. Secrets and inbound channel keys are never returned.",
|
|
23161
23273
|
annotations: { readOnlyHint: true },
|
|
23162
23274
|
inputSchema: external_exports.strictObject({
|
|
23163
23275
|
branchId: mcpOptionalId("branchId", "Branch", "Filter by target branch ID."),
|
|
@@ -23172,15 +23284,24 @@ function registerGatewayChannelTools(server, ctx) {
|
|
|
23172
23284
|
async (args) => {
|
|
23173
23285
|
const channelRepo = new GatewayChannelRepository2(ctx.db);
|
|
23174
23286
|
const branchRepo = new BranchRepository5(ctx.db);
|
|
23287
|
+
const callerSessionBranchId = await resolveCallerSessionBranchId(ctx);
|
|
23175
23288
|
const branchFilter = args.branchId ? await branchRepo.findById(args.branchId) : null;
|
|
23176
|
-
const
|
|
23289
|
+
const requestedBranchId = branchFilter?.branch_id;
|
|
23290
|
+
if (callerSessionBranchId && args.branchId && requestedBranchId !== callerSessionBranchId) {
|
|
23291
|
+
return textResult({
|
|
23292
|
+
channels: [],
|
|
23293
|
+
binding: "Results are scoped to the calling session's branch; the requested branchId targets a different branch, so this session cannot use its channels."
|
|
23294
|
+
});
|
|
23295
|
+
}
|
|
23296
|
+
const branchFilterId = callerSessionBranchId ?? requestedBranchId;
|
|
23177
23297
|
const allChannels = args.gatewayChannelId ? [await channelRepo.findById(args.gatewayChannelId)] : await channelRepo.findAll();
|
|
23178
23298
|
const channels = [];
|
|
23179
23299
|
for (const channel of allChannels) {
|
|
23180
23300
|
if (!channel) continue;
|
|
23181
23301
|
if (args.channelType && channel.channel_type !== args.channelType) continue;
|
|
23182
23302
|
if (channel.channel_type !== "slack") continue;
|
|
23183
|
-
if (args.branchId && channel.target_branch_id !== branchFilterId)
|
|
23303
|
+
if ((callerSessionBranchId || args.branchId) && channel.target_branch_id !== branchFilterId)
|
|
23304
|
+
continue;
|
|
23184
23305
|
if (!channel.enabled) continue;
|
|
23185
23306
|
const outbound = getOutboundConfig(channel);
|
|
23186
23307
|
if (!outbound.outbound_enabled) continue;
|
|
@@ -23203,13 +23324,18 @@ function registerGatewayChannelTools(server, ctx) {
|
|
|
23203
23324
|
]
|
|
23204
23325
|
});
|
|
23205
23326
|
}
|
|
23206
|
-
return textResult({
|
|
23327
|
+
return textResult({
|
|
23328
|
+
channels,
|
|
23329
|
+
...callerSessionBranchId && channels.length === 0 ? {
|
|
23330
|
+
hint: "No outbound-enabled channel targets this session's branch \u2014 ask an operator to create/enable one."
|
|
23331
|
+
} : {}
|
|
23332
|
+
});
|
|
23207
23333
|
}
|
|
23208
23334
|
);
|
|
23209
23335
|
server.registerTool(
|
|
23210
23336
|
"agor_gateway_slack_thread_history_get",
|
|
23211
23337
|
{
|
|
23212
|
-
description: "Fetch Slack thread history for a gateway-mapped Slack thread without exposing Slack tokens. Prefer sessionId to resolve the gateway thread mapping from an accessible Agor session. Alternatively pass gatewayChannelId + threadId: mapped threads require admin or branch all permission on the mapped branch; unmapped arbitrary thread reads are admin-only. Slack message text is untrusted external content.",
|
|
23338
|
+
description: "Fetch Slack thread history for a gateway-mapped Slack thread without exposing Slack tokens. Prefer sessionId to resolve the gateway thread mapping from an accessible Agor session. Alternatively pass gatewayChannelId + threadId: mapped threads require admin or branch all permission on the mapped branch; unmapped arbitrary thread reads are admin-only. When called from a session, reads are restricted to threads whose target branch matches the calling session's branch. Slack message text is untrusted external content.",
|
|
23213
23339
|
annotations: { readOnlyHint: true },
|
|
23214
23340
|
inputSchema: slackThreadHistorySchema
|
|
23215
23341
|
},
|
|
@@ -23277,7 +23403,7 @@ function registerGatewayChannelTools(server, ctx) {
|
|
|
23277
23403
|
server.registerTool(
|
|
23278
23404
|
"agor_gateway_emit_message",
|
|
23279
23405
|
{
|
|
23280
|
-
description: "Send a proactive Slack message through an outbound-enabled gateway channel and persist a seed/audit record. Targets may be Slack channel IDs, channel names, or user emails; v0 intentionally starts a fresh Slack thread/DM message for each emit and does not create a thread-session mapping until a human replies.",
|
|
23406
|
+
description: "Send a proactive Slack message through an outbound-enabled gateway channel and persist a seed/audit record. Targets may be Slack channel IDs, channel names, or user emails; v0 intentionally starts a fresh Slack thread/DM message for each emit and does not create a thread-session mapping until a human replies. When called from a session, outbound is restricted to channels whose target branch matches the calling session's branch.",
|
|
23281
23407
|
annotations: { destructiveHint: false, idempotentHint: false },
|
|
23282
23408
|
inputSchema: external_exports.strictObject({
|
|
23283
23409
|
gatewayChannelId: mcpRequiredId(
|
|
@@ -23314,7 +23440,7 @@ function registerGatewayChannelTools(server, ctx) {
|
|
|
23314
23440
|
}
|
|
23315
23441
|
);
|
|
23316
23442
|
}
|
|
23317
|
-
var configSchema, outboundTargetSchema, envVarSchema, agenticConfigSchema, gatewayChannelCreateSchema, slackThreadHistorySchema, gatewayChannelUpdateSchema;
|
|
23443
|
+
var configSchema, outboundTargetSchema, envVarSchema, agenticConfigSchema, gatewayChannelCreateSchema, slackThreadHistorySchema, gatewayChannelUpdateSchema, slackManifestGenerateSchema;
|
|
23318
23444
|
var init_gateway_channels = __esm({
|
|
23319
23445
|
"src/mcp/tools/gateway-channels.ts"() {
|
|
23320
23446
|
"use strict";
|
|
@@ -23378,44 +23504,48 @@ var init_gateway_channels = __esm({
|
|
|
23378
23504
|
agenticConfig: agenticConfigSchema.optional()
|
|
23379
23505
|
}).superRefine((value, issue2) => {
|
|
23380
23506
|
const config2 = value.config ?? {};
|
|
23381
|
-
if (value.
|
|
23382
|
-
|
|
23383
|
-
|
|
23384
|
-
|
|
23385
|
-
|
|
23386
|
-
|
|
23387
|
-
|
|
23388
|
-
|
|
23389
|
-
if (config2.connection_mode === "socket" && !config2.app_token) {
|
|
23390
|
-
issue2.addIssue({
|
|
23391
|
-
code: "custom",
|
|
23392
|
-
path: ["config", "app_token"],
|
|
23393
|
-
message: "config.app_token is required for Slack Socket Mode."
|
|
23394
|
-
});
|
|
23395
|
-
}
|
|
23396
|
-
}
|
|
23397
|
-
if (value.channelType === "github") {
|
|
23398
|
-
for (const field of ["app_id", "private_key", "installation_id", "watch_repos"]) {
|
|
23507
|
+
if (value.enabled !== false) {
|
|
23508
|
+
const requiredSecretMessages = {
|
|
23509
|
+
bot_token: "config.bot_token is required for Slack. Prefer a bot token stored outside the transcript when possible.",
|
|
23510
|
+
app_token: "config.app_token is required for Slack Socket Mode.",
|
|
23511
|
+
private_key: "config.private_key is required for GitHub gateway channels.",
|
|
23512
|
+
app_password: "config.app_password is required for Teams gateway channels."
|
|
23513
|
+
};
|
|
23514
|
+
for (const field of getRequiredSecretFields(value.channelType, config2)) {
|
|
23399
23515
|
if (!config2[field]) {
|
|
23400
23516
|
issue2.addIssue({
|
|
23401
23517
|
code: "custom",
|
|
23402
23518
|
path: ["config", field],
|
|
23403
|
-
message: `config.${field} is required for
|
|
23519
|
+
message: requiredSecretMessages[field] ?? `config.${field} is required for ${value.channelType} gateway channels.`
|
|
23404
23520
|
});
|
|
23405
23521
|
}
|
|
23406
23522
|
}
|
|
23407
23523
|
}
|
|
23408
|
-
if (value.channelType === "
|
|
23409
|
-
for (const field of ["app_id", "
|
|
23524
|
+
if (value.channelType === "github") {
|
|
23525
|
+
for (const field of ["app_id", "installation_id", "watch_repos"]) {
|
|
23410
23526
|
if (!config2[field]) {
|
|
23411
23527
|
issue2.addIssue({
|
|
23412
23528
|
code: "custom",
|
|
23413
23529
|
path: ["config", field],
|
|
23414
|
-
message: `config.${field} is required for
|
|
23530
|
+
message: `config.${field} is required for GitHub gateway channels.`
|
|
23415
23531
|
});
|
|
23416
23532
|
}
|
|
23417
23533
|
}
|
|
23418
23534
|
}
|
|
23535
|
+
if (value.channelType === "teams" && !config2.app_id) {
|
|
23536
|
+
issue2.addIssue({
|
|
23537
|
+
code: "custom",
|
|
23538
|
+
path: ["config", "app_id"],
|
|
23539
|
+
message: "config.app_id is required for Teams gateway channels."
|
|
23540
|
+
});
|
|
23541
|
+
}
|
|
23542
|
+
if (value.channelType === "slack" && config2.align_slack_users !== true && !value.agorUserId) {
|
|
23543
|
+
issue2.addIssue({
|
|
23544
|
+
code: "custom",
|
|
23545
|
+
path: ["agorUserId"],
|
|
23546
|
+
message: "Run-as-selected-user needs agorUserId \u2014 set config.align_slack_users:true to align by email, or pass agorUserId."
|
|
23547
|
+
});
|
|
23548
|
+
}
|
|
23419
23549
|
});
|
|
23420
23550
|
slackThreadHistorySchema = external_exports.strictObject({
|
|
23421
23551
|
sessionId: mcpOptionalId(
|
|
@@ -23479,6 +23609,23 @@ var init_gateway_channels = __esm({
|
|
|
23479
23609
|
),
|
|
23480
23610
|
agenticConfig: agenticConfigSchema.nullable().optional().describe("Replace agent/session defaults. null clears the gateway agentic config.")
|
|
23481
23611
|
});
|
|
23612
|
+
slackManifestGenerateSchema = external_exports.strictObject({
|
|
23613
|
+
appName: mcpRequiredString("appName", 'Slack app display name, e.g. "Agor".'),
|
|
23614
|
+
botDisplayName: mcpOptionalNonEmptyString(
|
|
23615
|
+
"botDisplayName",
|
|
23616
|
+
"Bot user display name. Defaults to appName."
|
|
23617
|
+
),
|
|
23618
|
+
publicChannels: external_exports.boolean().default(false).describe("Listen in public channels (#channel) via @mention."),
|
|
23619
|
+
privateChannels: external_exports.boolean().default(false).describe("Listen in private channels (groups) via @mention."),
|
|
23620
|
+
groupDms: external_exports.boolean().default(false).describe("Listen in group DMs (multi-person IMs) via @mention."),
|
|
23621
|
+
alignUsers: external_exports.boolean().default(true).describe(
|
|
23622
|
+
"Resolve Slack user email \u2192 Agor user (adds the users:read.email scope). Defaults to true: each Slack user runs as their matched Agor account and unmatched users are rejected, so no fixed run-as user is needed. Set false to run every message as one fixed Agor user (then pass agorUserId to agor_gateway_channels_create)."
|
|
23623
|
+
),
|
|
23624
|
+
outbound: external_exports.boolean().default(false).describe("Proactive outbound: post to channels by name and DM users by email."),
|
|
23625
|
+
restrictToChannelIds: external_exports.array(external_exports.string().min(1)).optional().describe(
|
|
23626
|
+
"Slack channel ID whitelist. Maps to config.allowed_channel_ids when you call agor_gateway_channels_create; it does NOT change the manifest scopes or events."
|
|
23627
|
+
)
|
|
23628
|
+
});
|
|
23482
23629
|
}
|
|
23483
23630
|
});
|
|
23484
23631
|
|
|
@@ -28907,8 +29054,191 @@ var init_env_vars = __esm({
|
|
|
28907
29054
|
}
|
|
28908
29055
|
});
|
|
28909
29056
|
|
|
29057
|
+
// src/widgets/gateway-token/index.ts
|
|
29058
|
+
import { Forbidden as Forbidden13 } from "@agor/core/feathers";
|
|
29059
|
+
import {
|
|
29060
|
+
GATEWAY_SENSITIVE_CONFIG_FIELDS as GATEWAY_SENSITIVE_CONFIG_FIELDS3,
|
|
29061
|
+
hasMinimumRole as hasMinimumRole12,
|
|
29062
|
+
ROLES as ROLES15
|
|
29063
|
+
} from "@agor/core/types";
|
|
29064
|
+
function isSupportedGatewayTokenChannelType(channelType) {
|
|
29065
|
+
return SUPPORTED_CHANNEL_TYPES.includes(channelType);
|
|
29066
|
+
}
|
|
29067
|
+
function orderedFields(fields) {
|
|
29068
|
+
return [...fields].sort();
|
|
29069
|
+
}
|
|
29070
|
+
function classifyGatewayTokenTest(result, appTokenExpected) {
|
|
29071
|
+
const unprobable = result.failures.some((failure) => failure.capability === "connector");
|
|
29072
|
+
if (unprobable) {
|
|
29073
|
+
return {
|
|
29074
|
+
enable: false,
|
|
29075
|
+
status: "unverifiable",
|
|
29076
|
+
summary: "credentials cannot be auto-verified for this channel type yet"
|
|
29077
|
+
};
|
|
29078
|
+
}
|
|
29079
|
+
const isAppTokenFailure = (failure) => failure.capability === "app_token";
|
|
29080
|
+
const hardFailure = result.failures.find(
|
|
29081
|
+
(failure) => failure.slackError && HARD_CREDENTIAL_SLACK_ERRORS.has(failure.slackError) || failure.capability === "config" || appTokenExpected && isAppTokenFailure(failure)
|
|
29082
|
+
);
|
|
29083
|
+
if (hardFailure) {
|
|
29084
|
+
return { enable: false, status: "failed", summary: hardFailure.reason };
|
|
29085
|
+
}
|
|
29086
|
+
if (appTokenExpected && result.appTokenValid === false) {
|
|
29087
|
+
return {
|
|
29088
|
+
enable: false,
|
|
29089
|
+
status: "failed",
|
|
29090
|
+
summary: "The app-level token is missing or invalid; Socket Mode cannot connect."
|
|
29091
|
+
};
|
|
29092
|
+
}
|
|
29093
|
+
if (result.ok) {
|
|
29094
|
+
return { enable: true, status: "verified", summary: "passed" };
|
|
29095
|
+
}
|
|
29096
|
+
const followUps = result.failures.filter((failure) => appTokenExpected || !isAppTokenFailure(failure)).map((failure) => failure.reason).filter(Boolean);
|
|
29097
|
+
return {
|
|
29098
|
+
enable: true,
|
|
29099
|
+
status: "verified",
|
|
29100
|
+
summary: followUps.length > 0 ? `passed, with follow-ups: ${followUps.join("; ")}` : "passed"
|
|
29101
|
+
};
|
|
29102
|
+
}
|
|
29103
|
+
function assertGatewayTokenAdmin(ctx) {
|
|
29104
|
+
if (!hasMinimumRole12(ctx.submitterRole, ROLES15.ADMIN)) {
|
|
29105
|
+
throw new Forbidden13("Only admins can set gateway channel tokens");
|
|
29106
|
+
}
|
|
29107
|
+
}
|
|
29108
|
+
async function applyGatewayTokenSubmit(ctx, submit, params) {
|
|
29109
|
+
assertGatewayTokenAdmin(ctx);
|
|
29110
|
+
const channelsService = ctx.app.service("gateway-channels");
|
|
29111
|
+
const channel = await channelsService.get(params.gatewayChannelId);
|
|
29112
|
+
if (!channel) {
|
|
29113
|
+
throw new Forbidden13(`Gateway channel ${params.gatewayChannelId} not found`);
|
|
29114
|
+
}
|
|
29115
|
+
if (!isSupportedGatewayTokenChannelType(channel.channel_type)) {
|
|
29116
|
+
throw new Forbidden13(
|
|
29117
|
+
`Gateway channel type "${channel.channel_type}" does not support token entry`
|
|
29118
|
+
);
|
|
29119
|
+
}
|
|
29120
|
+
if (channel.channel_type !== params.channelType) {
|
|
29121
|
+
throw new Forbidden13("Gateway channel type does not match the widget request");
|
|
29122
|
+
}
|
|
29123
|
+
const sessionsService = ctx.app.service("sessions");
|
|
29124
|
+
const session = await sessionsService.get(ctx.sessionId);
|
|
29125
|
+
if (!session.branch_id || channel.target_branch_id !== session.branch_id) {
|
|
29126
|
+
throw new Forbidden13("Gateway channel is not bound to this session's branch");
|
|
29127
|
+
}
|
|
29128
|
+
const requested = new Set(params.fields);
|
|
29129
|
+
const extra = Object.keys(submit.tokens).filter((field) => !requested.has(field));
|
|
29130
|
+
if (extra.length > 0) {
|
|
29131
|
+
throw new Forbidden13(
|
|
29132
|
+
`Submitted token fields were not requested: ${orderedFields(extra).join(", ")}`
|
|
29133
|
+
);
|
|
29134
|
+
}
|
|
29135
|
+
const testService = ctx.app.service(
|
|
29136
|
+
"gateway-channels/test"
|
|
29137
|
+
);
|
|
29138
|
+
const testResult = await testService.create({
|
|
29139
|
+
gatewayChannelId: params.gatewayChannelId,
|
|
29140
|
+
config: submit.tokens
|
|
29141
|
+
});
|
|
29142
|
+
const appTokenExpected = params.fields.includes("app_token");
|
|
29143
|
+
const { enable, status, summary } = classifyGatewayTokenTest(testResult, appTokenExpected);
|
|
29144
|
+
await channelsService.patch(
|
|
29145
|
+
params.gatewayChannelId,
|
|
29146
|
+
{ config: submit.tokens, enabled: enable },
|
|
29147
|
+
{ user: { user_id: ctx.submitterUserId, role: ctx.submitterRole } }
|
|
29148
|
+
);
|
|
29149
|
+
submitOutcomes.set(submit, {
|
|
29150
|
+
channelId: channel.id,
|
|
29151
|
+
channelName: channel.name,
|
|
29152
|
+
channelType: channel.channel_type,
|
|
29153
|
+
enabled: enable,
|
|
29154
|
+
unverified: status === "unverifiable",
|
|
29155
|
+
test: { ok: testResult.ok, summary }
|
|
29156
|
+
});
|
|
29157
|
+
}
|
|
29158
|
+
function registerGatewayTokenWidget() {
|
|
29159
|
+
registerWidget(gatewayTokenWidget);
|
|
29160
|
+
}
|
|
29161
|
+
var SUPPORTED_CHANNEL_TYPES, MAX_TOKEN_LENGTH, gatewayTokenParamsSchema, gatewayTokenSubmitSchema, submitOutcomes, HARD_CREDENTIAL_SLACK_ERRORS, gatewayTokenWidget;
|
|
29162
|
+
var init_gateway_token = __esm({
|
|
29163
|
+
"src/widgets/gateway-token/index.ts"() {
|
|
29164
|
+
"use strict";
|
|
29165
|
+
init_zod();
|
|
29166
|
+
init_registry();
|
|
29167
|
+
SUPPORTED_CHANNEL_TYPES = [
|
|
29168
|
+
"slack",
|
|
29169
|
+
"github",
|
|
29170
|
+
"teams"
|
|
29171
|
+
];
|
|
29172
|
+
MAX_TOKEN_LENGTH = 8192;
|
|
29173
|
+
gatewayTokenParamsSchema = external_exports.object({
|
|
29174
|
+
gatewayChannelId: external_exports.string().min(1).describe("Gateway channel whose credentials are being set."),
|
|
29175
|
+
channelType: external_exports.enum(["slack", "discord", "whatsapp", "telegram", "github", "teams"]).describe("Platform type of the channel; drives per-field placeholders."),
|
|
29176
|
+
channelName: external_exports.string().min(1).max(200).describe("Human-readable channel name for the form heading and prompts."),
|
|
29177
|
+
fields: external_exports.array(external_exports.enum(GATEWAY_SENSITIVE_CONFIG_FIELDS3)).min(1).max(GATEWAY_SENSITIVE_CONFIG_FIELDS3.length).refine((fields) => new Set(fields).size === fields.length, {
|
|
29178
|
+
message: "Token field names must be unique"
|
|
29179
|
+
}).describe("Secret config field names to collect (subset of the sensitive fields)."),
|
|
29180
|
+
reason: external_exports.string().min(1).max(200).describe("One sentence explaining why the tokens are needed. Renders as a muted line.")
|
|
29181
|
+
}).strict();
|
|
29182
|
+
gatewayTokenSubmitSchema = external_exports.object({
|
|
29183
|
+
tokens: external_exports.partialRecord(
|
|
29184
|
+
external_exports.enum(GATEWAY_SENSITIVE_CONFIG_FIELDS3),
|
|
29185
|
+
external_exports.string().min(1).max(MAX_TOKEN_LENGTH)
|
|
29186
|
+
).describe("Map of sensitive field name \u2192 credential value.")
|
|
29187
|
+
}).strict();
|
|
29188
|
+
submitOutcomes = /* @__PURE__ */ new WeakMap();
|
|
29189
|
+
HARD_CREDENTIAL_SLACK_ERRORS = /* @__PURE__ */ new Set([
|
|
29190
|
+
"invalid_auth",
|
|
29191
|
+
"account_inactive",
|
|
29192
|
+
"token_revoked",
|
|
29193
|
+
"token_expired",
|
|
29194
|
+
"not_authed",
|
|
29195
|
+
"no_permission"
|
|
29196
|
+
]);
|
|
29197
|
+
gatewayTokenWidget = {
|
|
29198
|
+
type: "gateway_token",
|
|
29199
|
+
schemaVersion: 1,
|
|
29200
|
+
paramsSchema: gatewayTokenParamsSchema,
|
|
29201
|
+
submitSchema: gatewayTokenSubmitSchema,
|
|
29202
|
+
buildResultMeta: (submit) => {
|
|
29203
|
+
const outcome = submitOutcomes.get(submit);
|
|
29204
|
+
return {
|
|
29205
|
+
channelId: outcome?.channelId ?? "",
|
|
29206
|
+
channelName: outcome?.channelName ?? "",
|
|
29207
|
+
channelType: outcome?.channelType ?? "slack",
|
|
29208
|
+
fieldsSet: orderedFields(Object.keys(submit.tokens)),
|
|
29209
|
+
enabled: outcome?.enabled ?? false,
|
|
29210
|
+
unverified: outcome?.unverified ?? false,
|
|
29211
|
+
test: outcome?.test ?? { ok: false, summary: "" }
|
|
29212
|
+
};
|
|
29213
|
+
},
|
|
29214
|
+
applySubmit: applyGatewayTokenSubmit,
|
|
29215
|
+
buildAutoResumePrompt: (rm3, params) => {
|
|
29216
|
+
const fields = orderedFields(rm3.fieldsSet).join(", ");
|
|
29217
|
+
const name = rm3.channelName || params.channelName;
|
|
29218
|
+
const channelType = rm3.channelType || params.channelType;
|
|
29219
|
+
if (rm3.enabled) {
|
|
29220
|
+
return `[Agor] User set the ${channelType} tokens (${fields}) for channel "${name}". Channel enabled. Connection test: ${rm3.test.summary || "passed"}.`;
|
|
29221
|
+
}
|
|
29222
|
+
if (rm3.unverified) {
|
|
29223
|
+
return `[Agor] Tokens saved for "${name}" (${fields}); ${channelType} credentials can't be auto-verified yet, so the channel is left disabled \u2014 enable it manually once confirmed.`;
|
|
29224
|
+
}
|
|
29225
|
+
return `[Agor] User set the ${channelType} tokens (${fields}) for channel "${name}", but it was left disabled; test failed: ${rm3.test.summary || "unknown error"}. Ask the user to double-check the tokens/scopes.`;
|
|
29226
|
+
},
|
|
29227
|
+
buildDismissedPrompt: (params) => `[Agor] User declined to provide tokens for "${params.channelName}"; it stays disabled. Don't immediately re-ask.`,
|
|
29228
|
+
// Dismissal is admin-only too — otherwise a member could terminally decline
|
|
29229
|
+
// an admin-only credential flow via the generic dismiss endpoint.
|
|
29230
|
+
authorizeDismiss: (ctx) => assertGatewayTokenAdmin(ctx)
|
|
29231
|
+
};
|
|
29232
|
+
}
|
|
29233
|
+
});
|
|
29234
|
+
|
|
28910
29235
|
// src/mcp/tools/widgets.ts
|
|
28911
|
-
import { MessageRole as MessageRole3 } from "@agor/core/types";
|
|
29236
|
+
import { getRequiredSecretFields as getRequiredSecretFields2, hasMinimumRole as hasMinimumRole13, MessageRole as MessageRole3, ROLES as ROLES16 } from "@agor/core/types";
|
|
29237
|
+
function requireAdmin4(ctx, action) {
|
|
29238
|
+
if (!hasMinimumRole13(ctx.authenticatedUser?.role, ROLES16.ADMIN)) {
|
|
29239
|
+
throw new Error(`Access denied: admin role required to ${action}`);
|
|
29240
|
+
}
|
|
29241
|
+
}
|
|
28912
29242
|
function widgetContentPreview(params) {
|
|
28913
29243
|
const list = params.names.join(", ");
|
|
28914
29244
|
return params.names.length === 1 ? `Please provide ${list}: ${params.reason}` : `Please provide ${list}: ${params.reason}`;
|
|
@@ -29027,13 +29357,113 @@ function registerWidgetTools(server, ctx) {
|
|
|
29027
29357
|
return textResult({ widget_id: widgetId, status: "requested" });
|
|
29028
29358
|
}
|
|
29029
29359
|
);
|
|
29360
|
+
server.registerTool(
|
|
29361
|
+
"agor_widgets_request_gateway_token",
|
|
29362
|
+
{
|
|
29363
|
+
description: "Ask an admin to securely provide a gateway channel's platform tokens (Slack bot/app tokens, GitHub private key, Teams app password) via a compact form that appears at the end of your message, so setup finishes without anyone pasting xoxb-/xapp- tokens into chat. FIRE-AND-FORGET: the widget renders inline at the end of your turn; end your turn after calling. You will receive a user-role message when it is resolved. Token values never enter your context \u2014 only the channel identity and field NAMES do. Admin-only: a non-admin agent cannot mint this widget. Keep `reason` to ONE short sentence (\u2264200 chars).",
|
|
29364
|
+
annotations: { destructiveHint: false, openWorldHint: false },
|
|
29365
|
+
inputSchema: external_exports.object({
|
|
29366
|
+
gatewayChannelId: external_exports.string().min(1).describe("Gateway channel ID (UUIDv7 or short ID) whose tokens are being set."),
|
|
29367
|
+
reason: external_exports.string().max(200).optional().describe("One short sentence explaining why the tokens are needed.")
|
|
29368
|
+
})
|
|
29369
|
+
},
|
|
29370
|
+
async (args) => {
|
|
29371
|
+
if (!ctx.sessionId) return sessionContextRequiredResult();
|
|
29372
|
+
const currentSessionId = ctx.sessionId;
|
|
29373
|
+
requireAdmin4(ctx, "set gateway channel tokens");
|
|
29374
|
+
const channel = await ctx.app.service("gateway-channels").get(args.gatewayChannelId, ctx.baseServiceParams);
|
|
29375
|
+
const channelType = channel.channel_type;
|
|
29376
|
+
if (!isSupportedGatewayTokenChannelType(channelType)) {
|
|
29377
|
+
throw new Error(
|
|
29378
|
+
`Gateway channel type "${channelType}" does not support token entry via this widget.`
|
|
29379
|
+
);
|
|
29380
|
+
}
|
|
29381
|
+
const fields = getRequiredSecretFields2(channelType, channel.config);
|
|
29382
|
+
if (fields.length === 0) {
|
|
29383
|
+
throw new Error(
|
|
29384
|
+
`Gateway channel "${channel.name}" has no required secret fields to collect.`
|
|
29385
|
+
);
|
|
29386
|
+
}
|
|
29387
|
+
const params = gatewayTokenParamsSchema.parse({
|
|
29388
|
+
gatewayChannelId: channel.id,
|
|
29389
|
+
channelType,
|
|
29390
|
+
channelName: channel.name,
|
|
29391
|
+
fields,
|
|
29392
|
+
reason: args.reason ?? `Provide the ${channelType} credentials to finish connecting "${channel.name}".`
|
|
29393
|
+
});
|
|
29394
|
+
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29395
|
+
const baseWidgetMeta = {
|
|
29396
|
+
widget_type: "gateway_token",
|
|
29397
|
+
schema_version: 1,
|
|
29398
|
+
params,
|
|
29399
|
+
status: "pending",
|
|
29400
|
+
requested_at: requestedAt,
|
|
29401
|
+
auto_resume: true
|
|
29402
|
+
};
|
|
29403
|
+
const hostTask = await findHostTaskForSession(
|
|
29404
|
+
ctx.app,
|
|
29405
|
+
currentSessionId,
|
|
29406
|
+
ctx.baseServiceParams
|
|
29407
|
+
);
|
|
29408
|
+
const hostTaskId = hostTask?.task_id;
|
|
29409
|
+
const created = await appendSystemMessage({
|
|
29410
|
+
app: ctx.app,
|
|
29411
|
+
db: ctx.db,
|
|
29412
|
+
sessionId: currentSessionId,
|
|
29413
|
+
taskId: hostTaskId,
|
|
29414
|
+
content: `Please provide the ${channelType} tokens for "${channel.name}": ${params.reason}`,
|
|
29415
|
+
contentPreview: `Widget: gateway_token (${channel.name})`,
|
|
29416
|
+
type: "widget_request",
|
|
29417
|
+
role: MessageRole3.SYSTEM,
|
|
29418
|
+
metadata: {
|
|
29419
|
+
widget: {
|
|
29420
|
+
...baseWidgetMeta,
|
|
29421
|
+
widget_id: "pending"
|
|
29422
|
+
}
|
|
29423
|
+
}
|
|
29424
|
+
});
|
|
29425
|
+
const widgetId = created.message_id;
|
|
29426
|
+
if (hostTask?.message_range) {
|
|
29427
|
+
try {
|
|
29428
|
+
await ctx.app.service("tasks").patch(
|
|
29429
|
+
hostTask.task_id,
|
|
29430
|
+
{
|
|
29431
|
+
message_range: {
|
|
29432
|
+
start_index: hostTask.message_range.start_index,
|
|
29433
|
+
end_index: created.index
|
|
29434
|
+
}
|
|
29435
|
+
},
|
|
29436
|
+
ctx.baseServiceParams
|
|
29437
|
+
);
|
|
29438
|
+
} catch (err) {
|
|
29439
|
+
console.warn(
|
|
29440
|
+
`[widgets] failed to extend task.message_range for widget ${widgetId}:`,
|
|
29441
|
+
err
|
|
29442
|
+
);
|
|
29443
|
+
}
|
|
29444
|
+
}
|
|
29445
|
+
await ctx.app.service("messages").patch(
|
|
29446
|
+
widgetId,
|
|
29447
|
+
{
|
|
29448
|
+
metadata: {
|
|
29449
|
+
...created.metadata ?? {},
|
|
29450
|
+
widget: { ...baseWidgetMeta, widget_id: widgetId }
|
|
29451
|
+
}
|
|
29452
|
+
},
|
|
29453
|
+
ctx.baseServiceParams
|
|
29454
|
+
);
|
|
29455
|
+
return textResult({ widget_id: widgetId, status: "requested" });
|
|
29456
|
+
}
|
|
29457
|
+
);
|
|
29030
29458
|
}
|
|
29031
29459
|
var init_widgets = __esm({
|
|
29032
29460
|
"src/mcp/tools/widgets.ts"() {
|
|
29033
29461
|
"use strict";
|
|
29462
|
+
init_zod();
|
|
29034
29463
|
init_append_system_message();
|
|
29035
29464
|
init_session_tasks();
|
|
29036
29465
|
init_env_vars();
|
|
29466
|
+
init_gateway_token();
|
|
29037
29467
|
init_server();
|
|
29038
29468
|
}
|
|
29039
29469
|
});
|
|
@@ -31323,13 +31753,13 @@ var init_identity_token = __esm({
|
|
|
31323
31753
|
function oidcFederationProvider(config2) {
|
|
31324
31754
|
return async () => {
|
|
31325
31755
|
requireSecureTokenEndpoint(config2.baseURL);
|
|
31326
|
-
const
|
|
31327
|
-
if (
|
|
31328
|
-
throw new WorkloadIdentityError(`Identity token is ${Math.ceil(
|
|
31756
|
+
const jwt13 = await config2.identityTokenProvider();
|
|
31757
|
+
if (jwt13.length > 16 * 1024) {
|
|
31758
|
+
throw new WorkloadIdentityError(`Identity token is ${Math.ceil(jwt13.length / 1024)} KiB, exceeds the 16 KiB assertion limit`);
|
|
31329
31759
|
}
|
|
31330
31760
|
const body = {
|
|
31331
31761
|
grant_type: GRANT_TYPE_JWT_BEARER,
|
|
31332
|
-
assertion:
|
|
31762
|
+
assertion: jwt13,
|
|
31333
31763
|
federation_rule_id: config2.federationRuleId,
|
|
31334
31764
|
organization_id: config2.organizationId
|
|
31335
31765
|
};
|
|
@@ -41711,7 +42141,7 @@ __export(session_token_service_exports, {
|
|
|
41711
42141
|
SessionTokenService: () => SessionTokenService
|
|
41712
42142
|
});
|
|
41713
42143
|
import { getCurrentTenantId as getCurrentTenantId6 } from "@agor/core/db";
|
|
41714
|
-
import
|
|
42144
|
+
import jwt12 from "jsonwebtoken";
|
|
41715
42145
|
function sessionTokenDebug(...args) {
|
|
41716
42146
|
if (DEBUG_SESSION_TOKENS) {
|
|
41717
42147
|
console.debug(...args);
|
|
@@ -41721,6 +42151,7 @@ var DEBUG_SESSION_TOKENS, SessionTokenService;
|
|
|
41721
42151
|
var init_session_token_service = __esm({
|
|
41722
42152
|
"src/services/session-token-service.ts"() {
|
|
41723
42153
|
"use strict";
|
|
42154
|
+
init_executor_session_token();
|
|
41724
42155
|
DEBUG_SESSION_TOKENS = process.env.AGOR_DEBUG_SESSION_TOKENS === "1" || process.env.DEBUG?.includes("session-token");
|
|
41725
42156
|
SessionTokenService = class {
|
|
41726
42157
|
constructor(config2) {
|
|
@@ -41750,10 +42181,8 @@ var init_session_token_service = __esm({
|
|
|
41750
42181
|
const payload = {
|
|
41751
42182
|
sub: userId,
|
|
41752
42183
|
// Standard JWT subject claim (used by Feathers for user lookup)
|
|
41753
|
-
type:
|
|
41754
|
-
purpose:
|
|
41755
|
-
sessionId,
|
|
41756
|
-
// Custom claim for session tracking
|
|
42184
|
+
type: EXECUTOR_SESSION_TOKEN_TYPE,
|
|
42185
|
+
purpose: EXECUTOR_SESSION_TOKEN_PURPOSE,
|
|
41757
42186
|
session_id: sessionId,
|
|
41758
42187
|
task_id: scope.taskId,
|
|
41759
42188
|
branch_id: scope.branchId,
|
|
@@ -41767,7 +42196,7 @@ var init_session_token_service = __esm({
|
|
|
41767
42196
|
iss: "agor"
|
|
41768
42197
|
// Must match Feathers jwtOptions.issuer
|
|
41769
42198
|
};
|
|
41770
|
-
const token =
|
|
42199
|
+
const token = jwt12.sign(payload, this.jwtSecret, {
|
|
41771
42200
|
algorithm: "HS256"
|
|
41772
42201
|
// Must match Feathers jwtOptions.algorithm
|
|
41773
42202
|
// No expiresIn here - we set exp directly in payload
|
|
@@ -42012,16 +42441,16 @@ import {
|
|
|
42012
42441
|
loadConfigFromFile,
|
|
42013
42442
|
renderGitConfigParametersForLog,
|
|
42014
42443
|
resolveGitConfigParameters,
|
|
42015
|
-
resolveMultiTenancyConfig as
|
|
42444
|
+
resolveMultiTenancyConfig as resolveMultiTenancyConfig8,
|
|
42016
42445
|
resolveSecurity,
|
|
42017
42446
|
resolveTenantContext as resolveTenantContext6,
|
|
42018
42447
|
saveConfig as saveConfig4,
|
|
42019
42448
|
TenantResolutionError as TenantResolutionError5
|
|
42020
42449
|
} from "@agor/core/config";
|
|
42021
|
-
import { getDatabaseUrl, runWithTenantDatabaseScope as
|
|
42450
|
+
import { getDatabaseUrl, runWithTenantDatabaseScope as runWithTenantDatabaseScope13 } from "@agor/core/db";
|
|
42022
42451
|
import {
|
|
42023
42452
|
authenticate,
|
|
42024
|
-
Forbidden as
|
|
42453
|
+
Forbidden as Forbidden27,
|
|
42025
42454
|
feathers,
|
|
42026
42455
|
feathersExpress,
|
|
42027
42456
|
NotAuthenticated as NotAuthenticated22,
|
|
@@ -42036,20 +42465,21 @@ import express from "express";
|
|
|
42036
42465
|
import expressStaticGzip from "express-static-gzip";
|
|
42037
42466
|
|
|
42038
42467
|
// src/auth/executor-runtime-scope.ts
|
|
42468
|
+
init_executor_session_token();
|
|
42039
42469
|
import { Forbidden } from "@agor/core/feathers";
|
|
42040
42470
|
function scopedPayload(context) {
|
|
42041
42471
|
const params = context.params;
|
|
42042
42472
|
const payload = params.authentication?.payload;
|
|
42043
|
-
if (payload?.type ===
|
|
42044
|
-
if (payload
|
|
42473
|
+
if (payload?.type === EXECUTOR_SESSION_TOKEN_TYPE) {
|
|
42474
|
+
if (!isExecutorSessionTokenPayload(payload)) {
|
|
42045
42475
|
throw new Forbidden("Executor token is not valid for this request");
|
|
42046
42476
|
}
|
|
42047
42477
|
return payload;
|
|
42048
42478
|
}
|
|
42049
42479
|
if (params.authentication?.strategy === "jwt" && params.task_id) {
|
|
42050
42480
|
return {
|
|
42051
|
-
type:
|
|
42052
|
-
purpose:
|
|
42481
|
+
type: EXECUTOR_SESSION_TOKEN_TYPE,
|
|
42482
|
+
purpose: EXECUTOR_SESSION_TOKEN_PURPOSE,
|
|
42053
42483
|
task_id: params.task_id,
|
|
42054
42484
|
session_id: params.session_id,
|
|
42055
42485
|
sessionId: params.sessionId,
|
|
@@ -42165,7 +42595,7 @@ function executorRuntimeScopeGuard() {
|
|
|
42165
42595
|
const payload = scopedPayload(context);
|
|
42166
42596
|
if (!payload) return context;
|
|
42167
42597
|
const scope = {
|
|
42168
|
-
sessionId: payload
|
|
42598
|
+
sessionId: getExecutorSessionTokenSessionId(payload),
|
|
42169
42599
|
taskId: payload.task_id,
|
|
42170
42600
|
branchId: payload.branch_id
|
|
42171
42601
|
};
|
|
@@ -42304,7 +42734,7 @@ import {
|
|
|
42304
42734
|
isUnixImpersonationEnabled,
|
|
42305
42735
|
loadConfig,
|
|
42306
42736
|
resolveExecutionSecurityMode,
|
|
42307
|
-
resolveMultiTenancyConfig as
|
|
42737
|
+
resolveMultiTenancyConfig as resolveMultiTenancyConfig4,
|
|
42308
42738
|
resolveMultiTenancyDatabaseDialect,
|
|
42309
42739
|
resolveTenantContext as resolveTenantContext3,
|
|
42310
42740
|
TenantResolutionError as TenantResolutionError3,
|
|
@@ -42461,6 +42891,17 @@ var gatewayRouteHook = async (context) => {
|
|
|
42461
42891
|
return context;
|
|
42462
42892
|
};
|
|
42463
42893
|
|
|
42894
|
+
// src/oauth-auth-helpers.ts
|
|
42895
|
+
import { runWithTenantDatabaseScope } from "@agor/core/db";
|
|
42896
|
+
async function runInOAuthTenantScope(db, tenantId, work) {
|
|
42897
|
+
if (!tenantId) return work();
|
|
42898
|
+
return runWithTenantDatabaseScope(db, tenantId, work);
|
|
42899
|
+
}
|
|
42900
|
+
function resolveForUserIdWithGate(opts) {
|
|
42901
|
+
const canUseExplicitUser = opts.isServiceAccount === true || opts.authPayloadType === "executor-session" && opts.queryForUserId === opts.callerUserId;
|
|
42902
|
+
return opts.queryForUserId && canUseExplicitUser ? opts.queryForUserId : opts.callerUserId;
|
|
42903
|
+
}
|
|
42904
|
+
|
|
42464
42905
|
// src/services/groups.ts
|
|
42465
42906
|
init_branch_authorization();
|
|
42466
42907
|
import { BoardRepository, GroupRepository } from "@agor/core/db";
|
|
@@ -43676,13 +44117,29 @@ function injectCreatedBy() {
|
|
|
43676
44117
|
}
|
|
43677
44118
|
|
|
43678
44119
|
// src/utils/mcp-header-secrets.ts
|
|
44120
|
+
init_executor_session_token();
|
|
43679
44121
|
import { redactMCPAuthSecrets } from "@agor/core/tools/mcp/auth-secrets";
|
|
43680
44122
|
import { redactMCPCustomHeaders } from "@agor/core/tools/mcp/http-headers";
|
|
44123
|
+
import jwt2 from "jsonwebtoken";
|
|
43681
44124
|
function shouldExposeMCPServerSecretsForSessionToken(params, options = {}) {
|
|
43682
|
-
const payload = params?.authentication?.payload;
|
|
43683
|
-
const sessionId = params?.session_id ?? payload
|
|
44125
|
+
const payload = params?.authentication?.payload ?? decodeExecutorSessionPayload(params);
|
|
44126
|
+
const sessionId = params?.session_id ?? getPayloadSessionId(payload);
|
|
43684
44127
|
return !!params?.provider && (params.authentication?.strategy === "session-token" || payload?.type === "executor-session") && !!sessionId && (!options.sessionId || sessionId === options.sessionId);
|
|
43685
44128
|
}
|
|
44129
|
+
function decodeExecutorSessionPayload(params) {
|
|
44130
|
+
if (params?.authentication?.strategy !== "jwt" || !params.authentication.accessToken) {
|
|
44131
|
+
return void 0;
|
|
44132
|
+
}
|
|
44133
|
+
const decoded = jwt2.decode(params.authentication.accessToken);
|
|
44134
|
+
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) {
|
|
44135
|
+
return void 0;
|
|
44136
|
+
}
|
|
44137
|
+
if (!isExecutorSessionTokenPayload(decoded)) return void 0;
|
|
44138
|
+
return decoded;
|
|
44139
|
+
}
|
|
44140
|
+
function getPayloadSessionId(payload) {
|
|
44141
|
+
return payload ? getExecutorSessionTokenSessionId(payload) : void 0;
|
|
44142
|
+
}
|
|
43686
44143
|
function shouldExposeMCPServerSecrets(params, options = {}) {
|
|
43687
44144
|
if (!params?.provider) return true;
|
|
43688
44145
|
if (params.user?._isServiceAccount || params.user?.role === "service") return true;
|
|
@@ -43778,6 +44235,7 @@ var RealtimeAccessCache = class {
|
|
|
43778
44235
|
}
|
|
43779
44236
|
branchVisibility = /* @__PURE__ */ new Map();
|
|
43780
44237
|
sessionBranches = /* @__PURE__ */ new Map();
|
|
44238
|
+
sessionOwners = /* @__PURE__ */ new Map();
|
|
43781
44239
|
branchVisibilityTtlMs;
|
|
43782
44240
|
sessionBranchTtlMs;
|
|
43783
44241
|
now;
|
|
@@ -43794,6 +44252,27 @@ var RealtimeAccessCache = class {
|
|
|
43794
44252
|
});
|
|
43795
44253
|
return branchId;
|
|
43796
44254
|
}
|
|
44255
|
+
/**
|
|
44256
|
+
* Owning user id for a session, cached on the same cadence as the
|
|
44257
|
+
* session→branch map. Used only to offer streaming events to the session
|
|
44258
|
+
* creator's own connections as a fallback when they haven't subscribed to
|
|
44259
|
+
* the per-session stream channel yet.
|
|
44260
|
+
*/
|
|
44261
|
+
async getSessionOwnerId(sessionId) {
|
|
44262
|
+
const cached2 = this.sessionOwners.get(sessionId);
|
|
44263
|
+
const now = this.now();
|
|
44264
|
+
if (cached2 && cached2.expiresAt > now) {
|
|
44265
|
+
return cached2.ownerId;
|
|
44266
|
+
}
|
|
44267
|
+
const ownerId = await this.options.sessionsRepository.findCreatedByBySessionId(
|
|
44268
|
+
sessionId
|
|
44269
|
+
) ?? null;
|
|
44270
|
+
this.sessionOwners.set(sessionId, {
|
|
44271
|
+
ownerId,
|
|
44272
|
+
expiresAt: now + this.sessionBranchTtlMs
|
|
44273
|
+
});
|
|
44274
|
+
return ownerId;
|
|
44275
|
+
}
|
|
43797
44276
|
async getBranchVisibility(branchId) {
|
|
43798
44277
|
const cached2 = this.branchVisibility.get(branchId);
|
|
43799
44278
|
const now = this.now();
|
|
@@ -43829,6 +44308,7 @@ var RealtimeAccessCache = class {
|
|
|
43829
44308
|
}
|
|
43830
44309
|
invalidateSession(sessionId) {
|
|
43831
44310
|
this.sessionBranches.delete(sessionId);
|
|
44311
|
+
this.sessionOwners.delete(sessionId);
|
|
43832
44312
|
}
|
|
43833
44313
|
clearVisibility() {
|
|
43834
44314
|
this.branchVisibility.clear();
|
|
@@ -43836,6 +44316,7 @@ var RealtimeAccessCache = class {
|
|
|
43836
44316
|
clearAll() {
|
|
43837
44317
|
this.branchVisibility.clear();
|
|
43838
44318
|
this.sessionBranches.clear();
|
|
44319
|
+
this.sessionOwners.clear();
|
|
43839
44320
|
}
|
|
43840
44321
|
visibilityFromEntry(entry) {
|
|
43841
44322
|
return entry.mode === "allAuthenticated" ? { mode: "allAuthenticated" } : { mode: "explicitUsers", userIds: entry.userIds };
|
|
@@ -43853,6 +44334,26 @@ import { hasMinimumRole as hasMinimumRole7, ROLES as ROLES8 } from "@agor/core/t
|
|
|
43853
44334
|
function tenantChannelName(tenantId) {
|
|
43854
44335
|
return `tenant:${tenantId}`;
|
|
43855
44336
|
}
|
|
44337
|
+
var SESSION_STREAM_CHANNEL_PREFIX = "session-stream:";
|
|
44338
|
+
function sessionStreamChannelName(sessionId) {
|
|
44339
|
+
return `${SESSION_STREAM_CHANNEL_PREFIX}${sessionId}`;
|
|
44340
|
+
}
|
|
44341
|
+
function leaveAllSessionStreamChannels(app, connection) {
|
|
44342
|
+
for (const name of app.channels ?? []) {
|
|
44343
|
+
if (name.startsWith(SESSION_STREAM_CHANNEL_PREFIX)) {
|
|
44344
|
+
app.channel(name).leave(connection);
|
|
44345
|
+
}
|
|
44346
|
+
}
|
|
44347
|
+
}
|
|
44348
|
+
function existingChannel(app, name) {
|
|
44349
|
+
return (app.channels ?? []).includes(name) ? app.channel(name) : null;
|
|
44350
|
+
}
|
|
44351
|
+
function joinSessionStreamChannel(app, sessionId, connection) {
|
|
44352
|
+
app.channel(sessionStreamChannelName(sessionId)).join(connection);
|
|
44353
|
+
}
|
|
44354
|
+
function leaveSessionStreamChannel(app, sessionId, connection) {
|
|
44355
|
+
existingChannel(app, sessionStreamChannelName(sessionId))?.leave(connection);
|
|
44356
|
+
}
|
|
43856
44357
|
var DEBUG_REALTIME_PUBLISH = process.env.AGOR_DEBUG_REALTIME_PUBLISH === "1" || process.env.DEBUG?.includes("realtime-publish");
|
|
43857
44358
|
function realtimePublishDebug(...args) {
|
|
43858
44359
|
if (DEBUG_REALTIME_PUBLISH) {
|
|
@@ -43868,8 +44369,27 @@ var SESSION_ID_SCOPED_PATHS = /* @__PURE__ */ new Set([
|
|
|
43868
44369
|
"session-env-selections"
|
|
43869
44370
|
]);
|
|
43870
44371
|
var OPTIONAL_BRANCH_OR_SESSION_SCOPED_PATHS = /* @__PURE__ */ new Set(["board-objects", "board-comments"]);
|
|
44372
|
+
var MESSAGE_STREAMING_EVENTS = /* @__PURE__ */ new Set([
|
|
44373
|
+
"streaming:start",
|
|
44374
|
+
"streaming:chunk",
|
|
44375
|
+
"streaming:end",
|
|
44376
|
+
"streaming:error",
|
|
44377
|
+
"thinking:start",
|
|
44378
|
+
"thinking:chunk",
|
|
44379
|
+
"thinking:end"
|
|
44380
|
+
]);
|
|
44381
|
+
var TASK_STREAMING_EVENTS = /* @__PURE__ */ new Set(["thinking:chunk", "tool:start", "tool:complete"]);
|
|
43871
44382
|
function isStreamingEvent(context) {
|
|
43872
|
-
|
|
44383
|
+
if (context.path === "messages/streaming") return true;
|
|
44384
|
+
const event = context.event;
|
|
44385
|
+
if (!event) return false;
|
|
44386
|
+
if (context.path === "messages") {
|
|
44387
|
+
return event.startsWith("streaming:") || MESSAGE_STREAMING_EVENTS.has(event);
|
|
44388
|
+
}
|
|
44389
|
+
if (context.path === "tasks") {
|
|
44390
|
+
return TASK_STREAMING_EVENTS.has(event);
|
|
44391
|
+
}
|
|
44392
|
+
return false;
|
|
43873
44393
|
}
|
|
43874
44394
|
function asRecord2(value) {
|
|
43875
44395
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -44005,6 +44525,47 @@ async function resolvePublishScope(data, context, accessCache) {
|
|
|
44005
44525
|
function filterToServiceConnections(authenticated) {
|
|
44006
44526
|
return authenticated.filter((connection) => isServiceConnection(connection));
|
|
44007
44527
|
}
|
|
44528
|
+
async function resolveStreamingDelivery(app, data, tenantScoped, accessCache, branchRbacEnabled, allowSuperadmin) {
|
|
44529
|
+
const serviceConnections = filterToServiceConnections(tenantScoped);
|
|
44530
|
+
const sessionId = extractSessionId(data);
|
|
44531
|
+
if (!sessionId) return serviceConnections;
|
|
44532
|
+
const tenantConnections = new Set(
|
|
44533
|
+
tenantScoped.connections
|
|
44534
|
+
);
|
|
44535
|
+
const existingRoom = existingChannel(app, sessionStreamChannelName(sessionId));
|
|
44536
|
+
const room = existingRoom ? existingRoom.filter((connection) => tenantConnections.has(connection)) : null;
|
|
44537
|
+
let ownerId = null;
|
|
44538
|
+
try {
|
|
44539
|
+
ownerId = await accessCache.getSessionOwnerId(sessionId);
|
|
44540
|
+
} catch {
|
|
44541
|
+
}
|
|
44542
|
+
const ownerChannel = () => tenantScoped.filter(
|
|
44543
|
+
(connection) => userFromConnection(connection)?.user_id === ownerId
|
|
44544
|
+
);
|
|
44545
|
+
if (!branchRbacEnabled) {
|
|
44546
|
+
const channels2 = [serviceConnections];
|
|
44547
|
+
if (room) channels2.push(room);
|
|
44548
|
+
if (ownerId) channels2.push(ownerChannel());
|
|
44549
|
+
return channels2;
|
|
44550
|
+
}
|
|
44551
|
+
const branchId = await accessCache.getBranchIdForSession(sessionId);
|
|
44552
|
+
const visibility = branchId ? await accessCache.getBranchVisibility(branchId) : null;
|
|
44553
|
+
if (!visibility) return serviceConnections;
|
|
44554
|
+
if (visibility.mode === "allAuthenticated") {
|
|
44555
|
+
const channels2 = [serviceConnections];
|
|
44556
|
+
if (room) channels2.push(room);
|
|
44557
|
+
if (ownerId) channels2.push(ownerChannel());
|
|
44558
|
+
return channels2;
|
|
44559
|
+
}
|
|
44560
|
+
const channels = [serviceConnections];
|
|
44561
|
+
if (room) {
|
|
44562
|
+
channels.push(filterToUserIdsOrSuperadmins(room, visibility.userIds, allowSuperadmin));
|
|
44563
|
+
}
|
|
44564
|
+
if (ownerId && visibility.userIds.has(ownerId)) {
|
|
44565
|
+
channels.push(ownerChannel());
|
|
44566
|
+
}
|
|
44567
|
+
return channels;
|
|
44568
|
+
}
|
|
44008
44569
|
function filterToUserIdsOrAdmins(authenticated, userIds, allowSuperadmin) {
|
|
44009
44570
|
return authenticated.filter((connection) => {
|
|
44010
44571
|
if (isServiceConnection(connection) || isAdminConnection(connection, allowSuperadmin)) {
|
|
@@ -44078,6 +44639,16 @@ function configureRealtimePublish(options) {
|
|
|
44078
44639
|
throw error51;
|
|
44079
44640
|
}
|
|
44080
44641
|
}
|
|
44642
|
+
if (isStreamingEvent(context)) {
|
|
44643
|
+
return resolveStreamingDelivery(
|
|
44644
|
+
app,
|
|
44645
|
+
data,
|
|
44646
|
+
tenantScoped,
|
|
44647
|
+
accessCache,
|
|
44648
|
+
branchRbacEnabled,
|
|
44649
|
+
allowSuperadmin
|
|
44650
|
+
);
|
|
44651
|
+
}
|
|
44081
44652
|
if (!branchRbacEnabled) return tenantScoped;
|
|
44082
44653
|
const scope = await resolvePublishScope(data, context, accessCache);
|
|
44083
44654
|
if (scope.kind === "global") return tenantScoped;
|
|
@@ -44210,28 +44781,9 @@ function recomputeNextRunAt() {
|
|
|
44210
44781
|
};
|
|
44211
44782
|
}
|
|
44212
44783
|
|
|
44213
|
-
// src/utils/session-
|
|
44214
|
-
import {
|
|
44215
|
-
|
|
44216
|
-
SessionStatus as SessionStatus2,
|
|
44217
|
-
sessionCanStartTask,
|
|
44218
|
-
TaskStatus
|
|
44219
|
-
} from "@agor/core/types";
|
|
44220
|
-
function isTaskBlockingPrompt(task) {
|
|
44221
|
-
return task.status !== TaskStatus.QUEUED && !isTerminalTaskStatus(task.status);
|
|
44222
|
-
}
|
|
44223
|
-
function isTerminalQueueProcessingSuppressed(params) {
|
|
44224
|
-
return params?.suppressTerminalQueueProcessing === true;
|
|
44225
|
-
}
|
|
44226
|
-
function shouldReconcileSessionPromptState(session, tasks3, options = {}) {
|
|
44227
|
-
if (session.status !== SessionStatus2.FAILED) return false;
|
|
44228
|
-
if (session.ready_for_prompt === true) return false;
|
|
44229
|
-
const ignoredTaskIds = new Set(options.ignoredTaskIds ?? []);
|
|
44230
|
-
return !tasks3.some((task) => !ignoredTaskIds.has(task.task_id) && isTaskBlockingPrompt(task));
|
|
44231
|
-
}
|
|
44232
|
-
|
|
44233
|
-
// src/register-hooks.ts
|
|
44234
|
-
init_spawn_executor();
|
|
44784
|
+
// src/utils/session-queue-tenant-scope.ts
|
|
44785
|
+
import { resolveMultiTenancyConfig as resolveMultiTenancyConfig2 } from "@agor/core/config";
|
|
44786
|
+
import { runWithTenantDatabaseScope as runWithTenantDatabaseScope3 } from "@agor/core/db";
|
|
44235
44787
|
|
|
44236
44788
|
// src/utils/tenant-db-scope.ts
|
|
44237
44789
|
init_runtime_tokens();
|
|
@@ -44244,10 +44796,10 @@ import {
|
|
|
44244
44796
|
enqueueTenantDatabasePostCommitCallback,
|
|
44245
44797
|
getCurrentTenantId as getCurrentTenantId2,
|
|
44246
44798
|
runWithoutTenantDatabaseScope,
|
|
44247
|
-
runWithTenantDatabaseScope
|
|
44799
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope2
|
|
44248
44800
|
} from "@agor/core/db";
|
|
44249
44801
|
import { NotAuthenticated as NotAuthenticated7 } from "@agor/core/feathers";
|
|
44250
|
-
import
|
|
44802
|
+
import jwt3 from "jsonwebtoken";
|
|
44251
44803
|
function readHeaderValue(headers, name) {
|
|
44252
44804
|
if (!headers) return null;
|
|
44253
44805
|
const wanted = name.toLowerCase();
|
|
@@ -44276,7 +44828,7 @@ function deferWithTenantDatabaseScope(db, params, work, onError) {
|
|
|
44276
44828
|
const schedule = () => {
|
|
44277
44829
|
runWithoutTenantDatabaseScope(() => {
|
|
44278
44830
|
setImmediate(() => {
|
|
44279
|
-
void
|
|
44831
|
+
void runWithTenantDatabaseScope2(db, tenantId, work).catch((error51) => {
|
|
44280
44832
|
if (onError) {
|
|
44281
44833
|
onError(error51);
|
|
44282
44834
|
return;
|
|
@@ -44298,7 +44850,7 @@ function createTenantDatabaseScopeAroundHook(options) {
|
|
|
44298
44850
|
const match = authorization?.match(/^Bearer\s+(.+)$/i);
|
|
44299
44851
|
if (!match || !options.jwtSecret) return void 0;
|
|
44300
44852
|
try {
|
|
44301
|
-
return
|
|
44853
|
+
return jwt3.verify(match[1], options.jwtSecret, {
|
|
44302
44854
|
issuer: RUNTIME_JWT_ISSUER,
|
|
44303
44855
|
audience: RUNTIME_JWT_AUDIENCE
|
|
44304
44856
|
});
|
|
@@ -44333,11 +44885,104 @@ function createTenantDatabaseScopeAroundHook(options) {
|
|
|
44333
44885
|
}
|
|
44334
44886
|
throw error51;
|
|
44335
44887
|
}
|
|
44336
|
-
await
|
|
44888
|
+
await runWithTenantDatabaseScope2(options.db, context.params.tenant?.tenant_id, next);
|
|
44337
44889
|
};
|
|
44338
44890
|
}
|
|
44339
44891
|
|
|
44892
|
+
// src/utils/session-queue-tenant-scope.ts
|
|
44893
|
+
function staticTenantId(config2) {
|
|
44894
|
+
const multiTenancy = resolveMultiTenancyConfig2(config2);
|
|
44895
|
+
return multiTenancy.mode === "static" ? multiTenancy.static_tenant_id : void 0;
|
|
44896
|
+
}
|
|
44897
|
+
function trustedTenantIdHint(options) {
|
|
44898
|
+
const hint = options.tenantIdHint?.trim();
|
|
44899
|
+
return hint || void 0;
|
|
44900
|
+
}
|
|
44901
|
+
function queueTenantParams(params, tenantId, source = "explicit") {
|
|
44902
|
+
const currentTenant = params?.tenant;
|
|
44903
|
+
return {
|
|
44904
|
+
...params ?? {},
|
|
44905
|
+
tenant: {
|
|
44906
|
+
...currentTenant,
|
|
44907
|
+
tenant_id: tenantId,
|
|
44908
|
+
source: currentTenant?.source ?? source
|
|
44909
|
+
}
|
|
44910
|
+
};
|
|
44911
|
+
}
|
|
44912
|
+
async function runWithSessionQueueTenantScope(options, work) {
|
|
44913
|
+
const capturedTenantId = resolveTenantIdForDeferredScope(options.params);
|
|
44914
|
+
if (capturedTenantId) {
|
|
44915
|
+
const scopedParams2 = queueTenantParams(options.params, capturedTenantId, "explicit");
|
|
44916
|
+
return runWithTenantDatabaseScope3(options.db, capturedTenantId, () => work(scopedParams2));
|
|
44917
|
+
}
|
|
44918
|
+
const tenantIdHint = trustedTenantIdHint(options);
|
|
44919
|
+
if (tenantIdHint) {
|
|
44920
|
+
const scopedParams2 = queueTenantParams(options.params, tenantIdHint, "explicit");
|
|
44921
|
+
return runWithTenantDatabaseScope3(options.db, tenantIdHint, () => work(scopedParams2));
|
|
44922
|
+
}
|
|
44923
|
+
const configuredStaticTenantId = staticTenantId(options.config);
|
|
44924
|
+
if (!configuredStaticTenantId) {
|
|
44925
|
+
console.error(
|
|
44926
|
+
`\u274C [Queue] ${options.label} skipped for session ${options.sessionId}: missing tenant context`
|
|
44927
|
+
);
|
|
44928
|
+
return void 0;
|
|
44929
|
+
}
|
|
44930
|
+
const scopedParams = queueTenantParams(options.params, configuredStaticTenantId, "static");
|
|
44931
|
+
return runWithTenantDatabaseScope3(options.db, configuredStaticTenantId, () => work(scopedParams));
|
|
44932
|
+
}
|
|
44933
|
+
function deferWithSessionQueueTenantScope(options, work, onError) {
|
|
44934
|
+
const handleError = (error51) => {
|
|
44935
|
+
if (onError) {
|
|
44936
|
+
onError(error51);
|
|
44937
|
+
return;
|
|
44938
|
+
}
|
|
44939
|
+
console.error(`\u274C [Queue] ${options.label} failed:`, error51);
|
|
44940
|
+
};
|
|
44941
|
+
const capturedTenantId = resolveTenantIdForDeferredScope(options.params);
|
|
44942
|
+
if (capturedTenantId) {
|
|
44943
|
+
const scopedParams2 = queueTenantParams(options.params, capturedTenantId, "explicit");
|
|
44944
|
+
deferWithTenantDatabaseScope(options.db, scopedParams2, () => work(scopedParams2), handleError);
|
|
44945
|
+
return;
|
|
44946
|
+
}
|
|
44947
|
+
const tenantIdHint = trustedTenantIdHint(options);
|
|
44948
|
+
if (tenantIdHint) {
|
|
44949
|
+
const scopedParams2 = queueTenantParams(options.params, tenantIdHint, "explicit");
|
|
44950
|
+
deferWithTenantDatabaseScope(options.db, scopedParams2, () => work(scopedParams2), handleError);
|
|
44951
|
+
return;
|
|
44952
|
+
}
|
|
44953
|
+
const configuredStaticTenantId = staticTenantId(options.config);
|
|
44954
|
+
if (!configuredStaticTenantId) {
|
|
44955
|
+
handleError(
|
|
44956
|
+
new Error(`${options.label} skipped for session ${options.sessionId}: missing tenant context`)
|
|
44957
|
+
);
|
|
44958
|
+
return;
|
|
44959
|
+
}
|
|
44960
|
+
const scopedParams = queueTenantParams(options.params, configuredStaticTenantId, "static");
|
|
44961
|
+
deferWithTenantDatabaseScope(options.db, scopedParams, () => work(scopedParams), handleError);
|
|
44962
|
+
}
|
|
44963
|
+
|
|
44964
|
+
// src/utils/session-task-state.ts
|
|
44965
|
+
import {
|
|
44966
|
+
isTerminalTaskStatus,
|
|
44967
|
+
SessionStatus as SessionStatus2,
|
|
44968
|
+
sessionCanStartTask,
|
|
44969
|
+
TaskStatus
|
|
44970
|
+
} from "@agor/core/types";
|
|
44971
|
+
function isTaskBlockingPrompt(task) {
|
|
44972
|
+
return task.status !== TaskStatus.QUEUED && !isTerminalTaskStatus(task.status);
|
|
44973
|
+
}
|
|
44974
|
+
function isTerminalQueueProcessingSuppressed(params) {
|
|
44975
|
+
return params?.suppressTerminalQueueProcessing === true;
|
|
44976
|
+
}
|
|
44977
|
+
function shouldReconcileSessionPromptState(session, tasks3, options = {}) {
|
|
44978
|
+
if (session.status !== SessionStatus2.FAILED) return false;
|
|
44979
|
+
if (session.ready_for_prompt === true) return false;
|
|
44980
|
+
const ignoredTaskIds = new Set(options.ignoredTaskIds ?? []);
|
|
44981
|
+
return !tasks3.some((task) => !ignoredTaskIds.has(task.task_id) && isTaskBlockingPrompt(task));
|
|
44982
|
+
}
|
|
44983
|
+
|
|
44340
44984
|
// src/register-hooks.ts
|
|
44985
|
+
init_spawn_executor();
|
|
44341
44986
|
var DEBUG_MCP_TOKENS2 = process.env.AGOR_DEBUG_MCP_TOKENS === "1" || process.env.DEBUG?.includes("mcp-tokens");
|
|
44342
44987
|
function mcpTokenDebug2(...args) {
|
|
44343
44988
|
if (DEBUG_MCP_TOKENS2) {
|
|
@@ -44449,6 +45094,10 @@ function shouldRunSessionPostTurnHooks(session) {
|
|
|
44449
45094
|
function shouldDrainQueueAfterSessionPostTurnPatch(session, params) {
|
|
44450
45095
|
return shouldRunSessionPostTurnHooks(session) && session.ready_for_prompt === true && !isTerminalQueueProcessingSuppressed(params);
|
|
44451
45096
|
}
|
|
45097
|
+
function getTrustedSessionTenantId(session) {
|
|
45098
|
+
const tenantId = session?.tenant_id;
|
|
45099
|
+
return typeof tenantId === "string" && tenantId.length > 0 ? tenantId : void 0;
|
|
45100
|
+
}
|
|
44452
45101
|
async function enrichSessionFindResultWithRemoteRelationships(result, sessionsService) {
|
|
44453
45102
|
if (isRemoteRelationshipsEnrichedResult(result)) return result;
|
|
44454
45103
|
if (Array.isArray(result)) {
|
|
@@ -44463,6 +45112,7 @@ async function enrichSessionFindResultWithRemoteRelationships(result, sessionsSe
|
|
|
44463
45112
|
}
|
|
44464
45113
|
var TENANT_OWNED_SERVICE_PATHS = [
|
|
44465
45114
|
"sessions",
|
|
45115
|
+
"sessions/:id/mcp-servers",
|
|
44466
45116
|
"session-relationships",
|
|
44467
45117
|
"tasks",
|
|
44468
45118
|
"messages",
|
|
@@ -44479,6 +45129,14 @@ var TENANT_OWNED_SERVICE_PATHS = [
|
|
|
44479
45129
|
"boards/:id/group-grants",
|
|
44480
45130
|
"app-variables",
|
|
44481
45131
|
"mcp-servers",
|
|
45132
|
+
"mcp-servers/discover",
|
|
45133
|
+
"mcp-servers/oauth-auth-headers",
|
|
45134
|
+
"mcp-servers/oauth-complete",
|
|
45135
|
+
"mcp-servers/oauth-disconnect",
|
|
45136
|
+
"mcp-servers/oauth-refresh",
|
|
45137
|
+
"mcp-servers/oauth-start",
|
|
45138
|
+
"mcp-servers/oauth-status",
|
|
45139
|
+
"mcp-servers/test-oauth",
|
|
44482
45140
|
"card-types",
|
|
44483
45141
|
"cards",
|
|
44484
45142
|
"artifacts",
|
|
@@ -44525,7 +45183,7 @@ function registerHooks(ctx) {
|
|
|
44525
45183
|
return void 0;
|
|
44526
45184
|
}
|
|
44527
45185
|
};
|
|
44528
|
-
const multiTenancy =
|
|
45186
|
+
const multiTenancy = resolveMultiTenancyConfig4(config2);
|
|
44529
45187
|
const tenantColumnsEnabled = resolveMultiTenancyDatabaseDialect(config2) === "postgresql";
|
|
44530
45188
|
const executionMode = resolveExecutionSecurityMode(config2);
|
|
44531
45189
|
const tenantOwnedServicePaths = TENANT_OWNED_SERVICE_PATHS;
|
|
@@ -44683,16 +45341,36 @@ function registerHooks(ctx) {
|
|
|
44683
45341
|
);
|
|
44684
45342
|
return context;
|
|
44685
45343
|
};
|
|
44686
|
-
const
|
|
45344
|
+
const membershipGroupIdFromContext = (context) => {
|
|
45345
|
+
const resultGroupId = context.result?.group_id;
|
|
45346
|
+
if (typeof resultGroupId === "string" && resultGroupId.length > 0) {
|
|
45347
|
+
return resultGroupId;
|
|
45348
|
+
}
|
|
45349
|
+
const dataGroupId = context.data?.group_id;
|
|
45350
|
+
if (typeof dataGroupId === "string" && dataGroupId.length > 0) return dataGroupId;
|
|
45351
|
+
const queryGroupId = context.params.query?.group_id;
|
|
45352
|
+
if (typeof queryGroupId === "string" && queryGroupId.length > 0) return queryGroupId;
|
|
45353
|
+
const routeGroupId = context.params.route?.groupId;
|
|
45354
|
+
if (typeof routeGroupId === "string" && routeGroupId.length > 0) return routeGroupId;
|
|
45355
|
+
return void 0;
|
|
45356
|
+
};
|
|
45357
|
+
const syncUnixAccessForGroupGrantedBranches = async (context, logPrefix) => {
|
|
44687
45358
|
if (!executionMode.unixFsIsolationEnabled) return context;
|
|
44688
|
-
const
|
|
44689
|
-
|
|
44690
|
-
|
|
44691
|
-
|
|
44692
|
-
logPrefix,
|
|
44693
|
-
context.params
|
|
45359
|
+
const groupId = membershipGroupIdFromContext(context);
|
|
45360
|
+
if (!groupId) {
|
|
45361
|
+
console.warn(
|
|
45362
|
+
`[Unix Integration] Could not resolve group_id for ${context.path}.${context.method}; skipping group membership permission sync`
|
|
44694
45363
|
);
|
|
44695
|
-
|
|
45364
|
+
return context;
|
|
45365
|
+
}
|
|
45366
|
+
const branchIds = await branchRepository.findExplicitFsAccessBranchIdsForGroup(groupId);
|
|
45367
|
+
if (branchIds.length === 0) return context;
|
|
45368
|
+
console.log(
|
|
45369
|
+
`[Unix Integration] Queueing group membership permission sync for ${branchIds.length} branch(es) granted to group ${shortId5(groupId)}`
|
|
45370
|
+
);
|
|
45371
|
+
for (const branchId of branchIds) {
|
|
45372
|
+
syncBranchUnixAccess(branchId, logPrefix, context.params);
|
|
45373
|
+
await invalidateRealtimeBranchAccess(branchId);
|
|
44696
45374
|
}
|
|
44697
45375
|
return context;
|
|
44698
45376
|
};
|
|
@@ -45236,7 +45914,13 @@ function registerHooks(ctx) {
|
|
|
45236
45914
|
});
|
|
45237
45915
|
const injectPerUserOAuthTokens = async (context) => {
|
|
45238
45916
|
const queryForUserId = context.params?.query?.forUserId;
|
|
45239
|
-
const
|
|
45917
|
+
const authPayloadType = context.params?.authentication?.payload?.type;
|
|
45918
|
+
const userId = resolveForUserIdWithGate({
|
|
45919
|
+
queryForUserId,
|
|
45920
|
+
isServiceAccount: context.params?.user?._isServiceAccount,
|
|
45921
|
+
authPayloadType,
|
|
45922
|
+
callerUserId: context.params?.user?.user_id
|
|
45923
|
+
});
|
|
45240
45924
|
if (!userId) {
|
|
45241
45925
|
return context;
|
|
45242
45926
|
}
|
|
@@ -45593,11 +46277,11 @@ function registerHooks(ctx) {
|
|
|
45593
46277
|
after: {
|
|
45594
46278
|
create: [
|
|
45595
46279
|
clearRealtimeBranchVisibility,
|
|
45596
|
-
(context) =>
|
|
46280
|
+
(context) => syncUnixAccessForGroupGrantedBranches(context, "[Executor/group-memberships.create]")
|
|
45597
46281
|
],
|
|
45598
46282
|
remove: [
|
|
45599
46283
|
clearRealtimeBranchVisibility,
|
|
45600
|
-
(context) =>
|
|
46284
|
+
(context) => syncUnixAccessForGroupGrantedBranches(context, "[Executor/group-memberships.remove]")
|
|
45601
46285
|
]
|
|
45602
46286
|
}
|
|
45603
46287
|
});
|
|
@@ -46243,19 +46927,29 @@ function registerHooks(ctx) {
|
|
|
46243
46927
|
}
|
|
46244
46928
|
});
|
|
46245
46929
|
if (shouldDrainQueueAfterSessionPostTurnPatch(session, context.params)) {
|
|
46246
|
-
|
|
46247
|
-
|
|
46930
|
+
const sessionTenantId = getTrustedSessionTenantId(session);
|
|
46931
|
+
deferWithSessionQueueTenantScope(
|
|
46932
|
+
{
|
|
46933
|
+
db,
|
|
46934
|
+
config: config2,
|
|
46935
|
+
sessionId: session.session_id,
|
|
46936
|
+
params: context.params,
|
|
46937
|
+
tenantIdHint: sessionTenantId,
|
|
46938
|
+
label: "SessionsService.after.patch queue drain"
|
|
46939
|
+
},
|
|
46940
|
+
async (queueParams) => {
|
|
46248
46941
|
console.log(
|
|
46249
46942
|
`\u{1F504} [SessionsService.after.patch] Session ${shortId5(session.session_id)} became promptable (${session.status}), checking for queued tasks...`
|
|
46250
46943
|
);
|
|
46251
|
-
await sessionsService.triggerQueueProcessing(session.session_id,
|
|
46252
|
-
}
|
|
46944
|
+
await sessionsService.triggerQueueProcessing(session.session_id, queueParams);
|
|
46945
|
+
},
|
|
46946
|
+
(error51) => {
|
|
46253
46947
|
console.error(
|
|
46254
46948
|
`\u274C [SessionsService.after.patch] Failed to process queue for session ${shortId5(session.session_id)}:`,
|
|
46255
46949
|
error51
|
|
46256
46950
|
);
|
|
46257
46951
|
}
|
|
46258
|
-
|
|
46952
|
+
);
|
|
46259
46953
|
} else {
|
|
46260
46954
|
console.log(
|
|
46261
46955
|
`\u23ED\uFE0F [SessionsService.after.patch] Queue drain suppressed for session ${shortId5(session.session_id)} (suppressTerminalQueueProcessing or not ready)`
|
|
@@ -46464,6 +47158,17 @@ function registerHooks(ctx) {
|
|
|
46464
47158
|
context.dispatch = result;
|
|
46465
47159
|
return context;
|
|
46466
47160
|
}
|
|
47161
|
+
if (_action === "mergeObjectFields" && objects) {
|
|
47162
|
+
if (!context.id) throw new Error("Board ID required");
|
|
47163
|
+
const result = await boardsService.mergeBoardObjectFields(
|
|
47164
|
+
context.id,
|
|
47165
|
+
objects
|
|
47166
|
+
);
|
|
47167
|
+
context.result = result;
|
|
47168
|
+
app.service("boards").emit("patched", result);
|
|
47169
|
+
context.dispatch = result;
|
|
47170
|
+
return context;
|
|
47171
|
+
}
|
|
46467
47172
|
if (_action === "deleteZone" && objectId) {
|
|
46468
47173
|
if (!context.id) throw new Error("Board ID required");
|
|
46469
47174
|
const result = await boardsService.deleteZone(
|
|
@@ -46676,7 +47381,7 @@ function registerHooks(ctx) {
|
|
|
46676
47381
|
import {
|
|
46677
47382
|
loadConfig as loadConfig4,
|
|
46678
47383
|
resolveBranchStorageConfig,
|
|
46679
|
-
resolveMultiTenancyConfig as
|
|
47384
|
+
resolveMultiTenancyConfig as resolveMultiTenancyConfig6
|
|
46680
47385
|
} from "@agor/core/config";
|
|
46681
47386
|
import {
|
|
46682
47387
|
BranchRepository as BranchRepository9,
|
|
@@ -46697,7 +47402,7 @@ import {
|
|
|
46697
47402
|
BadRequest as BadRequest9,
|
|
46698
47403
|
Conflict as Conflict2,
|
|
46699
47404
|
errorHandler,
|
|
46700
|
-
Forbidden as
|
|
47405
|
+
Forbidden as Forbidden14,
|
|
46701
47406
|
LocalStrategy,
|
|
46702
47407
|
NotAuthenticated as NotAuthenticated15,
|
|
46703
47408
|
NotFound as NotFound5
|
|
@@ -46705,9 +47410,9 @@ import {
|
|
|
46705
47410
|
import { PermissionService } from "@agor/core/permissions";
|
|
46706
47411
|
import {
|
|
46707
47412
|
AGENTIC_TOOL_CAPABILITIES as AGENTIC_TOOL_CAPABILITIES2,
|
|
46708
|
-
hasMinimumRole as
|
|
47413
|
+
hasMinimumRole as hasMinimumRole14,
|
|
46709
47414
|
MessageRole as MessageRole4,
|
|
46710
|
-
ROLES as
|
|
47415
|
+
ROLES as ROLES17,
|
|
46711
47416
|
SERVICE_GROUP_NAMES as SERVICE_GROUP_NAMES2,
|
|
46712
47417
|
SessionStatus as SessionStatus6,
|
|
46713
47418
|
TaskStatus as TaskStatus5
|
|
@@ -46715,12 +47420,64 @@ import {
|
|
|
46715
47420
|
import { NotFoundError as NotFoundError4 } from "@agor/core/utils/errors";
|
|
46716
47421
|
import { rateLimit as rateLimit2 } from "express-rate-limit";
|
|
46717
47422
|
|
|
47423
|
+
// src/auth/issue-browser-tokens-hook.ts
|
|
47424
|
+
init_runtime_tokens();
|
|
47425
|
+
init_token_invalidation();
|
|
47426
|
+
|
|
47427
|
+
// src/auth/user-redaction.ts
|
|
47428
|
+
function redactUserAuthMetadata(user) {
|
|
47429
|
+
const {
|
|
47430
|
+
tokens_valid_after: _tokensValidAfter,
|
|
47431
|
+
password: _password,
|
|
47432
|
+
tenant_id: _tenantId,
|
|
47433
|
+
...publicUser
|
|
47434
|
+
} = user;
|
|
47435
|
+
return publicUser;
|
|
47436
|
+
}
|
|
47437
|
+
|
|
47438
|
+
// src/auth/issue-browser-tokens-hook.ts
|
|
47439
|
+
var MACHINE_TOKEN_TYPES = /* @__PURE__ */ new Set(["executor-session", "service"]);
|
|
47440
|
+
function createIssueBrowserTokensHook(options) {
|
|
47441
|
+
const { jwtSecret, accessTokenTtl, refreshTokenTtl, tenantClaim, debug } = options;
|
|
47442
|
+
return async (context) => {
|
|
47443
|
+
debug?.("\u2705 Authentication succeeded:", {
|
|
47444
|
+
strategy: context.result?.authentication?.strategy,
|
|
47445
|
+
hasUser: !!context.result?.user,
|
|
47446
|
+
user_id: context.result?.user?.user_id,
|
|
47447
|
+
hasAccessToken: !!context.result?.accessToken
|
|
47448
|
+
});
|
|
47449
|
+
if (!context.result?.user) {
|
|
47450
|
+
return context;
|
|
47451
|
+
}
|
|
47452
|
+
const payloadType = context.result.authentication?.payload?.type;
|
|
47453
|
+
if (typeof payloadType === "string" && MACHINE_TOKEN_TYPES.has(payloadType)) {
|
|
47454
|
+
context.result.user = redactUserAuthMetadata(context.result.user);
|
|
47455
|
+
return context;
|
|
47456
|
+
}
|
|
47457
|
+
const tenantId = context.params?.tenant?.tenant_id ?? context.result.user.tenant_id;
|
|
47458
|
+
const tokens = issueRuntimeTokenPair(
|
|
47459
|
+
context.result.user,
|
|
47460
|
+
jwtSecret,
|
|
47461
|
+
accessTokenTtl,
|
|
47462
|
+
refreshTokenTtl,
|
|
47463
|
+
{
|
|
47464
|
+
...authTokenIssuedAtClaim(Date.now(), context.result.user),
|
|
47465
|
+
...runtimeTenantClaims(tenantId, tenantClaim)
|
|
47466
|
+
}
|
|
47467
|
+
);
|
|
47468
|
+
context.result.accessToken = tokens.accessToken;
|
|
47469
|
+
context.result.refreshToken = tokens.refreshToken;
|
|
47470
|
+
context.result.user = redactUserAuthMetadata(context.result.user);
|
|
47471
|
+
return context;
|
|
47472
|
+
};
|
|
47473
|
+
}
|
|
47474
|
+
|
|
46718
47475
|
// src/auth/launch-auth.ts
|
|
46719
47476
|
init_runtime_tokens();
|
|
46720
47477
|
init_token_invalidation();
|
|
46721
47478
|
import { createHash, createPublicKey, randomBytes } from "crypto";
|
|
46722
47479
|
import {
|
|
46723
|
-
resolveMultiTenancyConfig as
|
|
47480
|
+
resolveMultiTenancyConfig as resolveMultiTenancyConfig5,
|
|
46724
47481
|
resolveTenantContext as resolveTenantContext4,
|
|
46725
47482
|
TenantResolutionError as TenantResolutionError4
|
|
46726
47483
|
} from "@agor/core/config";
|
|
@@ -46730,27 +47487,14 @@ import {
|
|
|
46730
47487
|
hash as hash2,
|
|
46731
47488
|
insert as insert2,
|
|
46732
47489
|
reattributeLegacyAnonymousRows,
|
|
46733
|
-
runWithTenantDatabaseScope as
|
|
47490
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope5,
|
|
46734
47491
|
select as select2,
|
|
46735
47492
|
update as update2,
|
|
46736
47493
|
users as users2
|
|
46737
47494
|
} from "@agor/core/db";
|
|
46738
47495
|
import { BadRequest as BadRequest6, NotAuthenticated as NotAuthenticated10 } from "@agor/core/feathers";
|
|
46739
47496
|
import { normalizeRole as normalizeRole2, ROLES as ROLES11 } from "@agor/core/types";
|
|
46740
|
-
import
|
|
46741
|
-
|
|
46742
|
-
// src/auth/user-redaction.ts
|
|
46743
|
-
function redactUserAuthMetadata(user) {
|
|
46744
|
-
const {
|
|
46745
|
-
tokens_valid_after: _tokensValidAfter,
|
|
46746
|
-
password: _password,
|
|
46747
|
-
tenant_id: _tenantId,
|
|
46748
|
-
...publicUser
|
|
46749
|
-
} = user;
|
|
46750
|
-
return publicUser;
|
|
46751
|
-
}
|
|
46752
|
-
|
|
46753
|
-
// src/auth/launch-auth.ts
|
|
47497
|
+
import jwt5 from "jsonwebtoken";
|
|
46754
47498
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
46755
47499
|
var DEFAULT_SERVICE_TOKEN_ENV = "AGOR_EXTERNAL_LAUNCH_SERVICE_TOKEN";
|
|
46756
47500
|
var DEFAULT_SHARED_SECRET_ENV = "AGOR_EXTERNAL_LAUNCH_SHARED_SECRET";
|
|
@@ -47015,13 +47759,13 @@ async function resolveVerificationKey(header, settings) {
|
|
|
47015
47759
|
return createPublicKey({ key: jwk, format: "jwk" });
|
|
47016
47760
|
}
|
|
47017
47761
|
async function verifyLaunchAssertion(assertion, settings) {
|
|
47018
|
-
const decoded =
|
|
47762
|
+
const decoded = jwt5.decode(assertion, { complete: true });
|
|
47019
47763
|
if (!decoded || typeof decoded !== "object") {
|
|
47020
47764
|
throw new NotAuthenticated10("Invalid one-time launch assertion");
|
|
47021
47765
|
}
|
|
47022
47766
|
const key = await resolveVerificationKey(decoded.header, settings);
|
|
47023
47767
|
const algorithms = settings.algorithms ?? (settings.devSharedSecret ? ["HS256"] : void 0);
|
|
47024
|
-
const claims =
|
|
47768
|
+
const claims = jwt5.verify(assertion, key, {
|
|
47025
47769
|
issuer: settings.issuer,
|
|
47026
47770
|
audience: settings.audience,
|
|
47027
47771
|
algorithms
|
|
@@ -47064,7 +47808,7 @@ function issueRuntimeTokens(user, jwtSecret, accessTokenTtl, refreshTokenTtl, te
|
|
|
47064
47808
|
};
|
|
47065
47809
|
}
|
|
47066
47810
|
function createLaunchAuthService(options) {
|
|
47067
|
-
const multiTenancy =
|
|
47811
|
+
const multiTenancy = resolveMultiTenancyConfig5(options.config);
|
|
47068
47812
|
const tenantClaim = multiTenancy.auth_claim ?? "tenant_id";
|
|
47069
47813
|
return {
|
|
47070
47814
|
async create(data, params) {
|
|
@@ -47088,7 +47832,7 @@ function createLaunchAuthService(options) {
|
|
|
47088
47832
|
authPayload: claims,
|
|
47089
47833
|
headers: params?.headers
|
|
47090
47834
|
});
|
|
47091
|
-
return await
|
|
47835
|
+
return await runWithTenantDatabaseScope5(options.db, tenant.tenant_id, async () => {
|
|
47092
47836
|
const user = await upsertLaunchUser(options, claims, tenant);
|
|
47093
47837
|
return issueRuntimeTokens(
|
|
47094
47838
|
user,
|
|
@@ -47116,12 +47860,12 @@ function createLaunchAuthService(options) {
|
|
|
47116
47860
|
init_runtime_tokens();
|
|
47117
47861
|
init_token_invalidation();
|
|
47118
47862
|
import { NotAuthenticated as NotAuthenticated11 } from "@agor/core/feathers";
|
|
47119
|
-
import
|
|
47863
|
+
import jwt6 from "jsonwebtoken";
|
|
47120
47864
|
function createRefreshTokenService(options) {
|
|
47121
47865
|
return {
|
|
47122
47866
|
async create(data, _params) {
|
|
47123
47867
|
try {
|
|
47124
|
-
const decoded =
|
|
47868
|
+
const decoded = jwt6.verify(data.refreshToken, options.jwtSecret, {
|
|
47125
47869
|
issuer: RUNTIME_JWT_ISSUER,
|
|
47126
47870
|
audience: RUNTIME_JWT_AUDIENCE
|
|
47127
47871
|
});
|
|
@@ -47210,7 +47954,7 @@ import {
|
|
|
47210
47954
|
getCurrentTenantId as getCurrentTenantId4,
|
|
47211
47955
|
isPostgresDatabase,
|
|
47212
47956
|
runWithSystemDatabaseScope,
|
|
47213
|
-
runWithTenantDatabaseScope as
|
|
47957
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope6,
|
|
47214
47958
|
ScheduleRepository as ScheduleRepository2,
|
|
47215
47959
|
SessionMCPServerRepository as SessionMCPServerRepository2,
|
|
47216
47960
|
SessionRepository as SessionRepository4,
|
|
@@ -47382,7 +48126,7 @@ var SchedulerService = class {
|
|
|
47382
48126
|
continue;
|
|
47383
48127
|
}
|
|
47384
48128
|
try {
|
|
47385
|
-
await
|
|
48129
|
+
await runWithTenantDatabaseScope6(this.db, ref.tenantId, async () => {
|
|
47386
48130
|
const schedule = await this.scheduleRepo.findById(ref.scheduleId);
|
|
47387
48131
|
if (!schedule) return;
|
|
47388
48132
|
await this.processSchedule(schedule, now);
|
|
@@ -47405,7 +48149,7 @@ var SchedulerService = class {
|
|
|
47405
48149
|
}));
|
|
47406
48150
|
};
|
|
47407
48151
|
if (this.config.tenantId) {
|
|
47408
|
-
return
|
|
48152
|
+
return runWithTenantDatabaseScope6(
|
|
47409
48153
|
this.db,
|
|
47410
48154
|
this.config.tenantId,
|
|
47411
48155
|
() => findDue(this.config.tenantId)
|
|
@@ -47875,7 +48619,7 @@ init_users();
|
|
|
47875
48619
|
init_runtime_tokens();
|
|
47876
48620
|
import { resolveProxies } from "@agor/core/config";
|
|
47877
48621
|
import { rateLimit } from "express-rate-limit";
|
|
47878
|
-
import
|
|
48622
|
+
import jwt7 from "jsonwebtoken";
|
|
47879
48623
|
var RATE_LIMIT_PER_MINUTE = 600;
|
|
47880
48624
|
var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
47881
48625
|
var UPSTREAM_TIMEOUT_MS = 3e4;
|
|
@@ -47900,7 +48644,7 @@ async function authenticateRequest(req, app, jwtSecret) {
|
|
|
47900
48644
|
if (!match) return null;
|
|
47901
48645
|
let decoded;
|
|
47902
48646
|
try {
|
|
47903
|
-
decoded =
|
|
48647
|
+
decoded = jwt7.verify(match[1], jwtSecret, {
|
|
47904
48648
|
issuer: RUNTIME_JWT_ISSUER,
|
|
47905
48649
|
audience: [RUNTIME_JWT_AUDIENCE, ARTIFACT_RUNTIME_JWT_AUDIENCE]
|
|
47906
48650
|
});
|
|
@@ -48612,6 +49356,14 @@ async function doResolveWidget(widgetId, action, caller, deps) {
|
|
|
48612
49356
|
const entry = getWidget(widget.widget_type);
|
|
48613
49357
|
let resultMeta;
|
|
48614
49358
|
let autoResumePrompt;
|
|
49359
|
+
const ctx = {
|
|
49360
|
+
// biome-ignore lint/suspicious/noExplicitAny: Pass-through Feathers Application
|
|
49361
|
+
app: deps.app,
|
|
49362
|
+
sessionId: message.session_id,
|
|
49363
|
+
submitterUserId: caller.user_id,
|
|
49364
|
+
submitterRole: caller.role,
|
|
49365
|
+
sessionCreatorUserId: session.created_by
|
|
49366
|
+
};
|
|
48615
49367
|
if (action.kind === "submit") {
|
|
48616
49368
|
if (!entry) {
|
|
48617
49369
|
throw new NotFound3(
|
|
@@ -48623,18 +49375,13 @@ async function doResolveWidget(widgetId, action, caller, deps) {
|
|
|
48623
49375
|
throw new Forbidden12(`Invalid submit payload: ${parsed.error.message}`);
|
|
48624
49376
|
}
|
|
48625
49377
|
const submit = parsed.data;
|
|
48626
|
-
|
|
48627
|
-
// biome-ignore lint/suspicious/noExplicitAny: Pass-through Feathers Application
|
|
48628
|
-
app: deps.app,
|
|
48629
|
-
sessionId: message.session_id,
|
|
48630
|
-
submitterUserId: caller.user_id,
|
|
48631
|
-
submitterRole: caller.role,
|
|
48632
|
-
sessionCreatorUserId: session.created_by
|
|
48633
|
-
};
|
|
48634
|
-
await entry.applySubmit(submitCtx, submit, widget.params);
|
|
49378
|
+
await entry.applySubmit(ctx, submit, widget.params);
|
|
48635
49379
|
resultMeta = entry.buildResultMeta(submit);
|
|
48636
49380
|
autoResumePrompt = entry.buildAutoResumePrompt(resultMeta, widget.params);
|
|
48637
49381
|
} else {
|
|
49382
|
+
if (entry?.authorizeDismiss) {
|
|
49383
|
+
await entry.authorizeDismiss(ctx, widget.params);
|
|
49384
|
+
}
|
|
48638
49385
|
autoResumePrompt = entry ? entry.buildDismissedPrompt(widget.params) : `[Agor] User dismissed a widget request. Do not re-request immediately \u2014 ask whether to proceed without, or move on to other work.`;
|
|
48639
49386
|
}
|
|
48640
49387
|
const newStatus = action.kind === "submit" ? "submitted" : "dismissed";
|
|
@@ -48767,7 +49514,7 @@ async function registerRoutes(ctx) {
|
|
|
48767
49514
|
const appRecord = app;
|
|
48768
49515
|
const sessionTokenService = appRecord.sessionTokenService;
|
|
48769
49516
|
const authStrategiesArray = ["api-key", "jwt", "local"];
|
|
48770
|
-
const multiTenancy =
|
|
49517
|
+
const multiTenancy = resolveMultiTenancyConfig6(config2);
|
|
48771
49518
|
const tenantTokenClaim = multiTenancy.auth_claim ?? "tenant_id";
|
|
48772
49519
|
if (sessionTokenService) {
|
|
48773
49520
|
authStrategiesArray.push("session-token");
|
|
@@ -48832,32 +49579,13 @@ async function registerRoutes(ctx) {
|
|
|
48832
49579
|
authService.hooks({
|
|
48833
49580
|
after: {
|
|
48834
49581
|
create: [
|
|
48835
|
-
|
|
48836
|
-
|
|
48837
|
-
|
|
48838
|
-
|
|
48839
|
-
|
|
48840
|
-
|
|
48841
|
-
|
|
48842
|
-
});
|
|
48843
|
-
if (context.result?.user) {
|
|
48844
|
-
const tenantId = context.params?.tenant?.tenant_id ?? context.result.user.tenant_id;
|
|
48845
|
-
const tokens = issueRuntimeTokenPair(
|
|
48846
|
-
context.result.user,
|
|
48847
|
-
jwtSecret,
|
|
48848
|
-
ACCESS_TOKEN_TTL,
|
|
48849
|
-
REFRESH_TOKEN_TTL,
|
|
48850
|
-
{
|
|
48851
|
-
...authTokenIssuedAtClaim(Date.now(), context.result.user),
|
|
48852
|
-
...runtimeTenantClaims(tenantId, tenantTokenClaim)
|
|
48853
|
-
}
|
|
48854
|
-
);
|
|
48855
|
-
context.result.accessToken = tokens.accessToken;
|
|
48856
|
-
context.result.refreshToken = tokens.refreshToken;
|
|
48857
|
-
context.result.user = redactUserAuthMetadata(context.result.user);
|
|
48858
|
-
}
|
|
48859
|
-
return context;
|
|
48860
|
-
}
|
|
49582
|
+
createIssueBrowserTokensHook({
|
|
49583
|
+
jwtSecret,
|
|
49584
|
+
accessTokenTtl: ACCESS_TOKEN_TTL,
|
|
49585
|
+
refreshTokenTtl: REFRESH_TOKEN_TTL,
|
|
49586
|
+
tenantClaim: tenantTokenClaim,
|
|
49587
|
+
debug: authEventDebug
|
|
49588
|
+
})
|
|
48861
49589
|
]
|
|
48862
49590
|
}
|
|
48863
49591
|
});
|
|
@@ -48901,12 +49629,12 @@ async function registerRoutes(ctx) {
|
|
|
48901
49629
|
throw new NotAuthenticated15("Authentication required");
|
|
48902
49630
|
}
|
|
48903
49631
|
const caller = authParams.user;
|
|
48904
|
-
if (!
|
|
48905
|
-
throw new
|
|
49632
|
+
if (!hasMinimumRole14(caller.role, ROLES17.SUPERADMIN)) {
|
|
49633
|
+
throw new Forbidden14("Superadmin role required for impersonation");
|
|
48906
49634
|
}
|
|
48907
49635
|
const authPayload = authParams.authentication?.payload;
|
|
48908
49636
|
if (authPayload?.is_impersonated === true) {
|
|
48909
|
-
throw new
|
|
49637
|
+
throw new Forbidden14("Cannot impersonate from an already-impersonated token");
|
|
48910
49638
|
}
|
|
48911
49639
|
if (!data?.user_id) {
|
|
48912
49640
|
throw new BadRequest9("user_id is required");
|
|
@@ -48987,7 +49715,7 @@ async function registerRoutes(ctx) {
|
|
|
48987
49715
|
}
|
|
48988
49716
|
},
|
|
48989
49717
|
{
|
|
48990
|
-
create: { role:
|
|
49718
|
+
create: { role: ROLES17.MEMBER, action: "create messages" }
|
|
48991
49719
|
},
|
|
48992
49720
|
requireAuth
|
|
48993
49721
|
);
|
|
@@ -49009,7 +49737,7 @@ async function registerRoutes(ctx) {
|
|
|
49009
49737
|
}
|
|
49010
49738
|
},
|
|
49011
49739
|
{
|
|
49012
|
-
create: { role:
|
|
49740
|
+
create: { role: ROLES17.MEMBER, action: "broadcast streaming events" }
|
|
49013
49741
|
},
|
|
49014
49742
|
requireAuth
|
|
49015
49743
|
);
|
|
@@ -49037,10 +49765,12 @@ async function registerRoutes(ctx) {
|
|
|
49037
49765
|
}
|
|
49038
49766
|
},
|
|
49039
49767
|
{
|
|
49040
|
-
create: { role:
|
|
49768
|
+
create: { role: ROLES17.MEMBER, action: "broadcast task streaming events" }
|
|
49041
49769
|
},
|
|
49042
49770
|
requireAuth
|
|
49043
49771
|
);
|
|
49772
|
+
app.service("/messages/streaming").publish(() => []);
|
|
49773
|
+
app.service("/tasks/streaming").publish(() => []);
|
|
49044
49774
|
registerAuthenticatedRoute2(
|
|
49045
49775
|
app,
|
|
49046
49776
|
"/sessions/:id/fork",
|
|
@@ -49059,7 +49789,7 @@ async function registerRoutes(ctx) {
|
|
|
49059
49789
|
}
|
|
49060
49790
|
},
|
|
49061
49791
|
{
|
|
49062
|
-
create: { role:
|
|
49792
|
+
create: { role: ROLES17.MEMBER, action: "fork sessions" }
|
|
49063
49793
|
},
|
|
49064
49794
|
requireAuth
|
|
49065
49795
|
);
|
|
@@ -49081,7 +49811,7 @@ async function registerRoutes(ctx) {
|
|
|
49081
49811
|
}
|
|
49082
49812
|
},
|
|
49083
49813
|
{
|
|
49084
|
-
create: { role:
|
|
49814
|
+
create: { role: ROLES17.MEMBER, action: "spawn sessions" }
|
|
49085
49815
|
},
|
|
49086
49816
|
requireAuth
|
|
49087
49817
|
);
|
|
@@ -49097,7 +49827,7 @@ async function registerRoutes(ctx) {
|
|
|
49097
49827
|
// biome-ignore lint/suspicious/noExplicitAny: FeathersJS route handler type mismatch with Express RouteParams
|
|
49098
49828
|
},
|
|
49099
49829
|
{
|
|
49100
|
-
find: { role:
|
|
49830
|
+
find: { role: ROLES17.MEMBER, action: "view session genealogy" }
|
|
49101
49831
|
},
|
|
49102
49832
|
requireAuth
|
|
49103
49833
|
);
|
|
@@ -49121,7 +49851,7 @@ async function registerRoutes(ctx) {
|
|
|
49121
49851
|
callerRole: params.user?.role,
|
|
49122
49852
|
sessionCreatedBy: session.created_by
|
|
49123
49853
|
})) {
|
|
49124
|
-
throw new
|
|
49854
|
+
throw new Forbidden14("You can only restart Claude CLI sessions you created.");
|
|
49125
49855
|
}
|
|
49126
49856
|
const tabName = `cli-${shortId16(session.session_id)}`;
|
|
49127
49857
|
const channel = `user/${targetUserId}/terminal`;
|
|
@@ -49172,7 +49902,7 @@ async function registerRoutes(ctx) {
|
|
|
49172
49902
|
// biome-ignore lint/suspicious/noExplicitAny: FeathersJS route handler type mismatch
|
|
49173
49903
|
},
|
|
49174
49904
|
{
|
|
49175
|
-
create: { role:
|
|
49905
|
+
create: { role: ROLES17.MEMBER, action: "restart claude CLI session" }
|
|
49176
49906
|
},
|
|
49177
49907
|
requireAuth
|
|
49178
49908
|
);
|
|
@@ -49518,7 +50248,7 @@ ${errorMessage}`;
|
|
|
49518
50248
|
}
|
|
49519
50249
|
},
|
|
49520
50250
|
{
|
|
49521
|
-
create: { role:
|
|
50251
|
+
create: { role: ROLES17.MEMBER, action: "execute prompts" }
|
|
49522
50252
|
},
|
|
49523
50253
|
requireAuth
|
|
49524
50254
|
);
|
|
@@ -49548,7 +50278,7 @@ ${errorMessage}`;
|
|
|
49548
50278
|
} else {
|
|
49549
50279
|
const userId = params.user?.user_id;
|
|
49550
50280
|
if (!userId) {
|
|
49551
|
-
throw new
|
|
50281
|
+
throw new Forbidden14("Authentication required to run tasks");
|
|
49552
50282
|
}
|
|
49553
50283
|
const wt = await branchRepository.findById(session.branch_id);
|
|
49554
50284
|
if (!wt) {
|
|
@@ -49566,7 +50296,7 @@ ${errorMessage}`;
|
|
|
49566
50296
|
);
|
|
49567
50297
|
const canRun = PERMISSION_RANK[effectiveLevel] >= PERMISSION_RANK.prompt || effectiveLevel === "session" && session.created_by === userId;
|
|
49568
50298
|
if (!canRun) {
|
|
49569
|
-
throw new
|
|
50299
|
+
throw new Forbidden14(
|
|
49570
50300
|
`You have '${effectiveLevel}' permission on this branch, which does not allow running tasks. Need 'prompt' or 'all' (or 'session' for own sessions).`
|
|
49571
50301
|
);
|
|
49572
50302
|
}
|
|
@@ -49609,7 +50339,7 @@ ${errorMessage}`;
|
|
|
49609
50339
|
}
|
|
49610
50340
|
},
|
|
49611
50341
|
{
|
|
49612
|
-
create: { role:
|
|
50342
|
+
create: { role: ROLES17.MEMBER, action: "execute prompts" }
|
|
49613
50343
|
},
|
|
49614
50344
|
requireAuth
|
|
49615
50345
|
);
|
|
@@ -49636,7 +50366,7 @@ ${errorMessage}`;
|
|
|
49636
50366
|
}
|
|
49637
50367
|
},
|
|
49638
50368
|
{
|
|
49639
|
-
create: { role:
|
|
50369
|
+
create: { role: ROLES17.MEMBER, action: "send spawn-subsession prompts" }
|
|
49640
50370
|
},
|
|
49641
50371
|
requireAuth
|
|
49642
50372
|
);
|
|
@@ -49685,7 +50415,7 @@ ${errorMessage}`;
|
|
|
49685
50415
|
}
|
|
49686
50416
|
},
|
|
49687
50417
|
{
|
|
49688
|
-
create: { role:
|
|
50418
|
+
create: { role: ROLES17.MEMBER, action: "fire zone triggers" }
|
|
49689
50419
|
},
|
|
49690
50420
|
requireAuth
|
|
49691
50421
|
);
|
|
@@ -49696,7 +50426,7 @@ ${errorMessage}`;
|
|
|
49696
50426
|
try {
|
|
49697
50427
|
const { sessionId } = req.params;
|
|
49698
50428
|
const params = req.feathers;
|
|
49699
|
-
ensureMinimumRole(params,
|
|
50429
|
+
ensureMinimumRole(params, ROLES17.MEMBER, "upload files");
|
|
49700
50430
|
const session = await sessionsService.get(sessionId, params);
|
|
49701
50431
|
if (!session) {
|
|
49702
50432
|
console.error(`\u274C [Upload Authz] Session not found: ${shortId16(sessionId)}`);
|
|
@@ -49940,7 +50670,7 @@ ${errorMessage}`;
|
|
|
49940
50670
|
}
|
|
49941
50671
|
},
|
|
49942
50672
|
{
|
|
49943
|
-
create: { role:
|
|
50673
|
+
create: { role: ROLES17.MEMBER, action: "stop sessions" }
|
|
49944
50674
|
},
|
|
49945
50675
|
requireAuth
|
|
49946
50676
|
);
|
|
@@ -49961,12 +50691,24 @@ ${errorMessage}`;
|
|
|
49961
50691
|
// biome-ignore lint/suspicious/noExplicitAny: Service type not compatible with Express
|
|
49962
50692
|
},
|
|
49963
50693
|
{
|
|
49964
|
-
find: { role:
|
|
50694
|
+
find: { role: ROLES17.MEMBER, action: "view queue" }
|
|
49965
50695
|
},
|
|
49966
50696
|
requireAuth
|
|
49967
50697
|
);
|
|
49968
50698
|
const queueRetryScheduled = /* @__PURE__ */ new Set();
|
|
49969
50699
|
async function processNextQueuedTask(sessionId, params) {
|
|
50700
|
+
await runWithSessionQueueTenantScope(
|
|
50701
|
+
{
|
|
50702
|
+
db,
|
|
50703
|
+
config: config2,
|
|
50704
|
+
sessionId,
|
|
50705
|
+
params,
|
|
50706
|
+
label: "processNextQueuedTask"
|
|
50707
|
+
},
|
|
50708
|
+
async (scopedParams) => processNextQueuedTaskInTenantScope(sessionId, scopedParams)
|
|
50709
|
+
);
|
|
50710
|
+
}
|
|
50711
|
+
async function processNextQueuedTaskInTenantScope(sessionId, params) {
|
|
49970
50712
|
const existingLock = sessionTurnLocks.get(sessionId);
|
|
49971
50713
|
if (existingLock) {
|
|
49972
50714
|
console.log(`\u23F3 [Queue] Session turn in progress for ${shortId16(sessionId)}, waiting...`);
|
|
@@ -49985,14 +50727,27 @@ ${errorMessage}`;
|
|
|
49985
50727
|
}
|
|
49986
50728
|
if (!queueRetryScheduled.has(sessionId)) {
|
|
49987
50729
|
queueRetryScheduled.add(sessionId);
|
|
49988
|
-
|
|
49989
|
-
|
|
49990
|
-
|
|
49991
|
-
|
|
49992
|
-
|
|
50730
|
+
deferWithSessionQueueTenantScope(
|
|
50731
|
+
{
|
|
50732
|
+
db,
|
|
50733
|
+
config: config2,
|
|
50734
|
+
sessionId,
|
|
50735
|
+
params,
|
|
50736
|
+
label: "processNextQueuedTask retry"
|
|
50737
|
+
},
|
|
50738
|
+
async (retryParams) => {
|
|
50739
|
+
queueRetryScheduled.delete(sessionId);
|
|
50740
|
+
try {
|
|
50741
|
+
await processNextQueuedTask(sessionId, retryParams);
|
|
50742
|
+
} catch (error51) {
|
|
50743
|
+
console.error(`\u274C [Queue] Retry failed for session ${shortId16(sessionId)}:`, error51);
|
|
50744
|
+
}
|
|
50745
|
+
},
|
|
50746
|
+
(error51) => {
|
|
50747
|
+
queueRetryScheduled.delete(sessionId);
|
|
49993
50748
|
console.error(`\u274C [Queue] Retry failed for session ${shortId16(sessionId)}:`, error51);
|
|
49994
50749
|
}
|
|
49995
|
-
|
|
50750
|
+
);
|
|
49996
50751
|
} else {
|
|
49997
50752
|
console.log(
|
|
49998
50753
|
`\u23ED\uFE0F [Queue] Retry already scheduled for session ${shortId16(sessionId)}, not queueing another`
|
|
@@ -50147,7 +50902,7 @@ ${errorMessage}`;
|
|
|
50147
50902
|
}
|
|
50148
50903
|
},
|
|
50149
50904
|
{
|
|
50150
|
-
create: { role:
|
|
50905
|
+
create: { role: ROLES17.MEMBER, action: "respond to permission requests" }
|
|
50151
50906
|
},
|
|
50152
50907
|
requireAuth
|
|
50153
50908
|
);
|
|
@@ -50176,7 +50931,7 @@ ${errorMessage}`;
|
|
|
50176
50931
|
}
|
|
50177
50932
|
},
|
|
50178
50933
|
{
|
|
50179
|
-
create: { role:
|
|
50934
|
+
create: { role: ROLES17.MEMBER, action: "submit widgets" }
|
|
50180
50935
|
},
|
|
50181
50936
|
requireAuth
|
|
50182
50937
|
);
|
|
@@ -50199,7 +50954,7 @@ ${errorMessage}`;
|
|
|
50199
50954
|
}
|
|
50200
50955
|
},
|
|
50201
50956
|
{
|
|
50202
|
-
create: { role:
|
|
50957
|
+
create: { role: ROLES17.MEMBER, action: "dismiss widgets" }
|
|
50203
50958
|
},
|
|
50204
50959
|
requireAuth
|
|
50205
50960
|
);
|
|
@@ -50212,7 +50967,7 @@ ${errorMessage}`;
|
|
|
50212
50967
|
}
|
|
50213
50968
|
},
|
|
50214
50969
|
{
|
|
50215
|
-
create: { role:
|
|
50970
|
+
create: { role: ROLES17.MEMBER, action: "create tasks" }
|
|
50216
50971
|
},
|
|
50217
50972
|
requireAuth
|
|
50218
50973
|
);
|
|
@@ -50227,7 +50982,7 @@ ${errorMessage}`;
|
|
|
50227
50982
|
}
|
|
50228
50983
|
},
|
|
50229
50984
|
{
|
|
50230
|
-
create: { role:
|
|
50985
|
+
create: { role: ROLES17.MEMBER, action: "complete tasks" }
|
|
50231
50986
|
},
|
|
50232
50987
|
requireAuth
|
|
50233
50988
|
);
|
|
@@ -50242,7 +50997,7 @@ ${errorMessage}`;
|
|
|
50242
50997
|
}
|
|
50243
50998
|
},
|
|
50244
50999
|
{
|
|
50245
|
-
create: { role:
|
|
51000
|
+
create: { role: ROLES17.MEMBER, action: "fail tasks" }
|
|
50246
51001
|
},
|
|
50247
51002
|
requireAuth
|
|
50248
51003
|
);
|
|
@@ -50255,7 +51010,7 @@ ${errorMessage}`;
|
|
|
50255
51010
|
}
|
|
50256
51011
|
},
|
|
50257
51012
|
{
|
|
50258
|
-
create: { role:
|
|
51013
|
+
create: { role: ROLES17.MEMBER, action: "add local repositories" }
|
|
50259
51014
|
},
|
|
50260
51015
|
requireAuth
|
|
50261
51016
|
);
|
|
@@ -50268,7 +51023,7 @@ ${errorMessage}`;
|
|
|
50268
51023
|
}
|
|
50269
51024
|
},
|
|
50270
51025
|
{
|
|
50271
|
-
create: { role:
|
|
51026
|
+
create: { role: ROLES17.MEMBER, action: "clone repositories" }
|
|
50272
51027
|
},
|
|
50273
51028
|
requireAuth
|
|
50274
51029
|
);
|
|
@@ -50287,7 +51042,7 @@ ${errorMessage}`;
|
|
|
50287
51042
|
}
|
|
50288
51043
|
},
|
|
50289
51044
|
{
|
|
50290
|
-
create: { role:
|
|
51045
|
+
create: { role: ROLES17.MEMBER, action: "create branches" }
|
|
50291
51046
|
},
|
|
50292
51047
|
requireAuth
|
|
50293
51048
|
);
|
|
@@ -50304,7 +51059,7 @@ ${errorMessage}`;
|
|
|
50304
51059
|
}
|
|
50305
51060
|
},
|
|
50306
51061
|
{
|
|
50307
|
-
remove: { role:
|
|
51062
|
+
remove: { role: ROLES17.MEMBER, action: "remove branches" }
|
|
50308
51063
|
},
|
|
50309
51064
|
requireAuth
|
|
50310
51065
|
);
|
|
@@ -50320,7 +51075,7 @@ ${errorMessage}`;
|
|
|
50320
51075
|
}
|
|
50321
51076
|
},
|
|
50322
51077
|
{
|
|
50323
|
-
create: { role:
|
|
51078
|
+
create: { role: ROLES17.ADMIN, action: "import environment config from .agor.yml" }
|
|
50324
51079
|
},
|
|
50325
51080
|
requireAuth
|
|
50326
51081
|
);
|
|
@@ -50339,7 +51094,7 @@ ${errorMessage}`;
|
|
|
50339
51094
|
// Admin-only, matching Import and repo.environment edit. Export writes a
|
|
50340
51095
|
// file to the branch working tree, so even though the content is
|
|
50341
51096
|
// derivable, the side effect warrants the same permission bar as import.
|
|
50342
|
-
create: { role:
|
|
51097
|
+
create: { role: ROLES17.ADMIN, action: "export .agor.yml" }
|
|
50343
51098
|
},
|
|
50344
51099
|
requireAuth
|
|
50345
51100
|
);
|
|
@@ -50364,10 +51119,10 @@ ${errorMessage}`;
|
|
|
50364
51119
|
}
|
|
50365
51120
|
},
|
|
50366
51121
|
{
|
|
50367
|
-
find: { role:
|
|
50368
|
-
create: { role:
|
|
50369
|
-
patch: { role:
|
|
50370
|
-
remove: { role:
|
|
51122
|
+
find: { role: ROLES17.MEMBER, action: "list API keys" },
|
|
51123
|
+
create: { role: ROLES17.MEMBER, action: "create API keys" },
|
|
51124
|
+
patch: { role: ROLES17.MEMBER, action: "update API keys" },
|
|
51125
|
+
remove: { role: ROLES17.MEMBER, action: "delete API keys" }
|
|
50371
51126
|
},
|
|
50372
51127
|
requireAuth
|
|
50373
51128
|
);
|
|
@@ -50388,7 +51143,7 @@ ${errorMessage}`;
|
|
|
50388
51143
|
}
|
|
50389
51144
|
},
|
|
50390
51145
|
{
|
|
50391
|
-
create: { role:
|
|
51146
|
+
create: { role: ROLES17.MEMBER, action: "react to board comments" }
|
|
50392
51147
|
},
|
|
50393
51148
|
requireAuth
|
|
50394
51149
|
);
|
|
@@ -50410,7 +51165,7 @@ ${errorMessage}`;
|
|
|
50410
51165
|
}
|
|
50411
51166
|
},
|
|
50412
51167
|
{
|
|
50413
|
-
create: { role:
|
|
51168
|
+
create: { role: ROLES17.MEMBER, action: "reply to board comments" }
|
|
50414
51169
|
},
|
|
50415
51170
|
requireAuth
|
|
50416
51171
|
);
|
|
@@ -50429,7 +51184,7 @@ ${errorMessage}`;
|
|
|
50429
51184
|
// Branch `all`/admin control is enforced at the service layer. This
|
|
50430
51185
|
// route-level gate is just "authenticated" so the service remains
|
|
50431
51186
|
// the single source of truth across REST, WebSocket, and MCP.
|
|
50432
|
-
create: { role:
|
|
51187
|
+
create: { role: ROLES17.VIEWER, action: "start branch environments" }
|
|
50433
51188
|
},
|
|
50434
51189
|
requireAuth
|
|
50435
51190
|
);
|
|
@@ -50445,7 +51200,7 @@ ${errorMessage}`;
|
|
|
50445
51200
|
},
|
|
50446
51201
|
{
|
|
50447
51202
|
// Branch `all`/admin control is enforced at the service layer.
|
|
50448
|
-
create: { role:
|
|
51203
|
+
create: { role: ROLES17.VIEWER, action: "stop branch environments" }
|
|
50449
51204
|
},
|
|
50450
51205
|
requireAuth
|
|
50451
51206
|
);
|
|
@@ -50464,7 +51219,7 @@ ${errorMessage}`;
|
|
|
50464
51219
|
},
|
|
50465
51220
|
{
|
|
50466
51221
|
// Branch `all`/admin control is enforced at the service layer.
|
|
50467
|
-
create: { role:
|
|
51222
|
+
create: { role: ROLES17.VIEWER, action: "restart branch environments" }
|
|
50468
51223
|
},
|
|
50469
51224
|
requireAuth
|
|
50470
51225
|
);
|
|
@@ -50480,7 +51235,7 @@ ${errorMessage}`;
|
|
|
50480
51235
|
},
|
|
50481
51236
|
{
|
|
50482
51237
|
// Branch `all`/admin control is enforced at the service layer.
|
|
50483
|
-
create: { role:
|
|
51238
|
+
create: { role: ROLES17.VIEWER, action: "nuke branch environments" }
|
|
50484
51239
|
},
|
|
50485
51240
|
requireAuth
|
|
50486
51241
|
);
|
|
@@ -50500,7 +51255,7 @@ ${errorMessage}`;
|
|
|
50500
51255
|
},
|
|
50501
51256
|
{
|
|
50502
51257
|
// Branch `all`/admin control is enforced at the service layer.
|
|
50503
|
-
create: { role:
|
|
51258
|
+
create: { role: ROLES17.VIEWER, action: "render branch environment" }
|
|
50504
51259
|
},
|
|
50505
51260
|
requireAuth
|
|
50506
51261
|
);
|
|
@@ -50516,7 +51271,7 @@ ${errorMessage}`;
|
|
|
50516
51271
|
// biome-ignore lint/suspicious/noExplicitAny: Service type not compatible with Express
|
|
50517
51272
|
},
|
|
50518
51273
|
{
|
|
50519
|
-
find: { role:
|
|
51274
|
+
find: { role: ROLES17.VIEWER, action: "check branch health" }
|
|
50520
51275
|
},
|
|
50521
51276
|
requireAuth
|
|
50522
51277
|
);
|
|
@@ -50536,13 +51291,13 @@ ${errorMessage}`;
|
|
|
50536
51291
|
before: {
|
|
50537
51292
|
create: [
|
|
50538
51293
|
requireAuth,
|
|
50539
|
-
requireMinimumRole(
|
|
51294
|
+
requireMinimumRole(ROLES17.MEMBER, "archive or delete branches"),
|
|
50540
51295
|
async (context) => {
|
|
50541
51296
|
const id = context.params.route?.id;
|
|
50542
51297
|
if (!id) throw new Error("Branch ID required");
|
|
50543
51298
|
const branch = await branchRepository.findById(id);
|
|
50544
51299
|
if (!branch) {
|
|
50545
|
-
throw new
|
|
51300
|
+
throw new Forbidden14(`Branch not found: ${id}`);
|
|
50546
51301
|
}
|
|
50547
51302
|
await cacheBranchAccess(context.params, branchRepository, branch);
|
|
50548
51303
|
return context;
|
|
@@ -50550,8 +51305,8 @@ ${errorMessage}`;
|
|
|
50550
51305
|
branchRbacEnabled ? ensureBranchPermission("all", "archive or delete branches", superadminOpts) : (context) => {
|
|
50551
51306
|
const isOwner = context.params.isBranchOwner;
|
|
50552
51307
|
const userRole = context.params.user?.role;
|
|
50553
|
-
if (!isOwner && !
|
|
50554
|
-
throw new
|
|
51308
|
+
if (!isOwner && !hasMinimumRole14(userRole, ROLES17.ADMIN)) {
|
|
51309
|
+
throw new Forbidden14(
|
|
50555
51310
|
"You must be the branch owner or a global admin to archive/delete branches"
|
|
50556
51311
|
);
|
|
50557
51312
|
}
|
|
@@ -50572,13 +51327,13 @@ ${errorMessage}`;
|
|
|
50572
51327
|
before: {
|
|
50573
51328
|
create: [
|
|
50574
51329
|
requireAuth,
|
|
50575
|
-
requireMinimumRole(
|
|
51330
|
+
requireMinimumRole(ROLES17.MEMBER, "unarchive branches"),
|
|
50576
51331
|
async (context) => {
|
|
50577
51332
|
const id = context.params.route?.id;
|
|
50578
51333
|
if (!id) throw new Error("Branch ID required");
|
|
50579
51334
|
const branch = await branchRepository.findById(id);
|
|
50580
51335
|
if (!branch) {
|
|
50581
|
-
throw new
|
|
51336
|
+
throw new Forbidden14(`Branch not found: ${id}`);
|
|
50582
51337
|
}
|
|
50583
51338
|
await cacheBranchAccess(context.params, branchRepository, branch);
|
|
50584
51339
|
return context;
|
|
@@ -50586,8 +51341,8 @@ ${errorMessage}`;
|
|
|
50586
51341
|
branchRbacEnabled ? ensureBranchPermission("all", "unarchive branches", superadminOpts) : (context) => {
|
|
50587
51342
|
const isOwner = context.params.isBranchOwner;
|
|
50588
51343
|
const userRole = context.params.user?.role;
|
|
50589
|
-
if (!isOwner && !
|
|
50590
|
-
throw new
|
|
51344
|
+
if (!isOwner && !hasMinimumRole14(userRole, ROLES17.ADMIN)) {
|
|
51345
|
+
throw new Forbidden14(
|
|
50591
51346
|
"You must be the branch owner or a global admin to unarchive branches"
|
|
50592
51347
|
);
|
|
50593
51348
|
}
|
|
@@ -50637,7 +51392,7 @@ ${errorMessage}`;
|
|
|
50637
51392
|
before: {
|
|
50638
51393
|
create: [
|
|
50639
51394
|
requireAuth,
|
|
50640
|
-
requireMinimumRole(
|
|
51395
|
+
requireMinimumRole(ROLES17.MEMBER, "run schedule"),
|
|
50641
51396
|
// Reuse the canonical hook so caching semantics (params.schedule
|
|
50642
51397
|
// / params.branch / params.isBranchOwner) match every other
|
|
50643
51398
|
// schedule-touching path.
|
|
@@ -50646,8 +51401,8 @@ ${errorMessage}`;
|
|
|
50646
51401
|
branchRbacEnabled ? ensureBranchPermission("all", "run schedule", superadminOpts) : (context) => {
|
|
50647
51402
|
const isOwner = context.params.isBranchOwner;
|
|
50648
51403
|
const userRole = context.params.user?.role;
|
|
50649
|
-
if (!isOwner && !
|
|
50650
|
-
throw new
|
|
51404
|
+
if (!isOwner && !hasMinimumRole14(userRole, ROLES17.ADMIN)) {
|
|
51405
|
+
throw new Forbidden14(
|
|
50651
51406
|
"You must be the branch owner or a global admin to run schedules"
|
|
50652
51407
|
);
|
|
50653
51408
|
}
|
|
@@ -50711,7 +51466,7 @@ ${errorMessage}`;
|
|
|
50711
51466
|
before: {
|
|
50712
51467
|
create: [
|
|
50713
51468
|
requireAuth,
|
|
50714
|
-
requireMinimumRole(
|
|
51469
|
+
requireMinimumRole(ROLES17.MEMBER, "execute scheduled runs"),
|
|
50715
51470
|
async (context) => {
|
|
50716
51471
|
const id = context.params.route?.id;
|
|
50717
51472
|
if (!id) throw new BadRequest9("Branch ID required");
|
|
@@ -50725,8 +51480,8 @@ ${errorMessage}`;
|
|
|
50725
51480
|
branchRbacEnabled ? ensureBranchPermission("all", "execute scheduled runs", superadminOpts) : (context) => {
|
|
50726
51481
|
const isOwner = context.params.isBranchOwner;
|
|
50727
51482
|
const userRole = context.params.user?.role;
|
|
50728
|
-
if (!isOwner && !
|
|
50729
|
-
throw new
|
|
51483
|
+
if (!isOwner && !hasMinimumRole14(userRole, ROLES17.ADMIN)) {
|
|
51484
|
+
throw new Forbidden14(
|
|
50730
51485
|
"You must be the branch owner or a global admin to execute scheduled runs"
|
|
50731
51486
|
);
|
|
50732
51487
|
}
|
|
@@ -50750,7 +51505,7 @@ ${errorMessage}`;
|
|
|
50750
51505
|
},
|
|
50751
51506
|
{
|
|
50752
51507
|
// Branch `all`/admin control is enforced at the service layer.
|
|
50753
|
-
find: { role:
|
|
51508
|
+
find: { role: ROLES17.VIEWER, action: "view branch logs" }
|
|
50754
51509
|
},
|
|
50755
51510
|
requireAuth
|
|
50756
51511
|
);
|
|
@@ -50767,7 +51522,7 @@ ${errorMessage}`;
|
|
|
50767
51522
|
}
|
|
50768
51523
|
},
|
|
50769
51524
|
{
|
|
50770
|
-
create: { role:
|
|
51525
|
+
create: { role: ROLES17.MEMBER, action: "modify board sessions" }
|
|
50771
51526
|
},
|
|
50772
51527
|
requireAuth
|
|
50773
51528
|
);
|
|
@@ -50797,7 +51552,15 @@ ${errorMessage}`;
|
|
|
50797
51552
|
const includeGlobal = params.query?.includeGlobal === "true" || params.query?.includeGlobal === true;
|
|
50798
51553
|
const includeMetadata = params.query?.includeMetadata === "true" || params.query?.includeMetadata === true;
|
|
50799
51554
|
const mcpService = app.service("mcp-servers");
|
|
50800
|
-
const
|
|
51555
|
+
const queryForUserId = typeof params.query?.forUserId === "string" ? params.query.forUserId : void 0;
|
|
51556
|
+
const authPayloadType = params.authentication?.payload?.type;
|
|
51557
|
+
const routeUser = params.user;
|
|
51558
|
+
const userId = resolveForUserIdWithGate({
|
|
51559
|
+
queryForUserId,
|
|
51560
|
+
isServiceAccount: routeUser?._isServiceAccount,
|
|
51561
|
+
authPayloadType,
|
|
51562
|
+
callerUserId: params.user?.user_id
|
|
51563
|
+
});
|
|
50801
51564
|
const rawLookupParams = {
|
|
50802
51565
|
...params,
|
|
50803
51566
|
provider: void 0,
|
|
@@ -50932,10 +51695,10 @@ ${errorMessage}`;
|
|
|
50932
51695
|
// biome-ignore lint/suspicious/noExplicitAny: Service type not compatible with Express
|
|
50933
51696
|
},
|
|
50934
51697
|
{
|
|
50935
|
-
find: { role:
|
|
50936
|
-
create: { role:
|
|
50937
|
-
remove: { role:
|
|
50938
|
-
patch: { role:
|
|
51698
|
+
find: { role: ROLES17.MEMBER, action: "view session MCP servers" },
|
|
51699
|
+
create: { role: ROLES17.MEMBER, action: "modify session MCP servers" },
|
|
51700
|
+
remove: { role: ROLES17.MEMBER, action: "modify session MCP servers" },
|
|
51701
|
+
patch: { role: ROLES17.MEMBER, action: "modify session MCP servers" }
|
|
50939
51702
|
},
|
|
50940
51703
|
requireAuth
|
|
50941
51704
|
);
|
|
@@ -51024,10 +51787,10 @@ ${errorMessage}`;
|
|
|
51024
51787
|
// biome-ignore lint/suspicious/noExplicitAny: Service type not compatible with Express
|
|
51025
51788
|
},
|
|
51026
51789
|
{
|
|
51027
|
-
find: { role:
|
|
51028
|
-
create: { role:
|
|
51029
|
-
remove: { role:
|
|
51030
|
-
patch: { role:
|
|
51790
|
+
find: { role: ROLES17.MEMBER, action: "view session env selections" },
|
|
51791
|
+
create: { role: ROLES17.MEMBER, action: "modify session env selections" },
|
|
51792
|
+
remove: { role: ROLES17.MEMBER, action: "modify session env selections" },
|
|
51793
|
+
patch: { role: ROLES17.MEMBER, action: "modify session env selections" }
|
|
51031
51794
|
},
|
|
51032
51795
|
requireAuth
|
|
51033
51796
|
);
|
|
@@ -51308,7 +52071,9 @@ import {
|
|
|
51308
52071
|
BoardRepository as BoardRepository6,
|
|
51309
52072
|
BranchRepository as BranchRepository16,
|
|
51310
52073
|
eq as eq6,
|
|
52074
|
+
getCurrentTenantId as getCurrentTenantId7,
|
|
51311
52075
|
inArray as inArray2,
|
|
52076
|
+
isPostgresDatabase as isPostgresDatabase8,
|
|
51312
52077
|
MCPServerRepository as MCPServerRepository5,
|
|
51313
52078
|
SessionMCPServerRepository as SessionMCPServerRepository5,
|
|
51314
52079
|
select as select6,
|
|
@@ -51317,12 +52082,12 @@ import {
|
|
|
51317
52082
|
UserMCPOAuthTokenRepository as UserMCPOAuthTokenRepository4,
|
|
51318
52083
|
visibleSessionReferenceAccessExists as visibleSessionReferenceAccessExists2
|
|
51319
52084
|
} from "@agor/core/db";
|
|
51320
|
-
import { Forbidden as
|
|
52085
|
+
import { Forbidden as Forbidden26, NotAuthenticated as NotAuthenticated21 } from "@agor/core/feathers";
|
|
51321
52086
|
import {
|
|
51322
52087
|
AGENTIC_TOOL_CAPABILITIES as AGENTIC_TOOL_CAPABILITIES3,
|
|
51323
52088
|
isSessionExecuting as isSessionExecuting2,
|
|
51324
52089
|
isTaskExecuting as isTaskExecuting2,
|
|
51325
|
-
ROLES as
|
|
52090
|
+
ROLES as ROLES25,
|
|
51326
52091
|
SessionStatus as SessionStatus9,
|
|
51327
52092
|
TaskStatus as TaskStatus7
|
|
51328
52093
|
} from "@agor/core/types";
|
|
@@ -51441,10 +52206,10 @@ import {
|
|
|
51441
52206
|
import {
|
|
51442
52207
|
ARTIFACT_SCOPED_ONLY_GRANT_KEYS,
|
|
51443
52208
|
GRANT_ENV_VAR_NAMES,
|
|
51444
|
-
hasMinimumRole as
|
|
52209
|
+
hasMinimumRole as hasMinimumRole15,
|
|
51445
52210
|
NO_CONSENT_GRANT_KEYS,
|
|
51446
52211
|
proxyGrantEnvName,
|
|
51447
|
-
ROLES as
|
|
52212
|
+
ROLES as ROLES18
|
|
51448
52213
|
} from "@agor/core/types";
|
|
51449
52214
|
|
|
51450
52215
|
// src/utils/agor-runtime-source.ts
|
|
@@ -52197,7 +52962,7 @@ var ArtifactsService = class extends DrizzleService {
|
|
|
52197
52962
|
const existing = await this.artifactRepo.findById(artifactId);
|
|
52198
52963
|
if (!existing) throw new Error(`Artifact ${artifactId} not found`);
|
|
52199
52964
|
const isOwner = !!userId && existing.created_by === userId;
|
|
52200
|
-
const isAdmin4 = !!userRole &&
|
|
52965
|
+
const isAdmin4 = !!userRole && hasMinimumRole15(userRole, ROLES18.ADMIN);
|
|
52201
52966
|
if (userId && !isOwner && !isAdmin4) {
|
|
52202
52967
|
throw new Error("Forbidden: only the artifact's creator or an admin may update it");
|
|
52203
52968
|
}
|
|
@@ -53263,7 +54028,7 @@ var ArtifactsService = class extends DrizzleService {
|
|
|
53263
54028
|
const artifact = await this.artifactRepo.findById(artifactId);
|
|
53264
54029
|
if (!artifact) throw new Error(`Artifact ${artifactId} not found`);
|
|
53265
54030
|
const isOwner = !!userId && artifact.created_by === userId;
|
|
53266
|
-
const isAdmin4 = !!userRole &&
|
|
54031
|
+
const isAdmin4 = !!userRole && hasMinimumRole15(userRole, ROLES18.ADMIN);
|
|
53267
54032
|
if (!isOwner && !isAdmin4) {
|
|
53268
54033
|
throw new Error("Forbidden: only the artifact's creator or an admin may delete it");
|
|
53269
54034
|
}
|
|
@@ -53874,20 +54639,20 @@ function createBoardObjectsService(db) {
|
|
|
53874
54639
|
}
|
|
53875
54640
|
|
|
53876
54641
|
// src/services/board-owners.ts
|
|
53877
|
-
import { Forbidden as
|
|
53878
|
-
import { hasMinimumRole as
|
|
54642
|
+
import { Forbidden as Forbidden15, NotAuthenticated as NotAuthenticated16 } from "@agor/core/feathers";
|
|
54643
|
+
import { hasMinimumRole as hasMinimumRole16, ROLES as ROLES19 } from "@agor/core/types";
|
|
53879
54644
|
function requireBoardAccess(boardRepo, mode) {
|
|
53880
54645
|
return async (context) => {
|
|
53881
54646
|
if (!context.params.provider) return context;
|
|
53882
54647
|
if (context.params.user?._isServiceAccount) return context;
|
|
53883
54648
|
const user = context.params.user;
|
|
53884
54649
|
if (!user?.user_id) throw new NotAuthenticated16("Authentication required");
|
|
53885
|
-
if (
|
|
54650
|
+
if (hasMinimumRole16(user.role, ROLES19.ADMIN)) return context;
|
|
53886
54651
|
const boardId = context.params.route?.id;
|
|
53887
54652
|
if (!boardId) throw new Error("Board ID is required");
|
|
53888
54653
|
const allowed = mode === "view" ? await boardRepo.canView(boardId, user.user_id) : await boardRepo.canMutate(boardId, user.user_id);
|
|
53889
54654
|
if (allowed) return context;
|
|
53890
|
-
throw new
|
|
54655
|
+
throw new Forbidden15(
|
|
53891
54656
|
mode === "view" ? "You need board access to view board owners" : "You need board owner or board group 'all' access to manage board owners"
|
|
53892
54657
|
);
|
|
53893
54658
|
};
|
|
@@ -54121,6 +54886,13 @@ var BoardsService = class extends DrizzleService {
|
|
|
54121
54886
|
async batchUpsertBoardObjects(boardId, objects, _params) {
|
|
54122
54887
|
return this.boardRepo.batchUpsertBoardObjects(boardId, objects);
|
|
54123
54888
|
}
|
|
54889
|
+
/**
|
|
54890
|
+
* Custom method: Atomically shallow-merge field patches into existing board
|
|
54891
|
+
* objects (used by z-order reorder to persist only the changed zIndex).
|
|
54892
|
+
*/
|
|
54893
|
+
async mergeBoardObjectFields(boardId, patches, _params) {
|
|
54894
|
+
return this.boardRepo.mergeBoardObjectFields(boardId, patches);
|
|
54895
|
+
}
|
|
54124
54896
|
/**
|
|
54125
54897
|
* Custom method: Delete a zone and handle associated sessions
|
|
54126
54898
|
*/
|
|
@@ -54273,7 +55045,7 @@ function createBoardsService(db, emitBoardObjectPatched) {
|
|
|
54273
55045
|
init_branch_authorization();
|
|
54274
55046
|
init_spawn_executor();
|
|
54275
55047
|
import { shortId as shortId18 } from "@agor/core/db";
|
|
54276
|
-
import { Forbidden as
|
|
55048
|
+
import { Forbidden as Forbidden16, NotAuthenticated as NotAuthenticated17 } from "@agor/core/feathers";
|
|
54277
55049
|
function requireViewPermission(branchRepo, allowSuperadmin = true) {
|
|
54278
55050
|
return async (context) => {
|
|
54279
55051
|
if (!context.params.provider) {
|
|
@@ -54296,12 +55068,12 @@ function requireViewPermission(branchRepo, allowSuperadmin = true) {
|
|
|
54296
55068
|
}
|
|
54297
55069
|
const branch = await branchRepo.findById(branchId);
|
|
54298
55070
|
if (!branch) {
|
|
54299
|
-
throw new
|
|
55071
|
+
throw new Forbidden16(`Branch not found: ${branchId}`);
|
|
54300
55072
|
}
|
|
54301
55073
|
const isOwner = await branchRepo.isOwner(branch.branch_id, userId);
|
|
54302
55074
|
const effectivePermission = isOwner ? "all" : await branchRepo.resolveUserPermission(branch, userId);
|
|
54303
55075
|
if (PERMISSION_RANK[effectivePermission] < PERMISSION_RANK.view) {
|
|
54304
|
-
throw new
|
|
55076
|
+
throw new Forbidden16("You do not have permission to view this branch");
|
|
54305
55077
|
}
|
|
54306
55078
|
return context;
|
|
54307
55079
|
};
|
|
@@ -54328,7 +55100,7 @@ function requireBranchOwner(branchRepo, allowSuperadmin = true) {
|
|
|
54328
55100
|
}
|
|
54329
55101
|
const isOwner = await branchRepo.isOwner(branchId, userId);
|
|
54330
55102
|
if (!isOwner) {
|
|
54331
|
-
throw new
|
|
55103
|
+
throw new Forbidden16("Only branch owners can manage owners");
|
|
54332
55104
|
}
|
|
54333
55105
|
return context;
|
|
54334
55106
|
};
|
|
@@ -54484,7 +55256,7 @@ import {
|
|
|
54484
55256
|
validateManagedEnvLifecyclePolicy as validateManagedEnvLifecyclePolicy2,
|
|
54485
55257
|
validateRenderedManagedEnvUrlFields as validateRenderedManagedEnvUrlFields2
|
|
54486
55258
|
} from "@agor/core/environment/webhook";
|
|
54487
|
-
import { BadRequest as BadRequest10, Forbidden as
|
|
55259
|
+
import { BadRequest as BadRequest10, Forbidden as Forbidden17, NotAuthenticated as NotAuthenticated18 } from "@agor/core/feathers";
|
|
54488
55260
|
import { stripGitUrlCredentials } from "@agor/core/git/pure";
|
|
54489
55261
|
import {
|
|
54490
55262
|
BRANCH_ENVIRONMENT_CLEARABLE_FIELDS,
|
|
@@ -54507,6 +55279,27 @@ function shouldUseCloneReferencePath() {
|
|
|
54507
55279
|
return (config2.execution?.unix_user_mode ?? "simple") !== "strict";
|
|
54508
55280
|
}
|
|
54509
55281
|
|
|
55282
|
+
// src/utils/emit-service-event.ts
|
|
55283
|
+
var DEFAULT_EVENT_METHOD = {
|
|
55284
|
+
created: "create",
|
|
55285
|
+
updated: "update",
|
|
55286
|
+
patched: "patch",
|
|
55287
|
+
removed: "remove"
|
|
55288
|
+
};
|
|
55289
|
+
function emitServiceEvent(app, event) {
|
|
55290
|
+
const service = app.service(event.path);
|
|
55291
|
+
service.emit?.(event.event, event.data, {
|
|
55292
|
+
app,
|
|
55293
|
+
service,
|
|
55294
|
+
path: event.path,
|
|
55295
|
+
method: event.method ?? DEFAULT_EVENT_METHOD[event.event] ?? "patch",
|
|
55296
|
+
event: event.event,
|
|
55297
|
+
id: event.id,
|
|
55298
|
+
result: event.data,
|
|
55299
|
+
params: event.params ?? {}
|
|
55300
|
+
});
|
|
55301
|
+
}
|
|
55302
|
+
|
|
54510
55303
|
// src/utils/git-impersonation.ts
|
|
54511
55304
|
async function resolveDaemonUserForGroupRefresh() {
|
|
54512
55305
|
const { getDaemonUser: getDaemonUser2, isUnixGroupRefreshNeeded } = await import("@agor/core/config");
|
|
@@ -54928,7 +55721,7 @@ var BranchesService = class extends DrizzleService {
|
|
|
54928
55721
|
}
|
|
54929
55722
|
const isService = !!caller._isServiceAccount;
|
|
54930
55723
|
if (!isService) {
|
|
54931
|
-
throw new
|
|
55724
|
+
throw new Forbidden17("Only the executor service account may initialize Unix groups");
|
|
54932
55725
|
}
|
|
54933
55726
|
}
|
|
54934
55727
|
const { initializeBranchUnixGroup: initializeBranchUnixGroup2 } = await Promise.resolve().then(() => (init_unix_group_init(), unix_group_init_exports));
|
|
@@ -55013,7 +55806,7 @@ var BranchesService = class extends DrizzleService {
|
|
|
55013
55806
|
if (await this.branchRepo.isOwner(branch.branch_id, userId)) {
|
|
55014
55807
|
return;
|
|
55015
55808
|
}
|
|
55016
|
-
throw new
|
|
55809
|
+
throw new Forbidden17("Only branch owners or admins can manage assistant knowledge");
|
|
55017
55810
|
}
|
|
55018
55811
|
containsAssistantKnowledgeConfigMutation(data) {
|
|
55019
55812
|
if (!Object.hasOwn(data, "custom_context")) return false;
|
|
@@ -55074,7 +55867,7 @@ var BranchesService = class extends DrizzleService {
|
|
|
55074
55867
|
if (!userId) throw new NotAuthenticated18("Authentication required");
|
|
55075
55868
|
const permission = await namespaces.resolveNamespacePermission(namespace.namespace_id, userId);
|
|
55076
55869
|
if (permission !== "write" && permission !== "own") {
|
|
55077
|
-
throw new
|
|
55870
|
+
throw new Forbidden17(
|
|
55078
55871
|
"You need write access to use this Knowledge namespace as assistant home"
|
|
55079
55872
|
);
|
|
55080
55873
|
}
|
|
@@ -55751,7 +56544,13 @@ var BranchesService = class extends DrizzleService {
|
|
|
55751
56544
|
},
|
|
55752
56545
|
resolvedParams
|
|
55753
56546
|
);
|
|
55754
|
-
this.app
|
|
56547
|
+
emitServiceEvent(this.app, {
|
|
56548
|
+
path: "branches",
|
|
56549
|
+
event: "patched",
|
|
56550
|
+
data: branch,
|
|
56551
|
+
params: resolvedParams,
|
|
56552
|
+
id
|
|
56553
|
+
});
|
|
55755
56554
|
return branch;
|
|
55756
56555
|
}
|
|
55757
56556
|
/**
|
|
@@ -56186,7 +56985,9 @@ var BranchesService = class extends DrizzleService {
|
|
|
56186
56985
|
path: branch.path,
|
|
56187
56986
|
custom_context: branch.custom_context,
|
|
56188
56987
|
unix_gid: unixGid,
|
|
56189
|
-
host_ip_address: hostIpAddress
|
|
56988
|
+
host_ip_address: hostIpAddress,
|
|
56989
|
+
base_ref: branch.base_ref,
|
|
56990
|
+
ref_type: branch.ref_type
|
|
56190
56991
|
},
|
|
56191
56992
|
requestedVariant
|
|
56192
56993
|
);
|
|
@@ -56643,6 +57444,9 @@ function isAlias(id) {
|
|
|
56643
57444
|
}
|
|
56644
57445
|
var CONTEXT_1M_BETA = "context-1m-2025-08-07";
|
|
56645
57446
|
var ONE_MILLION_TOKENS = 9e5;
|
|
57447
|
+
function hasNativeMillionContext(id) {
|
|
57448
|
+
return id.startsWith("claude-fable");
|
|
57449
|
+
}
|
|
56646
57450
|
function toModelOptions(models) {
|
|
56647
57451
|
const aliases = models.filter((m) => isAlias(m.id));
|
|
56648
57452
|
const options = [];
|
|
@@ -56653,7 +57457,7 @@ function toModelOptions(models) {
|
|
|
56653
57457
|
description: void 0,
|
|
56654
57458
|
source: "dynamic"
|
|
56655
57459
|
});
|
|
56656
|
-
if (m.max_input_tokens && m.max_input_tokens >= ONE_MILLION_TOKENS) {
|
|
57460
|
+
if (m.max_input_tokens && m.max_input_tokens >= ONE_MILLION_TOKENS && !hasNativeMillionContext(m.id)) {
|
|
56657
57461
|
options.push({
|
|
56658
57462
|
id: `${m.id}[1m]`,
|
|
56659
57463
|
displayName: `${m.display_name} (1M context)`,
|
|
@@ -56737,11 +57541,11 @@ import {
|
|
|
56737
57541
|
resolveApiKey as resolveApiKey3,
|
|
56738
57542
|
saveConfig
|
|
56739
57543
|
} from "@agor/core/config";
|
|
56740
|
-
import { BadRequest as BadRequest11, Forbidden as
|
|
57544
|
+
import { BadRequest as BadRequest11, Forbidden as Forbidden18, NotAuthenticated as NotAuthenticated19 } from "@agor/core/feathers";
|
|
56741
57545
|
import {
|
|
56742
57546
|
TOOL_API_KEY_NAMES as TOOL_API_KEY_NAMES2
|
|
56743
57547
|
} from "@agor/core/types";
|
|
56744
|
-
import
|
|
57548
|
+
import jwt10 from "jsonwebtoken";
|
|
56745
57549
|
var RESOLVABLE_API_KEY_NAMES = {
|
|
56746
57550
|
ANTHROPIC_API_KEY: true,
|
|
56747
57551
|
ANTHROPIC_AUTH_TOKEN: true,
|
|
@@ -56762,7 +57566,7 @@ function getExecutorTokenPayload(params) {
|
|
|
56762
57566
|
}
|
|
56763
57567
|
const accessToken = params?.authentication?.accessToken;
|
|
56764
57568
|
if (typeof accessToken === "string") {
|
|
56765
|
-
const decoded =
|
|
57569
|
+
const decoded = jwt10.decode(accessToken);
|
|
56766
57570
|
if (decoded?.type === "executor-session" && decoded.purpose === "executor-task") {
|
|
56767
57571
|
return decoded;
|
|
56768
57572
|
}
|
|
@@ -56863,10 +57667,10 @@ var ConfigService = class {
|
|
|
56863
57667
|
if (!caller) {
|
|
56864
57668
|
throw new NotAuthenticated19("Authentication required");
|
|
56865
57669
|
}
|
|
56866
|
-
throw new
|
|
57670
|
+
throw new Forbidden18("Only executor runtime credentials may resolve API keys");
|
|
56867
57671
|
}
|
|
56868
57672
|
if (executorPayload?.task_id && executorPayload.task_id !== taskId) {
|
|
56869
|
-
throw new
|
|
57673
|
+
throw new Forbidden18("Executor token task scope does not match this request");
|
|
56870
57674
|
}
|
|
56871
57675
|
}
|
|
56872
57676
|
let userId;
|
|
@@ -56881,31 +57685,31 @@ var ConfigService = class {
|
|
|
56881
57685
|
} catch (err) {
|
|
56882
57686
|
console.warn(`[Config.resolveApiKey] Failed to fetch task ${taskId}:`, err);
|
|
56883
57687
|
if (executorPayload) {
|
|
56884
|
-
throw new
|
|
57688
|
+
throw new Forbidden18("Executor token task scope could not be verified");
|
|
56885
57689
|
}
|
|
56886
57690
|
}
|
|
56887
57691
|
if (executorPayload && (!userId || !sessionId)) {
|
|
56888
|
-
throw new
|
|
57692
|
+
throw new Forbidden18("Executor token task scope could not be verified");
|
|
56889
57693
|
}
|
|
56890
57694
|
if (executorPayload) {
|
|
56891
57695
|
const verifiedSessionId = sessionId;
|
|
56892
57696
|
if (!verifiedSessionId) {
|
|
56893
|
-
throw new
|
|
57697
|
+
throw new Forbidden18("Executor token task scope could not be verified");
|
|
56894
57698
|
}
|
|
56895
57699
|
if (!tool) {
|
|
56896
57700
|
throw new BadRequest11("Tool is required for executor API key resolution");
|
|
56897
57701
|
}
|
|
56898
57702
|
const expectedKeyName = TOOL_API_KEY_NAMES2[tool];
|
|
56899
57703
|
if (!expectedKeyName || expectedKeyName !== keyName) {
|
|
56900
|
-
throw new
|
|
57704
|
+
throw new Forbidden18("Executor token is not valid for this API key");
|
|
56901
57705
|
}
|
|
56902
57706
|
const sessionsService = this.app?.service("sessions");
|
|
56903
57707
|
if (!sessionsService) {
|
|
56904
|
-
throw new
|
|
57708
|
+
throw new Forbidden18("Executor token tool scope could not be verified");
|
|
56905
57709
|
}
|
|
56906
57710
|
const session = await sessionsService.get(verifiedSessionId, { provider: void 0 });
|
|
56907
57711
|
if (session?.agentic_tool !== tool) {
|
|
56908
|
-
throw new
|
|
57712
|
+
throw new Forbidden18("Executor token tool scope does not match this session");
|
|
56909
57713
|
}
|
|
56910
57714
|
}
|
|
56911
57715
|
const result = await resolveApiKey3(keyName, {
|
|
@@ -56990,7 +57794,7 @@ function createConfigService(db) {
|
|
|
56990
57794
|
// src/services/context.ts
|
|
56991
57795
|
import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
56992
57796
|
import { isAbsolute as isAbsolute4, join as join6, relative as relative3, resolve as resolve5 } from "path";
|
|
56993
|
-
import { ROLES as
|
|
57797
|
+
import { ROLES as ROLES20 } from "@agor/core/types";
|
|
56994
57798
|
var ContextService = class {
|
|
56995
57799
|
branchRepo;
|
|
56996
57800
|
constructor(branchRepo) {
|
|
@@ -57006,7 +57810,7 @@ var ContextService = class {
|
|
|
57006
57810
|
console.warn(
|
|
57007
57811
|
"[Context Service] DEPRECATED: This service is deprecated. Use /file endpoint instead for browsing all files in a branch."
|
|
57008
57812
|
);
|
|
57009
|
-
ensureMinimumRole(params,
|
|
57813
|
+
ensureMinimumRole(params, ROLES20.MEMBER, "list context files");
|
|
57010
57814
|
const branchId = params?.query?.branch_id;
|
|
57011
57815
|
if (!branchId) {
|
|
57012
57816
|
throw new Error("branch_id query parameter is required");
|
|
@@ -57032,7 +57836,7 @@ var ContextService = class {
|
|
|
57032
57836
|
* @param id - Relative path from branch root (e.g., "context/concepts/core.md", "CLAUDE.md")
|
|
57033
57837
|
*/
|
|
57034
57838
|
async get(id, params) {
|
|
57035
|
-
ensureMinimumRole(params,
|
|
57839
|
+
ensureMinimumRole(params, ROLES20.MEMBER, "read context file");
|
|
57036
57840
|
const branchId = params?.query?.branch_id;
|
|
57037
57841
|
if (!branchId) {
|
|
57038
57842
|
throw new Error("branch_id query parameter is required");
|
|
@@ -57294,7 +58098,7 @@ function createCursorModelsService(db) {
|
|
|
57294
58098
|
// src/services/file.ts
|
|
57295
58099
|
import { lstat as lstat2, readdir as readdir4, readFile as readFile4, realpath as realpath5 } from "fs/promises";
|
|
57296
58100
|
import { extname, isAbsolute as isAbsolute5, join as join7, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
|
|
57297
|
-
import { ROLES as
|
|
58101
|
+
import { ROLES as ROLES21 } from "@agor/core/types";
|
|
57298
58102
|
var MAX_FILES = 5e4;
|
|
57299
58103
|
var MAX_PREVIEW_SIZE = 1024 * 1024;
|
|
57300
58104
|
var MAX_TITLE_READ_BYTES = 4096;
|
|
@@ -57388,7 +58192,7 @@ var FileService = class {
|
|
|
57388
58192
|
* Returns lightweight list items without content
|
|
57389
58193
|
*/
|
|
57390
58194
|
async find(params) {
|
|
57391
|
-
ensureMinimumRole(params,
|
|
58195
|
+
ensureMinimumRole(params, ROLES21.MEMBER, "list files");
|
|
57392
58196
|
const branchId = params?.query?.branch_id;
|
|
57393
58197
|
if (!branchId) {
|
|
57394
58198
|
throw new Error("branch_id query parameter is required");
|
|
@@ -57421,7 +58225,7 @@ var FileService = class {
|
|
|
57421
58225
|
* @param id - Relative path from branch root (e.g., "src/index.ts", "README.md")
|
|
57422
58226
|
*/
|
|
57423
58227
|
async get(id, params) {
|
|
57424
|
-
ensureMinimumRole(params,
|
|
58228
|
+
ensureMinimumRole(params, ROLES21.MEMBER, "read file");
|
|
57425
58229
|
const branchId = params?.query?.branch_id;
|
|
57426
58230
|
if (!branchId) {
|
|
57427
58231
|
throw new Error("branch_id query parameter is required");
|
|
@@ -57682,7 +58486,8 @@ import {
|
|
|
57682
58486
|
getHiddenTenantId,
|
|
57683
58487
|
MCPServerRepository as MCPServerRepository3,
|
|
57684
58488
|
runWithoutTenantDatabaseScope as runWithoutTenantDatabaseScope3,
|
|
57685
|
-
runWithTenantDatabaseScope as
|
|
58489
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope7,
|
|
58490
|
+
SessionRepository as SessionRepository9,
|
|
57686
58491
|
shortId as shortId23,
|
|
57687
58492
|
ThreadSessionMapRepository as ThreadSessionMapRepository2,
|
|
57688
58493
|
UserMCPOAuthTokenRepository as UserMCPOAuthTokenRepository3,
|
|
@@ -57699,7 +58504,7 @@ import {
|
|
|
57699
58504
|
parseGitHubThreadId
|
|
57700
58505
|
} from "@agor/core/gateway";
|
|
57701
58506
|
import { resolveSessionDefaults as resolveSessionDefaults5 } from "@agor/core/sessions";
|
|
57702
|
-
import { hasMinimumRole as
|
|
58507
|
+
import { hasMinimumRole as hasMinimumRole17, ROLES as ROLES22, SessionStatus as SessionStatus7 } from "@agor/core/types";
|
|
57703
58508
|
import { getSessionUrl } from "@agor/core/utils/url";
|
|
57704
58509
|
function hasListeningConfig(channel) {
|
|
57705
58510
|
const config2 = channel.config;
|
|
@@ -57777,6 +58582,13 @@ function oneLineForPrompt(text, maxChars = 900) {
|
|
|
57777
58582
|
if (normalized.length <= maxChars) return normalized;
|
|
57778
58583
|
return `${normalized.slice(0, Math.max(0, maxChars - 1))}\u2026`;
|
|
57779
58584
|
}
|
|
58585
|
+
var SLACK_GATEWAY_REPLY_NOTE = "Note: Any assistant message you send in this current Agor session is streamed back directly to the Slack conversation. Only use outbound gateway tools when you intentionally need to start a separate thread, DM, or message.";
|
|
58586
|
+
function prependSlackGatewayReplyNote(prompt) {
|
|
58587
|
+
if (prompt.includes(SLACK_GATEWAY_REPLY_NOTE)) return prompt;
|
|
58588
|
+
return `${SLACK_GATEWAY_REPLY_NOTE}
|
|
58589
|
+
|
|
58590
|
+
${prompt}`;
|
|
58591
|
+
}
|
|
57780
58592
|
function formatSlackCatchUpPrompt(args) {
|
|
57781
58593
|
const slackChannelName = typeof args.metadata?.slack_channel_name === "string" ? args.metadata.slack_channel_name : null;
|
|
57782
58594
|
const slackChannelId = typeof args.metadata?.channel === "string" ? args.metadata.channel : args.threadId.split("-")[0];
|
|
@@ -57963,6 +58775,7 @@ var GatewayService = class _GatewayService {
|
|
|
57963
58775
|
threadMapRepo;
|
|
57964
58776
|
outboundRepo;
|
|
57965
58777
|
branchRepo;
|
|
58778
|
+
sessionRepo;
|
|
57966
58779
|
usersRepo;
|
|
57967
58780
|
mcpServerRepo;
|
|
57968
58781
|
userTokenRepo;
|
|
@@ -58005,6 +58818,7 @@ var GatewayService = class _GatewayService {
|
|
|
58005
58818
|
this.threadMapRepo = new ThreadSessionMapRepository2(db);
|
|
58006
58819
|
this.outboundRepo = new GatewayOutboundMessageRepository(db);
|
|
58007
58820
|
this.branchRepo = new BranchRepository13(db);
|
|
58821
|
+
this.sessionRepo = new SessionRepository9(db);
|
|
58008
58822
|
this.usersRepo = new UsersRepository9(db);
|
|
58009
58823
|
this.mcpServerRepo = new MCPServerRepository3(db);
|
|
58010
58824
|
this.userTokenRepo = new UserMCPOAuthTokenRepository3(db);
|
|
@@ -58500,8 +59314,26 @@ ${chunk}` : chunk;
|
|
|
58500
59314
|
console.warn("[gateway] Failed to mirror message stream to Slack:", error51);
|
|
58501
59315
|
}
|
|
58502
59316
|
}
|
|
59317
|
+
/**
|
|
59318
|
+
* Session-context emits are hard-bound to the session's branch, with no
|
|
59319
|
+
* admin-role bypass — agent sessions run as admin users, so a role bypass
|
|
59320
|
+
* would let any session impersonate another assistant's channel. The error
|
|
59321
|
+
* deliberately never echoes the channel's target branch, so a denied emit
|
|
59322
|
+
* cannot be used to enumerate which branch a foreign channel serves.
|
|
59323
|
+
*/
|
|
59324
|
+
async ensureSessionBranchBoundToChannel(channel, sessionId) {
|
|
59325
|
+
const session = await this.sessionRepo.findById(sessionId);
|
|
59326
|
+
if (!session) {
|
|
59327
|
+
throw new Error("Gateway outbound denied: emitting session not found");
|
|
59328
|
+
}
|
|
59329
|
+
if (session.branch_id !== channel.target_branch_id) {
|
|
59330
|
+
throw new Error(
|
|
59331
|
+
`Gateway outbound denied: session ${shortId23(sessionId)} runs on branch ${shortId23(session.branch_id)}, but this channel targets a different branch. Sessions can emit only through gateway channels whose target branch matches their own. Call agor_gateway_outbound_targets_list to see usable channels.`
|
|
59332
|
+
);
|
|
59333
|
+
}
|
|
59334
|
+
}
|
|
58503
59335
|
async ensureCanEmitFromChannel(channel, userId, userRole) {
|
|
58504
|
-
if (
|
|
59336
|
+
if (hasMinimumRole17(userRole, ROLES22.ADMIN)) return;
|
|
58505
59337
|
const branch = await this.branchRepo.findById(channel.target_branch_id);
|
|
58506
59338
|
if (!branch) {
|
|
58507
59339
|
throw new Error(`Branch not found for gateway channel ${shortId23(channel.id)}`);
|
|
@@ -58533,6 +59365,9 @@ ${chunk}` : chunk;
|
|
|
58533
59365
|
if (config2.outbound_enabled !== true) {
|
|
58534
59366
|
throw new Error("Gateway outbound is disabled for this channel");
|
|
58535
59367
|
}
|
|
59368
|
+
if (data.emittedBySessionId) {
|
|
59369
|
+
await this.ensureSessionBranchBoundToChannel(channel, data.emittedBySessionId);
|
|
59370
|
+
}
|
|
58536
59371
|
await this.ensureCanEmitFromChannel(channel, data.emittedByUserId, data.userRole);
|
|
58537
59372
|
const target = data.target ?? (typeof config2.default_outbound_target === "string" ? config2.default_outbound_target : void 0);
|
|
58538
59373
|
if (!target) throw new Error("No usable default outbound target configured");
|
|
@@ -59107,6 +59942,9 @@ ${chunk}` : chunk;
|
|
|
59107
59942
|
promptText = contextPrefix + promptText;
|
|
59108
59943
|
}
|
|
59109
59944
|
}
|
|
59945
|
+
if (channel.channel_type === "slack") {
|
|
59946
|
+
promptText = prependSlackGatewayReplyNote(promptText);
|
|
59947
|
+
}
|
|
59110
59948
|
if (created && mcpAuthWarning) {
|
|
59111
59949
|
promptText = `${mcpAuthWarning}
|
|
59112
59950
|
|
|
@@ -59414,7 +60252,7 @@ ${promptText}`;
|
|
|
59414
60252
|
if (!tenantId) {
|
|
59415
60253
|
throw new Error(`Missing tenant context for gateway listener channel ${channel.id}`);
|
|
59416
60254
|
}
|
|
59417
|
-
await
|
|
60255
|
+
await runWithTenantDatabaseScope7(this.db, tenantId, async () => {
|
|
59418
60256
|
await this.create({
|
|
59419
60257
|
channel_key: channel.channel_key,
|
|
59420
60258
|
thread_id: msg.threadId,
|
|
@@ -59470,11 +60308,11 @@ function createGatewayChannelsService(db) {
|
|
|
59470
60308
|
import { GatewayChannelRepository as GatewayChannelRepository5 } from "@agor/core/db";
|
|
59471
60309
|
import { NotFound as NotFound6 } from "@agor/core/feathers";
|
|
59472
60310
|
import { getConnector as getConnector3 } from "@agor/core/gateway";
|
|
59473
|
-
import { GATEWAY_REDACTED_SENTINEL as GATEWAY_REDACTED_SENTINEL3, GATEWAY_SENSITIVE_CONFIG_FIELDS as
|
|
60311
|
+
import { GATEWAY_REDACTED_SENTINEL as GATEWAY_REDACTED_SENTINEL3, GATEWAY_SENSITIVE_CONFIG_FIELDS as GATEWAY_SENSITIVE_CONFIG_FIELDS4 } from "@agor/core/types";
|
|
59474
60312
|
function resolveEffectiveConfig(stored, overrides) {
|
|
59475
60313
|
const resolved = { ...stored };
|
|
59476
60314
|
if (!overrides) return resolved;
|
|
59477
|
-
const sensitive = new Set(
|
|
60315
|
+
const sensitive = new Set(GATEWAY_SENSITIVE_CONFIG_FIELDS4);
|
|
59478
60316
|
for (const [key, value] of Object.entries(overrides)) {
|
|
59479
60317
|
if (value === void 0 || value === null) continue;
|
|
59480
60318
|
if (value === GATEWAY_REDACTED_SENTINEL3) continue;
|
|
@@ -59532,7 +60370,7 @@ function createGatewayChannelsTestService(db) {
|
|
|
59532
60370
|
|
|
59533
60371
|
// src/services/github-app-setup.ts
|
|
59534
60372
|
import { shortId as shortId24 } from "@agor/core/db";
|
|
59535
|
-
import { hasMinimumRole as
|
|
60373
|
+
import { hasMinimumRole as hasMinimumRole18, ROLES as ROLES23 } from "@agor/core/types";
|
|
59536
60374
|
|
|
59537
60375
|
// src/utils/html.ts
|
|
59538
60376
|
function escapeHtml(value) {
|
|
@@ -59638,7 +60476,7 @@ function handleIssueState(app) {
|
|
|
59638
60476
|
res.status(401).json({ error: "Authentication required to initiate GitHub App install" });
|
|
59639
60477
|
return;
|
|
59640
60478
|
}
|
|
59641
|
-
if (!
|
|
60479
|
+
if (!hasMinimumRole18(authed.role, ROLES23.ADMIN)) {
|
|
59642
60480
|
res.status(403).json({ error: "Admin role required to initiate GitHub App install" });
|
|
59643
60481
|
return;
|
|
59644
60482
|
}
|
|
@@ -59850,7 +60688,7 @@ function registerGitHubAppSetupRoutes(app, opts) {
|
|
|
59850
60688
|
// src/services/knowledge-document-edits.ts
|
|
59851
60689
|
init_knowledge_access();
|
|
59852
60690
|
import { KnowledgeDocumentVersionRepository, KnowledgeNamespaceRepository as KnowledgeNamespaceRepository4 } from "@agor/core/db";
|
|
59853
|
-
import { BadRequest as BadRequest12, Forbidden as
|
|
60691
|
+
import { BadRequest as BadRequest12, Forbidden as Forbidden19, NotFound as NotFound7 } from "@agor/core/feathers";
|
|
59854
60692
|
import { applyKnowledgeEditOps, KnowledgeEditError } from "@agor/core/knowledge";
|
|
59855
60693
|
import { createTwoFilesPatch as createTwoFilesPatch2, structuredPatch } from "diff";
|
|
59856
60694
|
function versionToToken(version2) {
|
|
@@ -59880,7 +60718,7 @@ var KnowledgeDocumentEditsService = class {
|
|
|
59880
60718
|
}
|
|
59881
60719
|
async ensureCanEdit(document, user) {
|
|
59882
60720
|
if (await canWriteKnowledgeDocument(this.namespaceRepo, document, user)) return;
|
|
59883
|
-
throw new
|
|
60721
|
+
throw new Forbidden19("You do not have permission to edit this knowledge document");
|
|
59884
60722
|
}
|
|
59885
60723
|
async create(data, params) {
|
|
59886
60724
|
if (!data || !Array.isArray(data.ops) || data.ops.length === 0) {
|
|
@@ -60007,7 +60845,7 @@ import {
|
|
|
60007
60845
|
KnowledgeGraphRepository,
|
|
60008
60846
|
KnowledgeNamespaceRepository as KnowledgeNamespaceRepository5
|
|
60009
60847
|
} from "@agor/core/db";
|
|
60010
|
-
import { BadRequest as BadRequest13, Forbidden as
|
|
60848
|
+
import { BadRequest as BadRequest13, Forbidden as Forbidden20, NotFound as NotFound8 } from "@agor/core/feathers";
|
|
60011
60849
|
import {
|
|
60012
60850
|
buildKnowledgeDocumentUri as buildKnowledgeDocumentUri2,
|
|
60013
60851
|
extractKnowledgeLinks,
|
|
@@ -60637,7 +61475,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
60637
61475
|
const statusChanged = data.status !== void 0 && data.status !== existing.status;
|
|
60638
61476
|
if (!visibilityChanged && !editPolicyChanged && !statusChanged) return;
|
|
60639
61477
|
if (!this.canManageDocument(existing, user)) {
|
|
60640
|
-
throw new
|
|
61478
|
+
throw new Forbidden20(
|
|
60641
61479
|
"Only the owner or an admin can change knowledge document visibility, lifecycle status, or edit policy"
|
|
60642
61480
|
);
|
|
60643
61481
|
}
|
|
@@ -60663,7 +61501,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
60663
61501
|
user
|
|
60664
61502
|
);
|
|
60665
61503
|
if (!hasKnowledgeNamespacePermission(permission, "write")) {
|
|
60666
|
-
throw new
|
|
61504
|
+
throw new Forbidden20("You do not have permission to write to this knowledge namespace");
|
|
60667
61505
|
}
|
|
60668
61506
|
}
|
|
60669
61507
|
prepareWriteData(data, existing) {
|
|
@@ -60851,7 +61689,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
60851
61689
|
if (!doc) throw new NotFound8(`Knowledge document not found: ${id}`);
|
|
60852
61690
|
await this.assertActiveDocument(doc);
|
|
60853
61691
|
if (!await this.canRead(doc, params?.user)) {
|
|
60854
|
-
throw new
|
|
61692
|
+
throw new Forbidden20("You do not have permission to view this knowledge document");
|
|
60855
61693
|
}
|
|
60856
61694
|
return this.hydrateDocument(doc, params?.query);
|
|
60857
61695
|
}
|
|
@@ -60860,7 +61698,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
60860
61698
|
if (!doc) throw new NotFound8("Knowledge document not found");
|
|
60861
61699
|
await this.assertActiveDocument(doc);
|
|
60862
61700
|
if (!await this.canRead(doc, params?.user)) {
|
|
60863
|
-
throw new
|
|
61701
|
+
throw new Forbidden20("You do not have permission to view this knowledge document");
|
|
60864
61702
|
}
|
|
60865
61703
|
return this.hydrateDocument(doc, data);
|
|
60866
61704
|
}
|
|
@@ -60879,7 +61717,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
60879
61717
|
await this.assertActiveDocument(existing);
|
|
60880
61718
|
this.assertCanChangeGovernance(existing, data, params?.user);
|
|
60881
61719
|
if (!await this.canEdit(existing, params?.user)) {
|
|
60882
|
-
throw new
|
|
61720
|
+
throw new Forbidden20("You do not have permission to update this knowledge document");
|
|
60883
61721
|
}
|
|
60884
61722
|
await this.assertExpectedVersion(existing, data.expected_version);
|
|
60885
61723
|
const result2 = await this.repo.update(
|
|
@@ -60979,7 +61817,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
60979
61817
|
await this.assertActiveDocument(existing);
|
|
60980
61818
|
this.assertCanChangeGovernance(existing, data, params?.user);
|
|
60981
61819
|
if (!await this.canEdit(existing, params?.user)) {
|
|
60982
|
-
throw new
|
|
61820
|
+
throw new Forbidden20("You do not have permission to update this knowledge document");
|
|
60983
61821
|
}
|
|
60984
61822
|
const result = await this.repo.update(String(id), {
|
|
60985
61823
|
...this.prepareWriteData(data, existing),
|
|
@@ -61004,7 +61842,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
61004
61842
|
await this.assertActiveDocument(existing);
|
|
61005
61843
|
this.assertCanChangeGovernance(existing, data, params?.user);
|
|
61006
61844
|
if (!await this.canEdit(existing, params?.user)) {
|
|
61007
|
-
throw new
|
|
61845
|
+
throw new Forbidden20("You do not have permission to update this knowledge document");
|
|
61008
61846
|
}
|
|
61009
61847
|
const result = await this.repo.update(String(id), {
|
|
61010
61848
|
...this.prepareWriteData(data, existing),
|
|
@@ -61030,7 +61868,7 @@ var KnowledgeDocumentsService = class extends DrizzleService {
|
|
|
61030
61868
|
await this.assertActiveDocument(existing);
|
|
61031
61869
|
await this.assertCanWriteNamespace(existing.namespace_id, params?.user);
|
|
61032
61870
|
if (!this.canManageDocument(existing, params?.user)) {
|
|
61033
|
-
throw new
|
|
61871
|
+
throw new Forbidden20("You do not have permission to delete this knowledge document");
|
|
61034
61872
|
}
|
|
61035
61873
|
await this.repo.delete(String(id));
|
|
61036
61874
|
this.emit?.("removed", existing, params);
|
|
@@ -61048,7 +61886,7 @@ import {
|
|
|
61048
61886
|
KnowledgeGraphRepository as KnowledgeGraphRepository2,
|
|
61049
61887
|
KnowledgeNamespaceRepository as KnowledgeNamespaceRepository6
|
|
61050
61888
|
} from "@agor/core/db";
|
|
61051
|
-
import { Forbidden as
|
|
61889
|
+
import { Forbidden as Forbidden21, NotFound as NotFound9 } from "@agor/core/feathers";
|
|
61052
61890
|
import {
|
|
61053
61891
|
KNOWLEDGE_DOCUMENT_URI_PREFIX as KNOWLEDGE_DOCUMENT_URI_PREFIX2,
|
|
61054
61892
|
KNOWLEDGE_UNIT_URI_PREFIX,
|
|
@@ -61195,7 +62033,7 @@ var KnowledgeGraphService = class {
|
|
|
61195
62033
|
}
|
|
61196
62034
|
if (!document) return;
|
|
61197
62035
|
if (!await this.canWrite(document, user)) {
|
|
61198
|
-
throw new
|
|
62036
|
+
throw new Forbidden21("You do not have permission to link this knowledge document");
|
|
61199
62037
|
}
|
|
61200
62038
|
}
|
|
61201
62039
|
async documentForNode(node) {
|
|
@@ -61337,7 +62175,7 @@ import {
|
|
|
61337
62175
|
GroupRepository as GroupRepository2,
|
|
61338
62176
|
KnowledgeNamespaceRepository as KnowledgeNamespaceRepository7
|
|
61339
62177
|
} from "@agor/core/db";
|
|
61340
|
-
import { BadRequest as BadRequest14, Forbidden as
|
|
62178
|
+
import { BadRequest as BadRequest14, Forbidden as Forbidden22, NotFound as NotFound10 } from "@agor/core/feathers";
|
|
61341
62179
|
var KnowledgeNamespacesService = class extends DrizzleService {
|
|
61342
62180
|
repo;
|
|
61343
62181
|
groups;
|
|
@@ -61398,13 +62236,13 @@ var KnowledgeNamespacesService = class extends DrizzleService {
|
|
|
61398
62236
|
async assertCanReadNamespace(namespace, params) {
|
|
61399
62237
|
const permission = await this.namespacePermission(namespace, params);
|
|
61400
62238
|
if (permission === "none") {
|
|
61401
|
-
throw new
|
|
62239
|
+
throw new Forbidden22("You do not have permission to view this knowledge namespace");
|
|
61402
62240
|
}
|
|
61403
62241
|
}
|
|
61404
62242
|
async assertCanManage(namespace, params) {
|
|
61405
62243
|
const permission = await this.namespacePermission(namespace, params);
|
|
61406
62244
|
if (permission !== "own") {
|
|
61407
|
-
throw new
|
|
62245
|
+
throw new Forbidden22("You do not have permission to manage this knowledge namespace");
|
|
61408
62246
|
}
|
|
61409
62247
|
}
|
|
61410
62248
|
attributionUserId(params, requestedUserId) {
|
|
@@ -62230,7 +63068,7 @@ import {
|
|
|
62230
63068
|
KnowledgeDocumentVersionRepository as KnowledgeDocumentVersionRepository3,
|
|
62231
63069
|
KnowledgeNamespaceRepository as KnowledgeNamespaceRepository9
|
|
62232
63070
|
} from "@agor/core/db";
|
|
62233
|
-
import { BadRequest as BadRequest17, Forbidden as
|
|
63071
|
+
import { BadRequest as BadRequest17, Forbidden as Forbidden23 } from "@agor/core/feathers";
|
|
62234
63072
|
import {
|
|
62235
63073
|
parseKnowledgeUri as parseKnowledgeUri4
|
|
62236
63074
|
} from "@agor/core/types";
|
|
@@ -62274,7 +63112,7 @@ var KnowledgeVersionsService = class extends DrizzleService {
|
|
|
62274
63112
|
const namespace = await this.namespaces.findById(document.namespace_id);
|
|
62275
63113
|
if (!namespace || namespace.archived) return [];
|
|
62276
63114
|
if (!await this.canRead(document, params?.user)) {
|
|
62277
|
-
throw new
|
|
63115
|
+
throw new Forbidden23("You do not have permission to view this knowledge document history");
|
|
62278
63116
|
}
|
|
62279
63117
|
const versions = await this.versions.findAll({
|
|
62280
63118
|
document_id: documentId
|
|
@@ -62963,7 +63801,7 @@ import {
|
|
|
62963
63801
|
shortId as shortId26
|
|
62964
63802
|
} from "@agor/core/db";
|
|
62965
63803
|
import { autoAssignBranchUniqueId } from "@agor/core/environment/variable-resolver";
|
|
62966
|
-
import { BadRequest as BadRequest19, Forbidden as
|
|
63804
|
+
import { BadRequest as BadRequest19, Forbidden as Forbidden24, NotAuthenticated as NotAuthenticated20 } from "@agor/core/feathers";
|
|
62967
63805
|
import {
|
|
62968
63806
|
assertRemoteRefVisibleForClone,
|
|
62969
63807
|
getDefaultBranch,
|
|
@@ -63183,7 +64021,7 @@ var ReposService = class extends DrizzleService {
|
|
|
63183
64021
|
}
|
|
63184
64022
|
const isService = !!caller._isServiceAccount;
|
|
63185
64023
|
if (!isService) {
|
|
63186
|
-
throw new
|
|
64024
|
+
throw new Forbidden24("Only the executor service account may initialize Unix groups");
|
|
63187
64025
|
}
|
|
63188
64026
|
}
|
|
63189
64027
|
const { initializeRepoUnixGroup: initializeRepoUnixGroup2 } = await Promise.resolve().then(() => (init_unix_group_init(), unix_group_init_exports));
|
|
@@ -63857,6 +64695,50 @@ function createSessionMCPServersService(db) {
|
|
|
63857
64695
|
return new SessionMCPServersService(db);
|
|
63858
64696
|
}
|
|
63859
64697
|
|
|
64698
|
+
// src/services/session-streams.ts
|
|
64699
|
+
import { BadRequest as BadRequest20 } from "@agor/core/feathers";
|
|
64700
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
64701
|
+
function isCanonicalSessionId(id) {
|
|
64702
|
+
return UUID_RE.test(id);
|
|
64703
|
+
}
|
|
64704
|
+
function createSessionStreamsService(app) {
|
|
64705
|
+
const resolveAccessibleSessionId = async (sessionId, params) => {
|
|
64706
|
+
const session = await app.service("sessions").get(sessionId, { ...params ?? {}, query: {} });
|
|
64707
|
+
return session?.session_id ?? null;
|
|
64708
|
+
};
|
|
64709
|
+
return {
|
|
64710
|
+
async create(data, params) {
|
|
64711
|
+
const connection = params?.connection;
|
|
64712
|
+
if (!connection) {
|
|
64713
|
+
throw new BadRequest20("session stream subscription requires a realtime connection");
|
|
64714
|
+
}
|
|
64715
|
+
const sessionId = data?.session_id ?? data?.sessionId;
|
|
64716
|
+
if (!sessionId || typeof sessionId !== "string") {
|
|
64717
|
+
throw new BadRequest20("session_id is required");
|
|
64718
|
+
}
|
|
64719
|
+
const canonicalId = await resolveAccessibleSessionId(sessionId, params) ?? sessionId;
|
|
64720
|
+
joinSessionStreamChannel(app, canonicalId, connection);
|
|
64721
|
+
return { session_id: canonicalId, subscribed: true };
|
|
64722
|
+
},
|
|
64723
|
+
async remove(id, params) {
|
|
64724
|
+
const connection = params?.connection;
|
|
64725
|
+
const sessionId = typeof id === "string" ? id : "";
|
|
64726
|
+
if (!connection || !sessionId) {
|
|
64727
|
+
return { session_id: sessionId, subscribed: false };
|
|
64728
|
+
}
|
|
64729
|
+
let canonicalId = sessionId;
|
|
64730
|
+
if (!isCanonicalSessionId(sessionId)) {
|
|
64731
|
+
try {
|
|
64732
|
+
canonicalId = await resolveAccessibleSessionId(sessionId, params) ?? sessionId;
|
|
64733
|
+
} catch {
|
|
64734
|
+
}
|
|
64735
|
+
}
|
|
64736
|
+
leaveSessionStreamChannel(app, canonicalId, connection);
|
|
64737
|
+
return { session_id: canonicalId, subscribed: false };
|
|
64738
|
+
}
|
|
64739
|
+
};
|
|
64740
|
+
}
|
|
64741
|
+
|
|
63860
64742
|
// src/services/tasks.ts
|
|
63861
64743
|
init_drizzle();
|
|
63862
64744
|
init_append_system_message();
|
|
@@ -64761,14 +65643,14 @@ function createTasksService(db, app) {
|
|
|
64761
65643
|
}
|
|
64762
65644
|
|
|
64763
65645
|
// src/services/templates.ts
|
|
64764
|
-
import { BadRequest as
|
|
65646
|
+
import { BadRequest as BadRequest21 } from "@agor/core/feathers";
|
|
64765
65647
|
import { renderTemplate as renderTemplate2 } from "@agor/core/templates/handlebars-helpers";
|
|
64766
|
-
import { ROLES as
|
|
65648
|
+
import { ROLES as ROLES24 } from "@agor/core/types";
|
|
64767
65649
|
var TemplatesService = class {
|
|
64768
65650
|
async create(data, params) {
|
|
64769
|
-
ensureMinimumRole(params,
|
|
65651
|
+
ensureMinimumRole(params, ROLES24.MEMBER, "render templates");
|
|
64770
65652
|
if (typeof data?.template !== "string") {
|
|
64771
|
-
throw new
|
|
65653
|
+
throw new BadRequest21("template (string) is required");
|
|
64772
65654
|
}
|
|
64773
65655
|
const context = data.context && typeof data.context === "object" ? data.context : {};
|
|
64774
65656
|
const rendered = renderTemplate2(data.template, context, { onError: data.onError ?? "empty" });
|
|
@@ -64796,11 +65678,11 @@ import {
|
|
|
64796
65678
|
} from "@agor/core/config";
|
|
64797
65679
|
import {
|
|
64798
65680
|
BranchRepository as BranchRepository15,
|
|
64799
|
-
SessionRepository as
|
|
65681
|
+
SessionRepository as SessionRepository10,
|
|
64800
65682
|
shortId as shortId29,
|
|
64801
65683
|
UsersRepository as UsersRepository10
|
|
64802
65684
|
} from "@agor/core/db";
|
|
64803
|
-
import { Forbidden as
|
|
65685
|
+
import { Forbidden as Forbidden25 } from "@agor/core/feathers";
|
|
64804
65686
|
import {
|
|
64805
65687
|
getBranchSymlinkPath,
|
|
64806
65688
|
resolveUnixUserForImpersonation as resolveUnixUserForImpersonation3,
|
|
@@ -64904,12 +65786,12 @@ var TerminalsService = class {
|
|
|
64904
65786
|
if (rbacEnabled) {
|
|
64905
65787
|
const userId = params?.user?.user_id;
|
|
64906
65788
|
if (!userId) {
|
|
64907
|
-
throw new
|
|
65789
|
+
throw new Forbidden25("Authentication required to open terminals");
|
|
64908
65790
|
}
|
|
64909
65791
|
const branchRepo = new BranchRepository15(this.db);
|
|
64910
65792
|
const branch = await branchRepo.findById(data.branchId);
|
|
64911
65793
|
if (!branch) {
|
|
64912
|
-
throw new
|
|
65794
|
+
throw new Forbidden25(`Branch not found: ${data.branchId}`);
|
|
64913
65795
|
}
|
|
64914
65796
|
const isOwner = await branchRepo.isOwner(branch.branch_id, userId);
|
|
64915
65797
|
const effectivePermission = await branchRepo.resolveUserPermission(branch, userId);
|
|
@@ -64924,7 +65806,7 @@ var TerminalsService = class {
|
|
|
64924
65806
|
allowSuperadmin,
|
|
64925
65807
|
effectivePermission
|
|
64926
65808
|
)) {
|
|
64927
|
-
throw new
|
|
65809
|
+
throw new Forbidden25(
|
|
64928
65810
|
`You need 'session' permission on branch ${branch.name} to open a terminal there.`
|
|
64929
65811
|
);
|
|
64930
65812
|
}
|
|
@@ -65025,11 +65907,11 @@ var TerminalsService = class {
|
|
|
65025
65907
|
async resolveEnsureCliTab(sessionId, claimedBranchId, params) {
|
|
65026
65908
|
if (!sessionId) return null;
|
|
65027
65909
|
try {
|
|
65028
|
-
const sessionRepo = new
|
|
65910
|
+
const sessionRepo = new SessionRepository10(this.db);
|
|
65029
65911
|
const session = await sessionRepo.findById(sessionId).catch(() => null);
|
|
65030
65912
|
if (session?.agentic_tool !== "claude-code-cli") return null;
|
|
65031
65913
|
if (claimedBranchId && claimedBranchId !== session.branch_id) {
|
|
65032
|
-
throw new
|
|
65914
|
+
throw new Forbidden25(
|
|
65033
65915
|
`ensureCliSessionId session belongs to a different branch than the one provided.`
|
|
65034
65916
|
);
|
|
65035
65917
|
}
|
|
@@ -65039,12 +65921,12 @@ var TerminalsService = class {
|
|
|
65039
65921
|
if (rbacEnabled) {
|
|
65040
65922
|
const callerUserId = params?.user?.user_id;
|
|
65041
65923
|
if (!callerUserId) {
|
|
65042
|
-
throw new
|
|
65924
|
+
throw new Forbidden25("Authentication required to ensure a CLI tab");
|
|
65043
65925
|
}
|
|
65044
65926
|
const branchRepo2 = new BranchRepository15(this.db);
|
|
65045
65927
|
const wt = await branchRepo2.findById(session.branch_id);
|
|
65046
65928
|
if (!wt) {
|
|
65047
|
-
throw new
|
|
65929
|
+
throw new Forbidden25(`Session's branch not found: ${session.branch_id}`);
|
|
65048
65930
|
}
|
|
65049
65931
|
const isOwner = await branchRepo2.isOwner(wt.branch_id, callerUserId);
|
|
65050
65932
|
const effectivePermission = await branchRepo2.resolveUserPermission(wt, callerUserId);
|
|
@@ -65059,7 +65941,7 @@ var TerminalsService = class {
|
|
|
65059
65941
|
allowSuperadmin,
|
|
65060
65942
|
effectivePermission
|
|
65061
65943
|
)) {
|
|
65062
|
-
throw new
|
|
65944
|
+
throw new Forbidden25(
|
|
65063
65945
|
`You need 'session' permission on the session's branch to ensure its CLI tab.`
|
|
65064
65946
|
);
|
|
65065
65947
|
}
|
|
@@ -65070,7 +65952,7 @@ var TerminalsService = class {
|
|
|
65070
65952
|
callerRole: params.user?.role,
|
|
65071
65953
|
sessionCreatedBy: session.created_by
|
|
65072
65954
|
})) {
|
|
65073
|
-
throw new
|
|
65955
|
+
throw new Forbidden25("You can only ensure CLI tabs for Claude CLI sessions you created.");
|
|
65074
65956
|
}
|
|
65075
65957
|
const branchRepo = new BranchRepository15(this.db);
|
|
65076
65958
|
const branch = await branchRepo.findById(session.branch_id);
|
|
@@ -65089,7 +65971,7 @@ var TerminalsService = class {
|
|
|
65089
65971
|
sessionId: session.session_id
|
|
65090
65972
|
};
|
|
65091
65973
|
} catch (err) {
|
|
65092
|
-
if (err instanceof
|
|
65974
|
+
if (err instanceof Forbidden25) throw err;
|
|
65093
65975
|
console.warn("[TerminalsService] resolveEnsureCliTab failed", err);
|
|
65094
65976
|
return null;
|
|
65095
65977
|
}
|
|
@@ -65403,7 +66285,7 @@ init_users();
|
|
|
65403
66285
|
init_runtime_tokens();
|
|
65404
66286
|
import { resolveTenantContext as resolveTenantContext5 } from "@agor/core/config";
|
|
65405
66287
|
import { shortId as shortId30 } from "@agor/core/db";
|
|
65406
|
-
import
|
|
66288
|
+
import jwt11 from "jsonwebtoken";
|
|
65407
66289
|
function socketAuthState(userId, isService, tenant) {
|
|
65408
66290
|
return tenant ? { userId, isService, tenant } : { userId, isService };
|
|
65409
66291
|
}
|
|
@@ -65494,7 +66376,7 @@ function createSocketIOConfig(app, options) {
|
|
|
65494
66376
|
console.log(`\u{1F513} WebSocket connection without auth (for login flow): ${socket.id}`);
|
|
65495
66377
|
return next();
|
|
65496
66378
|
}
|
|
65497
|
-
const decoded =
|
|
66379
|
+
const decoded = jwt11.verify(token, jwtSecret, {
|
|
65498
66380
|
issuer: RUNTIME_JWT_ISSUER,
|
|
65499
66381
|
audience: RUNTIME_JWT_AUDIENCE
|
|
65500
66382
|
});
|
|
@@ -65849,6 +66731,7 @@ function configureChannels(app, options = {}) {
|
|
|
65849
66731
|
app.channel(tenantUserChannelName(tenant.tenant_id, userId)).leave(context.connection);
|
|
65850
66732
|
}
|
|
65851
66733
|
}
|
|
66734
|
+
leaveAllSessionStreamChannels(app, context.connection);
|
|
65852
66735
|
}
|
|
65853
66736
|
});
|
|
65854
66737
|
}
|
|
@@ -66073,6 +66956,13 @@ async function registerServices(ctx) {
|
|
|
66073
66956
|
sessionsService.setExecuteHandler(
|
|
66074
66957
|
createExecuteHandler(ctx, sessionsService, sessionTokenService)
|
|
66075
66958
|
);
|
|
66959
|
+
app.use("/session-streams", createSessionStreamsService(app), {
|
|
66960
|
+
methods: ["create", "remove"]
|
|
66961
|
+
});
|
|
66962
|
+
app.service("/session-streams").hooks({
|
|
66963
|
+
before: { all: [ctx.requireAuth] }
|
|
66964
|
+
});
|
|
66965
|
+
app.service("/session-streams").publish(() => []);
|
|
66076
66966
|
app.use("/tasks", createTasksService(db, app), {
|
|
66077
66967
|
// Custom events not in this list are dropped at the FeathersJS transport
|
|
66078
66968
|
// boundary — they fire on the local EventEmitter but never reach socket
|
|
@@ -66275,7 +67165,7 @@ async function registerServices(ctx) {
|
|
|
66275
67165
|
app.use("/gateway-channels/test", createGatewayChannelsTestService(db));
|
|
66276
67166
|
app.service("gateway-channels/test").hooks({
|
|
66277
67167
|
before: {
|
|
66278
|
-
create: [ctx.requireAuth, requireMinimumRole(
|
|
67168
|
+
create: [ctx.requireAuth, requireMinimumRole(ROLES25.ADMIN, "test gateway channels")]
|
|
66279
67169
|
}
|
|
66280
67170
|
});
|
|
66281
67171
|
app.use("/thread-session-map", createThreadSessionMapService(db));
|
|
@@ -66313,9 +67203,9 @@ async function registerServices(ctx) {
|
|
|
66313
67203
|
app.use("/cursor-models", createCursorModelsService(db));
|
|
66314
67204
|
app.service("/cursor-models").hooks({ before: { find: [ctx.requireAuth] } });
|
|
66315
67205
|
const branchRepository = new BranchRepository16(db);
|
|
66316
|
-
const { UsersRepository: UsersRepository11, SessionRepository:
|
|
67206
|
+
const { UsersRepository: UsersRepository11, SessionRepository: SessionRepository12 } = await import("@agor/core/db");
|
|
66317
67207
|
const usersRepository = new UsersRepository11(db);
|
|
66318
|
-
const sessionsRepository = new
|
|
67208
|
+
const sessionsRepository = new SessionRepository12(db);
|
|
66319
67209
|
if (svcEnabled("file_browser")) {
|
|
66320
67210
|
app.use("/context", createContextService(branchRepository));
|
|
66321
67211
|
app.use("/file", createFileService(branchRepository));
|
|
@@ -66792,6 +67682,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
66792
67682
|
mcpServerId: opts.mcpServerId,
|
|
66793
67683
|
userId: opts.userId,
|
|
66794
67684
|
oauthMode: opts.oauthMode,
|
|
67685
|
+
tenantId: opts.tenantId ?? getCurrentTenantId7(),
|
|
66795
67686
|
socketId: opts.socketId,
|
|
66796
67687
|
createdAt: Date.now(),
|
|
66797
67688
|
tokenResolve,
|
|
@@ -66815,6 +67706,31 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
66815
67706
|
}
|
|
66816
67707
|
return base;
|
|
66817
67708
|
}
|
|
67709
|
+
const tenantIdFromParams = (params) => params?.tenant?.tenant_id ?? getCurrentTenantId7();
|
|
67710
|
+
const persistOAuthTokenForPendingFlow = async (tokenResponse, pendingFlow, logPrefix) => {
|
|
67711
|
+
const work = () => persistOAuthToken(
|
|
67712
|
+
db,
|
|
67713
|
+
tokenResponse,
|
|
67714
|
+
pendingFlow.mcpUrl,
|
|
67715
|
+
{
|
|
67716
|
+
...pendingFlow,
|
|
67717
|
+
clientId: pendingFlow.context.clientId,
|
|
67718
|
+
clientSecret: pendingFlow.context.clientSecret,
|
|
67719
|
+
tokenEndpoint: pendingFlow.context.tokenEndpoint
|
|
67720
|
+
},
|
|
67721
|
+
logPrefix
|
|
67722
|
+
);
|
|
67723
|
+
if (pendingFlow.tenantId) {
|
|
67724
|
+
await runInOAuthTenantScope(db, pendingFlow.tenantId, work);
|
|
67725
|
+
return;
|
|
67726
|
+
}
|
|
67727
|
+
if (isPostgresDatabase8(db) && pendingFlow.mcpServerId) {
|
|
67728
|
+
throw new Error(
|
|
67729
|
+
"Missing tenant context for MCP OAuth callback. Please restart the OAuth flow."
|
|
67730
|
+
);
|
|
67731
|
+
}
|
|
67732
|
+
await work();
|
|
67733
|
+
};
|
|
66818
67734
|
const oauthCallbackHandler = async (req, res) => {
|
|
66819
67735
|
res.setHeader("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'");
|
|
66820
67736
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
@@ -66857,18 +67773,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
66857
67773
|
const { completeMCPOAuthFlow } = await import("@agor/core/tools/mcp/oauth-mcp-transport");
|
|
66858
67774
|
const tokenResponse = await completeMCPOAuthFlow(pendingFlow.context, code, state);
|
|
66859
67775
|
pendingOAuthFlows.delete(state);
|
|
66860
|
-
await
|
|
66861
|
-
db,
|
|
66862
|
-
tokenResponse,
|
|
66863
|
-
pendingFlow.mcpUrl,
|
|
66864
|
-
{
|
|
66865
|
-
...pendingFlow,
|
|
66866
|
-
clientId: pendingFlow.context.clientId,
|
|
66867
|
-
clientSecret: pendingFlow.context.clientSecret,
|
|
66868
|
-
tokenEndpoint: pendingFlow.context.tokenEndpoint
|
|
66869
|
-
},
|
|
66870
|
-
"OAuth Callback"
|
|
66871
|
-
);
|
|
67776
|
+
await persistOAuthTokenForPendingFlow(tokenResponse, pendingFlow, "OAuth Callback");
|
|
66872
67777
|
if (app.io) {
|
|
66873
67778
|
const oauthEvent = {
|
|
66874
67779
|
state,
|
|
@@ -66997,6 +67902,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
66997
67902
|
// call (writes to the shared MCP server row, not per-user).
|
|
66998
67903
|
oauthMode: "shared",
|
|
66999
67904
|
clientId: data.client_id,
|
|
67905
|
+
tenantId: tenantIdFromParams(params),
|
|
67000
67906
|
socketId: connection?.id
|
|
67001
67907
|
});
|
|
67002
67908
|
} catch (err) {
|
|
@@ -67199,6 +68105,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67199
68105
|
try {
|
|
67200
68106
|
console.log("[OAuth Start] Starting two-phase OAuth flow for:", data.mcp_url);
|
|
67201
68107
|
const userId = params?.user?.user_id;
|
|
68108
|
+
const tenantId = tenantIdFromParams(params);
|
|
67202
68109
|
let oauthMode;
|
|
67203
68110
|
let authorizationUrlOverride;
|
|
67204
68111
|
let tokenUrlOverride;
|
|
@@ -67206,8 +68113,10 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67206
68113
|
let clientIdFromConfig;
|
|
67207
68114
|
let scopeOverride;
|
|
67208
68115
|
if (data.mcp_server_id) {
|
|
67209
|
-
const
|
|
67210
|
-
|
|
68116
|
+
const server = await runInOAuthTenantScope(db, tenantId, () => {
|
|
68117
|
+
const mcpServerRepo = new MCPServerRepository5(db);
|
|
68118
|
+
return mcpServerRepo.findById(data.mcp_server_id);
|
|
68119
|
+
});
|
|
67211
68120
|
if (server?.auth?.type === "oauth") {
|
|
67212
68121
|
oauthMode = server.auth.oauth_mode || "per_user";
|
|
67213
68122
|
authorizationUrlOverride = server.auth.oauth_authorization_url;
|
|
@@ -67255,6 +68164,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67255
68164
|
authorizationUrlOverride,
|
|
67256
68165
|
tokenUrlOverride,
|
|
67257
68166
|
scope: scopeOverride,
|
|
68167
|
+
tenantId,
|
|
67258
68168
|
socketId
|
|
67259
68169
|
});
|
|
67260
68170
|
} catch (err) {
|
|
@@ -67299,18 +68209,13 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67299
68209
|
};
|
|
67300
68210
|
const tokenResponse = await completeMCPOAuthFlow(pendingFlow.context, code, state);
|
|
67301
68211
|
pendingOAuthFlows.delete(state);
|
|
67302
|
-
|
|
67303
|
-
|
|
67304
|
-
|
|
67305
|
-
|
|
67306
|
-
|
|
67307
|
-
|
|
67308
|
-
|
|
67309
|
-
clientSecret: pendingFlow.context.clientSecret,
|
|
67310
|
-
tokenEndpoint: pendingFlow.context.tokenEndpoint
|
|
67311
|
-
},
|
|
67312
|
-
"OAuth Complete"
|
|
67313
|
-
);
|
|
68212
|
+
const activeTenantId = getCurrentTenantId7();
|
|
68213
|
+
if (pendingFlow.tenantId && activeTenantId && pendingFlow.tenantId !== activeTenantId) {
|
|
68214
|
+
throw new Error(
|
|
68215
|
+
"OAuth flow belongs to a different tenant. Please restart the OAuth flow."
|
|
68216
|
+
);
|
|
68217
|
+
}
|
|
68218
|
+
await persistOAuthTokenForPendingFlow(tokenResponse, pendingFlow, "OAuth Complete");
|
|
67314
68219
|
return { success: true, message: "OAuth authentication successful!", tokenObtained: true };
|
|
67315
68220
|
} catch (error51) {
|
|
67316
68221
|
console.error("[OAuth Complete] Error:", error51);
|
|
@@ -67367,17 +68272,35 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67367
68272
|
}
|
|
67368
68273
|
const sessionId = params?.session_id;
|
|
67369
68274
|
const trustedInternalOrService = shouldExposeMCPServerSecrets(params);
|
|
67370
|
-
|
|
68275
|
+
let trustedSessionExecutor = shouldExposeMCPServerSecretsForSessionToken(params, {
|
|
67371
68276
|
sessionId
|
|
67372
68277
|
});
|
|
68278
|
+
let executorSessionId = sessionId;
|
|
68279
|
+
if (!trustedSessionExecutor && params?.provider && data.executorSessionToken) {
|
|
68280
|
+
const executorTokenService = app.sessionTokenService;
|
|
68281
|
+
const sessionInfo = await executorTokenService?.validateToken(
|
|
68282
|
+
data.executorSessionToken,
|
|
68283
|
+
{}
|
|
68284
|
+
);
|
|
68285
|
+
if (sessionInfo?.session_id) {
|
|
68286
|
+
executorSessionId = sessionInfo.session_id;
|
|
68287
|
+
trustedSessionExecutor = true;
|
|
68288
|
+
}
|
|
68289
|
+
}
|
|
67373
68290
|
if (!trustedInternalOrService && !trustedSessionExecutor) {
|
|
67374
|
-
throw new
|
|
68291
|
+
throw new Forbidden26("oauth-auth-headers is only available to trusted executor paths");
|
|
67375
68292
|
}
|
|
67376
68293
|
const userTokenRepo = new UserMCPOAuthTokenRepository4(db);
|
|
67377
68294
|
const mcpServerRepo = new MCPServerRepository5(db);
|
|
67378
68295
|
if (trustedSessionExecutor) {
|
|
68296
|
+
if (!executorSessionId) {
|
|
68297
|
+
throw new Forbidden26("oauth-auth-headers requires executor session scope");
|
|
68298
|
+
}
|
|
67379
68299
|
const sessionMcpRepo = new SessionMCPServerRepository5(db);
|
|
67380
|
-
const attachedServers = await sessionMcpRepo.listServers(
|
|
68300
|
+
const attachedServers = await sessionMcpRepo.listServers(
|
|
68301
|
+
executorSessionId,
|
|
68302
|
+
true
|
|
68303
|
+
);
|
|
67381
68304
|
const globalServers = await mcpServerRepo.findAll({ scope: "global", enabled: true });
|
|
67382
68305
|
const allowedServerIds = /* @__PURE__ */ new Set([
|
|
67383
68306
|
...globalServers.map((server) => server.mcp_server_id),
|
|
@@ -67517,7 +68440,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67517
68440
|
const { restoreRedactedMCPAuthSecrets } = await import("@agor/core/tools/mcp/auth-secrets");
|
|
67518
68441
|
const { resolveMCPAuthHeaders } = await import("@agor/core/tools/mcp/jwt-auth");
|
|
67519
68442
|
const { mergeMCPRemoteHeaders, restoreRedactedMCPCustomHeaders } = await import("@agor/core/tools/mcp/http-headers");
|
|
67520
|
-
const { hasMinimumRole:
|
|
68443
|
+
const { hasMinimumRole: hasMinimumRole19, ROLES: ROLES26 } = await import("@agor/core/types");
|
|
67521
68444
|
const mcpServerRepo = new MCPServerRepository5(db);
|
|
67522
68445
|
const validateUrl = (url2) => {
|
|
67523
68446
|
try {
|
|
@@ -67552,7 +68475,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67552
68475
|
if (params?.provider && params.user) {
|
|
67553
68476
|
const userId2 = params.user.user_id;
|
|
67554
68477
|
const userRole = params.user.role?.toLowerCase();
|
|
67555
|
-
const isAdmin4 =
|
|
68478
|
+
const isAdmin4 = hasMinimumRole19(userRole, ROLES26.ADMIN);
|
|
67556
68479
|
const isOwner = server.owner_user_id === userId2;
|
|
67557
68480
|
if (server.scope === "global" && !isOwner && !isAdmin4)
|
|
67558
68481
|
return {
|
|
@@ -67581,7 +68504,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67581
68504
|
if (params?.provider && params.user) {
|
|
67582
68505
|
const userId2 = params.user.user_id;
|
|
67583
68506
|
const userRole = params.user.role?.toLowerCase();
|
|
67584
|
-
const isAdmin4 =
|
|
68507
|
+
const isAdmin4 = hasMinimumRole19(userRole, ROLES26.ADMIN);
|
|
67585
68508
|
const isOwner = server.owner_user_id === userId2;
|
|
67586
68509
|
if (server.scope === "global" && !isOwner && !isAdmin4)
|
|
67587
68510
|
return {
|
|
@@ -67674,6 +68597,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67674
68597
|
// call). Without a serverId nothing is persisted to the DB; the
|
|
67675
68598
|
// daemon-level cache below carries the token for this request.
|
|
67676
68599
|
oauthMode: "shared",
|
|
68600
|
+
tenantId: tenantIdFromParams(params),
|
|
67677
68601
|
socketId: connection?.id
|
|
67678
68602
|
});
|
|
67679
68603
|
const tokenResponse = await started.awaitToken();
|
|
@@ -67848,7 +68772,7 @@ async function registerMCPServices(ctx, sessionsService) {
|
|
|
67848
68772
|
return { oauthCallbackHandler };
|
|
67849
68773
|
}
|
|
67850
68774
|
async function bootstrapSuperadminUsers(config2, usersService, allowSuperadmin) {
|
|
67851
|
-
const { ROLES:
|
|
68775
|
+
const { ROLES: ROLES26 } = await import("@agor/core/types");
|
|
67852
68776
|
const bootstrapUsers = config2.execution?.bootstrap_superadmin_users ?? [];
|
|
67853
68777
|
if (bootstrapUsers.length === 0) return;
|
|
67854
68778
|
if (!allowSuperadmin) {
|
|
@@ -67863,8 +68787,8 @@ async function bootstrapSuperadminUsers(config2, usersService, allowSuperadmin)
|
|
|
67863
68787
|
if (!userId) continue;
|
|
67864
68788
|
try {
|
|
67865
68789
|
const user = await usersService.get(userId);
|
|
67866
|
-
if (user.role ===
|
|
67867
|
-
await usersService.patch(userId, { role:
|
|
68790
|
+
if (user.role === ROLES26.SUPERADMIN) continue;
|
|
68791
|
+
await usersService.patch(userId, { role: ROLES26.SUPERADMIN });
|
|
67868
68792
|
promotedCount++;
|
|
67869
68793
|
console.log(
|
|
67870
68794
|
`[RBAC] Bootstrap promoted user ${shortId32(userId)} (${user.email}) to superadmin`
|
|
@@ -68091,7 +69015,7 @@ import {
|
|
|
68091
69015
|
createTenantScopedDatabaseProxy,
|
|
68092
69016
|
formatPendingMigrationsMessage,
|
|
68093
69017
|
runWithSystemDatabaseScope as runWithSystemDatabaseScope2,
|
|
68094
|
-
runWithTenantDatabaseScope as
|
|
69018
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope8,
|
|
68095
69019
|
seedInitialData
|
|
68096
69020
|
} from "@agor/core/db";
|
|
68097
69021
|
import { extractDbFilePath } from "@agor/core/utils/path";
|
|
@@ -68348,7 +69272,7 @@ async function initializeDatabase(dbPath, options = {}) {
|
|
|
68348
69272
|
}
|
|
68349
69273
|
};
|
|
68350
69274
|
if (options.tenantId) {
|
|
68351
|
-
await
|
|
69275
|
+
await runWithTenantDatabaseScope8(scopedDb, options.tenantId, runInitialDataSetup);
|
|
68352
69276
|
} else {
|
|
68353
69277
|
await runWithSystemDatabaseScope2(scopedDb, "database initialization", runInitialDataSetup);
|
|
68354
69278
|
}
|
|
@@ -68484,15 +69408,15 @@ import path17 from "path";
|
|
|
68484
69408
|
import {
|
|
68485
69409
|
getAgorHome as getAgorHome2,
|
|
68486
69410
|
resolveExecutorHeartbeatConfig as resolveExecutorHeartbeatConfig3,
|
|
68487
|
-
resolveMultiTenancyConfig as
|
|
69411
|
+
resolveMultiTenancyConfig as resolveMultiTenancyConfig7
|
|
68488
69412
|
} from "@agor/core/config";
|
|
68489
69413
|
import {
|
|
68490
69414
|
MessagesRepository as MessagesRepository3,
|
|
68491
|
-
runWithTenantDatabaseScope as
|
|
68492
|
-
SessionRepository as
|
|
69415
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope11,
|
|
69416
|
+
SessionRepository as SessionRepository11,
|
|
68493
69417
|
shortId as shortId37
|
|
68494
69418
|
} from "@agor/core/db";
|
|
68495
|
-
import { SessionStatus as SessionStatus10, TaskStatus as TaskStatus8 } from "@agor/core/types";
|
|
69419
|
+
import { isTerminalTaskStatus as isTerminalTaskStatus3, SessionStatus as SessionStatus10, TaskStatus as TaskStatus8 } from "@agor/core/types";
|
|
68496
69420
|
|
|
68497
69421
|
// src/services/executor-heartbeat-supervisor.ts
|
|
68498
69422
|
import { shortId as shortId33 } from "@agor/core/db";
|
|
@@ -68566,8 +69490,9 @@ import { ENVIRONMENT as ENVIRONMENT2 } from "@agor/core/config";
|
|
|
68566
69490
|
import {
|
|
68567
69491
|
BranchRepository as BranchRepository17,
|
|
68568
69492
|
getHiddenTenantId as getHiddenTenantId2,
|
|
69493
|
+
runWithoutTenantDatabaseScope as runWithoutTenantDatabaseScope4,
|
|
68569
69494
|
runWithSystemDatabaseScope as runWithSystemDatabaseScope3,
|
|
68570
|
-
runWithTenantDatabaseScope as
|
|
69495
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope9,
|
|
68571
69496
|
shortId as shortId34
|
|
68572
69497
|
} from "@agor/core/db";
|
|
68573
69498
|
import { NotFoundError as NotFoundError7 } from "@agor/core/utils/errors";
|
|
@@ -68655,15 +69580,17 @@ var HealthMonitor = class {
|
|
|
68655
69580
|
startMonitoring(branchId, params = this.branchParams.get(branchId)) {
|
|
68656
69581
|
this.stopMonitoring(branchId);
|
|
68657
69582
|
if (params) this.branchParams.set(branchId, params);
|
|
68658
|
-
|
|
68659
|
-
|
|
68660
|
-
this.checkHealth(branchId);
|
|
68661
|
-
const interval = setInterval(() => {
|
|
69583
|
+
runWithoutTenantDatabaseScope4(() => {
|
|
69584
|
+
setTimeout(() => {
|
|
68662
69585
|
if (this.isShuttingDown) return;
|
|
68663
69586
|
this.checkHealth(branchId);
|
|
68664
|
-
|
|
68665
|
-
|
|
68666
|
-
|
|
69587
|
+
const interval = setInterval(() => {
|
|
69588
|
+
if (this.isShuttingDown) return;
|
|
69589
|
+
this.checkHealth(branchId);
|
|
69590
|
+
}, ENVIRONMENT2.HEALTH_CHECK_INTERVAL_MS);
|
|
69591
|
+
this.intervals.set(branchId, interval);
|
|
69592
|
+
}, ENVIRONMENT2.STARTUP_GRACE_PERIOD_MS);
|
|
69593
|
+
});
|
|
68667
69594
|
}
|
|
68668
69595
|
/**
|
|
68669
69596
|
* Stop monitoring a branch's health
|
|
@@ -68776,7 +69703,7 @@ var HealthMonitor = class {
|
|
|
68776
69703
|
activeCount += 1;
|
|
68777
69704
|
};
|
|
68778
69705
|
if (this.db) {
|
|
68779
|
-
await
|
|
69706
|
+
await runWithTenantDatabaseScope9(this.db, ref.tenantId, loadAndStart);
|
|
68780
69707
|
} else {
|
|
68781
69708
|
await loadAndStart();
|
|
68782
69709
|
}
|
|
@@ -68802,7 +69729,7 @@ var HealthMonitor = class {
|
|
|
68802
69729
|
};
|
|
68803
69730
|
if (this.tenantId) {
|
|
68804
69731
|
if (!this.db) return findRefs(this.tenantId);
|
|
68805
|
-
return
|
|
69732
|
+
return runWithTenantDatabaseScope9(this.db, this.tenantId, () => findRefs(this.tenantId));
|
|
68806
69733
|
}
|
|
68807
69734
|
if (!this.db) return findRefs();
|
|
68808
69735
|
return runWithSystemDatabaseScope3(
|
|
@@ -68846,11 +69773,11 @@ import {
|
|
|
68846
69773
|
generateId as generateId9,
|
|
68847
69774
|
inArray as inArray3,
|
|
68848
69775
|
insert as insert3,
|
|
68849
|
-
isPostgresDatabase as
|
|
69776
|
+
isPostgresDatabase as isPostgresDatabase9,
|
|
68850
69777
|
kbDocumentUnits as kbDocumentUnits3,
|
|
68851
69778
|
kbDocumentVersions as kbDocumentVersions2,
|
|
68852
69779
|
kbEmbeddingSpaces,
|
|
68853
|
-
runWithTenantDatabaseScope as
|
|
69780
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope10,
|
|
68854
69781
|
select as select7,
|
|
68855
69782
|
shortId as shortId35,
|
|
68856
69783
|
sql as sql7,
|
|
@@ -69102,7 +70029,7 @@ var KnowledgeEmbeddingIndexer = class {
|
|
|
69102
70029
|
}
|
|
69103
70030
|
async tick() {
|
|
69104
70031
|
if (this.options.tenantId) {
|
|
69105
|
-
return
|
|
70032
|
+
return runWithTenantDatabaseScope10(this.db, this.options.tenantId, () => this.tickInScope());
|
|
69106
70033
|
}
|
|
69107
70034
|
return this.tickInScope();
|
|
69108
70035
|
}
|
|
@@ -69124,7 +70051,7 @@ var KnowledgeEmbeddingIndexer = class {
|
|
|
69124
70051
|
const semantic = config2.knowledge?.semantic_search;
|
|
69125
70052
|
if (semantic?.enabled !== true) return this.idle();
|
|
69126
70053
|
if (semantic.indexing?.paused === true) return this.idle();
|
|
69127
|
-
if (!
|
|
70054
|
+
if (!isPostgresDatabase9(this.db)) return this.idle();
|
|
69128
70055
|
const provider = semantic.provider ?? "openai";
|
|
69129
70056
|
if (provider !== "openai") return this.idle();
|
|
69130
70057
|
const apiKey = await this.variables.getPlain(
|
|
@@ -69357,7 +70284,7 @@ async function readAndClearSentinel() {
|
|
|
69357
70284
|
}
|
|
69358
70285
|
}
|
|
69359
70286
|
function startupTenantParams(config2) {
|
|
69360
|
-
const multiTenancy =
|
|
70287
|
+
const multiTenancy = resolveMultiTenancyConfig7(config2);
|
|
69361
70288
|
return {
|
|
69362
70289
|
tenant: {
|
|
69363
70290
|
tenant_id: multiTenancy.static_tenant_id,
|
|
@@ -69366,7 +70293,7 @@ function startupTenantParams(config2) {
|
|
|
69366
70293
|
};
|
|
69367
70294
|
}
|
|
69368
70295
|
async function runStartupTenantDatabaseScope(ctx, work) {
|
|
69369
|
-
return
|
|
70296
|
+
return runWithTenantDatabaseScope11(ctx.db, startupTenantParams(ctx.config).tenant.tenant_id, work);
|
|
69370
70297
|
}
|
|
69371
70298
|
async function cleanupOrphanStatuses(ctx) {
|
|
69372
70299
|
return runStartupTenantDatabaseScope(ctx, () => cleanupOrphanStatusesInTenantScope(ctx));
|
|
@@ -69459,18 +70386,36 @@ async function cleanupOrphanStatusesInTenantScope(ctx) {
|
|
|
69459
70386
|
}
|
|
69460
70387
|
}
|
|
69461
70388
|
}
|
|
69462
|
-
const
|
|
70389
|
+
const bootInterruptedTaskIds = /* @__PURE__ */ new Set([
|
|
70390
|
+
...orphanedTasks.map((t) => t.task_id),
|
|
70391
|
+
...queuedTasks.map((t) => t.task_id)
|
|
70392
|
+
]);
|
|
70393
|
+
const idleNotReadyResult = await sessionsService.find({
|
|
69463
70394
|
query: { status: SessionStatus10.IDLE, ready_for_prompt: false, $limit: 1e3 },
|
|
69464
70395
|
...startupParams
|
|
69465
70396
|
});
|
|
69466
|
-
const stuckIdleSessions =
|
|
69467
|
-
|
|
69468
|
-
|
|
69469
|
-
|
|
69470
|
-
|
|
69471
|
-
` \u2713 Unblocked stuck-idle session ${shortId37(session.session_id)} (ready_for_prompt was false)`
|
|
69472
|
-
);
|
|
70397
|
+
const stuckIdleSessions = [];
|
|
70398
|
+
for (const session of idleNotReadyResult.data) {
|
|
70399
|
+
const latestTaskId = session.tasks?.at(-1);
|
|
70400
|
+
if (!latestTaskId) {
|
|
70401
|
+
continue;
|
|
69473
70402
|
}
|
|
70403
|
+
let wasInterrupted = bootInterruptedTaskIds.has(latestTaskId);
|
|
70404
|
+
if (!wasInterrupted) {
|
|
70405
|
+
try {
|
|
70406
|
+
const latestTask = await tasksService.get(latestTaskId, startupParams);
|
|
70407
|
+
wasInterrupted = !isTerminalTaskStatus3(latestTask.status);
|
|
70408
|
+
} catch {
|
|
70409
|
+
}
|
|
70410
|
+
}
|
|
70411
|
+
if (!wasInterrupted) {
|
|
70412
|
+
continue;
|
|
70413
|
+
}
|
|
70414
|
+
stuckIdleSessions.push(session);
|
|
70415
|
+
await app.service("sessions").patch(session.session_id, { ready_for_prompt: true }, startupParams);
|
|
70416
|
+
startupDebug(
|
|
70417
|
+
` \u2713 Unblocked stuck-idle session ${shortId37(session.session_id)} (ready_for_prompt was false, latest task interrupted)`
|
|
70418
|
+
);
|
|
69474
70419
|
}
|
|
69475
70420
|
const cleanupParts = [
|
|
69476
70421
|
`${orphanedTasks.length} orphaned task(s) stopped`,
|
|
@@ -69522,7 +70467,7 @@ async function injectRestartNoticesInTenantScope(ctx, cleanupResult) {
|
|
|
69522
70467
|
lastOrphanedTaskBySession.set(sid, task);
|
|
69523
70468
|
}
|
|
69524
70469
|
}
|
|
69525
|
-
const sessionRepo = new
|
|
70470
|
+
const sessionRepo = new SessionRepository11(db);
|
|
69526
70471
|
const messageRepo = new MessagesRepository3(db);
|
|
69527
70472
|
for (const sessionId of affectedSessionIds) {
|
|
69528
70473
|
try {
|
|
@@ -69620,7 +70565,7 @@ async function startup(ctx) {
|
|
|
69620
70565
|
terminalsService
|
|
69621
70566
|
} = ctx;
|
|
69622
70567
|
const orphanCleanupResult = await cleanupOrphanStatuses(ctx);
|
|
69623
|
-
const startupMultiTenancy =
|
|
70568
|
+
const startupMultiTenancy = resolveMultiTenancyConfig7(config2);
|
|
69624
70569
|
const healthMonitor = new HealthMonitor(app, {
|
|
69625
70570
|
defaultParams: startupTenantParams(config2),
|
|
69626
70571
|
db,
|
|
@@ -69670,7 +70615,7 @@ async function startup(ctx) {
|
|
|
69670
70615
|
}
|
|
69671
70616
|
let schedulerService = null;
|
|
69672
70617
|
if (svcEnabled("scheduler")) {
|
|
69673
|
-
const multiTenancy =
|
|
70618
|
+
const multiTenancy = resolveMultiTenancyConfig7(config2);
|
|
69674
70619
|
schedulerService = new SchedulerService(db, app, {
|
|
69675
70620
|
tickInterval: 3e4,
|
|
69676
70621
|
// 30 seconds
|
|
@@ -69806,7 +70751,7 @@ import {
|
|
|
69806
70751
|
eq as eq7,
|
|
69807
70752
|
gte as gte3,
|
|
69808
70753
|
lt,
|
|
69809
|
-
runWithTenantDatabaseScope as
|
|
70754
|
+
runWithTenantDatabaseScope as runWithTenantDatabaseScope12,
|
|
69810
70755
|
select as select8,
|
|
69811
70756
|
sessions as sessions2,
|
|
69812
70757
|
tasks as tasks2
|
|
@@ -69897,7 +70842,7 @@ async function flushOpenSourceTelemetryUsageSummary(db) {
|
|
|
69897
70842
|
}
|
|
69898
70843
|
function startOpenSourceTelemetryUsageSummaryInterval(db, options) {
|
|
69899
70844
|
const run = () => {
|
|
69900
|
-
|
|
70845
|
+
runWithTenantDatabaseScope12(
|
|
69901
70846
|
db,
|
|
69902
70847
|
options.tenantId,
|
|
69903
70848
|
() => flushOpenSourceTelemetryUsageSummary(db)
|
|
@@ -69920,9 +70865,11 @@ init_spawn_executor();
|
|
|
69920
70865
|
|
|
69921
70866
|
// src/widgets/index.ts
|
|
69922
70867
|
init_env_vars();
|
|
70868
|
+
init_gateway_token();
|
|
69923
70869
|
init_registry();
|
|
69924
70870
|
function registerAllWidgets() {
|
|
69925
70871
|
registerEnvVarsWidget();
|
|
70872
|
+
registerGatewayTokenWidget();
|
|
69926
70873
|
}
|
|
69927
70874
|
|
|
69928
70875
|
// src/index.ts
|
|
@@ -69960,7 +70907,7 @@ async function startDaemon(options) {
|
|
|
69960
70907
|
process.env.GIT_TERMINAL_PROMPT = "0";
|
|
69961
70908
|
process.env.GIT_ASKPASS = "echo";
|
|
69962
70909
|
let config2 = options?.config ? options.config : options?.configPath ? await loadConfigFromFile(options.configPath) : await loadConfig16();
|
|
69963
|
-
const multiTenancy =
|
|
70910
|
+
const multiTenancy = resolveMultiTenancyConfig8(config2);
|
|
69964
70911
|
console.log(
|
|
69965
70912
|
`\u{1F3E2} Multi-tenancy: mode=${multiTenancy.mode} tenant=${multiTenancy.mode === "static" ? multiTenancy.static_tenant_id : "auth-resolved"}`
|
|
69966
70913
|
);
|
|
@@ -70027,14 +70974,14 @@ async function startDaemon(options) {
|
|
|
70027
70974
|
if (context.method === "patch") {
|
|
70028
70975
|
const data = context.data;
|
|
70029
70976
|
if (data?.password) return context;
|
|
70030
|
-
throw new
|
|
70977
|
+
throw new Forbidden27("Password change required. Please update your password.", {
|
|
70031
70978
|
code: "PASSWORD_CHANGE_REQUIRED",
|
|
70032
70979
|
user_id: freshUser.user_id
|
|
70033
70980
|
});
|
|
70034
70981
|
}
|
|
70035
70982
|
}
|
|
70036
70983
|
}
|
|
70037
|
-
throw new
|
|
70984
|
+
throw new Forbidden27("Password change required. Please update your password.", {
|
|
70038
70985
|
code: "PASSWORD_CHANGE_REQUIRED",
|
|
70039
70986
|
user_id: freshUser.user_id
|
|
70040
70987
|
});
|
|
@@ -70381,14 +71328,14 @@ async function startDaemon(options) {
|
|
|
70381
71328
|
});
|
|
70382
71329
|
runPostStartJob("cli-watcher-rehydrate", async () => {
|
|
70383
71330
|
const { rehydrateCliWatchers: rehydrateCliWatchers2, scanCliWatcherRehydrateSessions: scanCliWatcherRehydrateSessions2 } = await Promise.resolve().then(() => (init_claude_cli_integration(), claude_cli_integration_exports));
|
|
70384
|
-
const cliSessions = await
|
|
71331
|
+
const cliSessions = await runWithTenantDatabaseScope13(
|
|
70385
71332
|
db,
|
|
70386
71333
|
multiTenancy.static_tenant_id,
|
|
70387
71334
|
() => scanCliWatcherRehydrateSessions2(app)
|
|
70388
71335
|
);
|
|
70389
71336
|
await rehydrateCliWatchers2(
|
|
70390
71337
|
app,
|
|
70391
|
-
(branchId) =>
|
|
71338
|
+
(branchId) => runWithTenantDatabaseScope13(db, multiTenancy.static_tenant_id, async () => {
|
|
70392
71339
|
try {
|
|
70393
71340
|
const branch = await app.service("branches").get(branchId);
|
|
70394
71341
|
return branch?.path ?? null;
|