@bevel-software/platform-core-backend 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/core-ports.d.ts +11 -1
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts +3 -3
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +46 -13
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +12 -2
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +137 -24
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +46 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +99 -13
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +16 -12
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +28 -13
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +42 -12
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +67 -10
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +214 -35
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +17 -16
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +7 -3
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +8 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.js +8 -2
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +38 -6
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +39 -6
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/index.d.ts +2 -1
- package/dist/modules/agent-instructions/index.d.ts.map +1 -1
- package/dist/modules/agent-instructions/index.js +2 -1
- package/dist/modules/agent-instructions/index.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +115 -0
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -0
- package/dist/modules/agent-instructions/shared-file-rules.js +272 -0
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +4 -2
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/auth/account-admission.d.ts +58 -7
- package/dist/modules/auth/account-admission.d.ts.map +1 -1
- package/dist/modules/auth/account-admission.js +44 -1
- package/dist/modules/auth/account-admission.js.map +1 -1
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +57 -16
- package/dist/modules/auth/account-erasure.service.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts +1 -1
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +61 -5
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +1 -1
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +18 -7
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +8 -0
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +70 -2
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +197 -18
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +7 -2
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts +20 -2
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +66 -35
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/connection.d.ts +16 -0
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +117 -0
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +337 -120
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +116 -58
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts +8 -0
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +271 -1
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
- package/dist/modules/kb-fs/branch-name.js +12 -2
- package/dist/modules/kb-fs/branch-name.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +11 -16
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +79 -0
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.d.ts +2 -2
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.js +14 -3
- package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
- package/dist/modules/kb-sync/sync-auth.d.ts +3 -1
- package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -1
- package/dist/modules/kb-sync/sync-auth.js +1 -1
- package/dist/modules/kb-sync/sync-auth.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +21 -6
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +8 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +38 -8
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +3 -2
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-records.store.js +7 -4
- package/dist/modules/plugins/join-request-records.store.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +8 -3
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +52 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +207 -19
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +90 -18
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.contract.js +4 -1
- package/dist/modules/skills/skills.contract.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +98 -9
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +239 -37
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts +7 -0
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +71 -7
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +12 -4
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +3 -3
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +16 -7
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +34 -12
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +2 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +25 -4
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +3 -2
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-helpers/validate-token.js +1 -1
- package/dist/modules/tool-helpers/validate-token.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +42 -31
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +80 -0
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -0
- package/dist/modules/tool-registry/description-length.js +108 -0
- package/dist/modules/tool-registry/description-length.js.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +98 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.js +81 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +75 -35
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +24 -0
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +30 -0
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +94 -0
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +193 -3
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +38 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +60 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +7 -3
- package/dist/modules/workflow/recovery-bot.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +12 -3
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/workflow-hooks.d.ts +54 -32
- package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -1
- package/dist/modules/workflow/workflow-hooks.js +16 -1
- package/dist/modules/workflow/workflow-hooks.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +60 -6
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +115 -5
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts +94 -0
- package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -0
- package/dist/modules/workspace/agent-access.gate.js +123 -0
- package/dist/modules/workspace/agent-access.gate.js.map +1 -0
- package/dist/modules/workspace/agent-upload.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-upload.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.routes.js +210 -0
- package/dist/modules/workspace/agent-upload.routes.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +284 -0
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.store.js +553 -0
- package/dist/modules/workspace/agent-upload.store.js.map +1 -0
- package/dist/modules/workspace/routine-write-policy.d.ts +5 -6
- package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
- package/dist/modules/workspace/routine-write-policy.js +5 -6
- package/dist/modules/workspace/routine-write-policy.js.map +1 -1
- package/dist/modules/workspace/session-sink.d.ts +5 -5
- package/dist/modules/workspace/set-aside-clone.d.ts +46 -0
- package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -0
- package/dist/modules/workspace/set-aside-clone.js +92 -0
- package/dist/modules/workspace/set-aside-clone.js.map +1 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +59 -9
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +65 -24
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +3 -3
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +40 -0
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +46 -4
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/upload-limits.d.ts +13 -0
- package/dist/modules/workspace/upload-limits.d.ts.map +1 -0
- package/dist/modules/workspace/upload-limits.js +13 -0
- package/dist/modules/workspace/upload-limits.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +93 -4
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +128 -5
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +316 -58
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +13 -11
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +791 -198
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +0 -6
- package/dist/modules/workspace/write-denial.d.ts.map +1 -1
- package/dist/modules/workspace/write-denial.js +0 -6
- package/dist/modules/workspace/write-denial.js.map +1 -1
- package/dist/modules/workspace/zip-entry-rules.d.ts +114 -0
- package/dist/modules/workspace/zip-entry-rules.d.ts.map +1 -0
- package/dist/modules/workspace/zip-entry-rules.js +154 -0
- package/dist/modules/workspace/zip-entry-rules.js.map +1 -0
- package/dist/modules/write-access/write-access.d.ts +60 -0
- package/dist/modules/write-access/write-access.d.ts.map +1 -0
- package/dist/modules/write-access/write-access.js +129 -0
- package/dist/modules/write-access/write-access.js.map +1 -0
- package/dist/shared/column-crypto.d.ts +194 -0
- package/dist/shared/column-crypto.d.ts.map +1 -0
- package/dist/shared/column-crypto.js +144 -0
- package/dist/shared/column-crypto.js.map +1 -0
- package/dist/shared/domain-errors.d.ts +25 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +28 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +20 -0
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +26 -0
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/token-crypto.d.ts.map +1 -1
- package/dist/shared/token-crypto.js +25 -1
- package/dist/shared/token-crypto.js.map +1 -1
- package/dist/tenancy/static-tenant-source.d.ts +0 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +1 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/dist/tenancy/tenant-secrets.d.ts +5 -1
- package/dist/tenancy/tenant-secrets.d.ts.map +1 -1
- package/dist/tenancy/tenant-secrets.js +4 -0
- package/dist/tenancy/tenant-secrets.js.map +1 -1
- package/kb-template/AGENTS.md +2 -0
- package/migrations/0014_change_request_closed_reason.sql +1 -0
- package/migrations/0015_account_deactivation.sql +3 -0
- package/migrations/0016_pii_encryption.sql +20 -0
- package/migrations/meta/0014_snapshot.json +2265 -0
- package/migrations/meta/0015_snapshot.json +2271 -0
- package/migrations/meta/0016_snapshot.json +2327 -0
- package/migrations/meta/_journal.json +21 -0
- package/package.json +3 -3
- package/src/__tests__/kb-layout-config.test.ts +0 -2
- package/src/__tests__/retired-settings.test.ts +96 -0
- package/src/core/__tests__/gated-boot.test.ts +132 -0
- package/src/core/__tests__/lifecycle.test.ts +241 -5
- package/src/core/__tests__/set-aside-root-is-one-place.test.ts +63 -0
- package/src/core/core-ports.ts +11 -1
- package/src/core/create-core-server.ts +52 -16
- package/src/core/create-core-services.ts +156 -24
- package/src/core/lifecycle.ts +117 -13
- package/src/core-config.ts +31 -14
- package/src/index.ts +30 -1
- package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +385 -0
- package/src/modules/access/__tests__/access-control.prospective.test.ts +94 -18
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +6 -7
- package/src/modules/access/__tests__/users-db-double.ts +21 -12
- package/src/modules/access/access-control.interface.ts +43 -12
- package/src/modules/access/access-control.service.ts +234 -36
- package/src/modules/access/access.routes.ts +17 -16
- package/src/modules/access/directory-sync-bot.ts +7 -3
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +11 -5
- package/src/modules/agent-instructions/__tests__/compose.test.ts +19 -10
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +238 -0
- package/src/modules/agent-instructions/agent-instructions.routes.ts +12 -2
- package/src/modules/agent-instructions/compose.ts +50 -7
- package/src/modules/agent-instructions/index.ts +12 -0
- package/src/modules/agent-instructions/shared-file-rules.ts +314 -0
- package/src/modules/audit/agent-audit.service.ts +4 -2
- package/src/modules/auth/__tests__/account-deactivation.test.ts +253 -0
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +6 -2
- package/src/modules/auth/__tests__/account.routes.test.ts +87 -6
- package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
- package/src/modules/auth/__tests__/auth.service.test.ts +4 -2
- package/src/modules/auth/account-admission.ts +78 -9
- package/src/modules/auth/account-erasure.service.ts +69 -18
- package/src/modules/auth/account.routes.ts +63 -7
- package/src/modules/auth/auth.middleware.ts +19 -8
- package/src/modules/auth/auth.routes.ts +8 -0
- package/src/modules/auth/auth.service.ts +206 -23
- package/src/modules/auth/oidc-auth-provider.ts +7 -2
- package/src/modules/code-mode/__tests__/chain-runtime.e2e.test.ts +335 -0
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +46 -2
- package/src/modules/code-mode/code-mode.tool.ts +80 -34
- package/src/modules/database/__tests__/connection.test.ts +12 -0
- package/src/modules/database/__tests__/pii-backfill.pg.test.ts +561 -0
- package/src/modules/database/connection.ts +117 -0
- package/src/modules/database/core-schema.ts +116 -58
- package/src/modules/database/migrate.ts +353 -1
- package/src/modules/kb-fs/__tests__/branch-name.test.ts +10 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +123 -0
- package/src/modules/kb-fs/branch-name.ts +14 -1
- package/src/modules/kb-fs/repo-path.ts +85 -0
- package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +26 -1
- package/src/modules/kb-sync/kb-sync.routes.ts +14 -4
- package/src/modules/kb-sync/sync-auth.ts +2 -2
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -3
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +4 -3
- package/src/modules/mcp/__tests__/mcp.service.test.ts +115 -13
- package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
- package/src/modules/mcp/mcp.service.ts +46 -7
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
- package/src/modules/plugins/join-request-records.store.ts +7 -4
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
- package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +91 -58
- package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +25 -8
- package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +14 -11
- package/src/modules/settings/__tests__/setup.routes.repository-change.test.ts +390 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +3 -0
- package/src/modules/settings/deployment-settings.service.ts +8 -3
- package/src/modules/settings/setup.routes.ts +263 -19
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +2 -1
- package/src/modules/skills/__tests__/branch-skills.tools.test.ts +218 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +229 -5
- package/src/modules/skills/skills.contract.ts +91 -18
- package/src/modules/skills/skills.service.ts +278 -41
- package/src/modules/skills/skills.tools.ts +80 -8
- package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +14 -1
- package/src/modules/tool-auth/external-api-key.service.ts +12 -4
- package/src/modules/tool-auth/internal-token.service.ts +3 -3
- package/src/modules/tool-auth/tool-auth.middleware.ts +34 -11
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +5 -3
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -3
- package/src/modules/tool-helpers/__tests__/validate-token.test.ts +11 -1
- package/src/modules/tool-helpers/tool-handler.ts +24 -4
- package/src/modules/tool-helpers/tool.contract.ts +3 -2
- package/src/modules/tool-helpers/validate-token.ts +1 -1
- package/src/modules/tool-manuals/tool-manuals.tools.ts +44 -31
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +378 -0
- package/src/modules/tool-registry/description-length.ts +111 -0
- package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +111 -0
- package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +25 -0
- package/src/modules/workflow/__tests__/workflow.service.repository-replaced.test.ts +249 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +264 -6
- package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
- package/src/modules/workflow/agent-tools/workflow.tools.ts +86 -35
- package/src/modules/workflow/file-lock.service.ts +31 -0
- package/src/modules/workflow/git/__tests__/git.service.fileBytesAtCommit.test.ts +260 -0
- package/src/modules/workflow/git/__tests__/git.service.prFetchFailure.test.ts +170 -0
- package/src/modules/workflow/git/git.service.ts +215 -1
- package/src/modules/workflow/pending-commits.service.ts +64 -2
- package/src/modules/workflow/recovery-bot.ts +7 -3
- package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +4 -0
- package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +25 -3
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
- package/src/modules/workflow/workflow-hooks.ts +64 -26
- package/src/modules/workflow/workflow.service.ts +125 -5
- package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
- package/src/modules/workspace/__tests__/agent-uploads.test.ts +1604 -0
- package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +394 -0
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +2 -1
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -1
- package/src/modules/workspace/__tests__/set-aside-clone.test.ts +52 -0
- package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +305 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -0
- package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +156 -0
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +147 -0
- package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +402 -0
- package/src/modules/workspace/__tests__/workspace.service.test.ts +77 -9
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +41 -39
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +2 -3
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +766 -56
- package/src/modules/workspace/agent-access.gate.ts +164 -0
- package/src/modules/workspace/agent-upload.routes.ts +214 -0
- package/src/modules/workspace/agent-upload.store.ts +668 -0
- package/src/modules/workspace/routine-write-policy.ts +5 -6
- package/src/modules/workspace/session-sink.ts +5 -5
- package/src/modules/workspace/set-aside-clone.ts +96 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +86 -0
- package/src/modules/workspace/startup/kb-startup-runner.ts +115 -34
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +9 -9
- package/src/modules/workspace/startup/steps/seed-tree.ts +3 -6
- package/src/modules/workspace/startup/steps/template-source.ts +53 -5
- package/src/modules/workspace/upload-limits.ts +12 -0
- package/src/modules/workspace/workspace.routes.ts +96 -5
- package/src/modules/workspace/workspace.service.ts +319 -61
- package/src/modules/workspace/workspace.tools.ts +894 -214
- package/src/modules/workspace/write-denial.ts +0 -8
- package/src/modules/workspace/zip-entry-rules.ts +173 -0
- package/src/modules/write-access/__tests__/write-access.test.ts +248 -0
- package/src/modules/write-access/write-access.ts +153 -0
- package/src/shared/__tests__/column-crypto.test.ts +217 -0
- package/src/shared/column-crypto.ts +218 -0
- package/src/shared/domain-errors.ts +31 -0
- package/src/shared/git.contract.ts +27 -0
- package/src/shared/token-crypto.ts +28 -1
- package/src/tenancy/__tests__/static-tenant-source.test.ts +4 -1
- package/src/tenancy/static-tenant-source.ts +1 -3
- package/src/tenancy/tenant-secrets.ts +5 -1
- package/dist/modules/workflow/session-ontology.policy.d.ts +0 -52
- package/dist/modules/workflow/session-ontology.policy.d.ts.map +0 -1
- package/dist/modules/workflow/session-ontology.policy.js +0 -62
- package/dist/modules/workflow/session-ontology.policy.js.map +0 -1
- package/dist/modules/workflow/session-ontology.service.d.ts +0 -105
- package/dist/modules/workflow/session-ontology.service.d.ts.map +0 -1
- package/dist/modules/workflow/session-ontology.service.js +0 -147
- package/dist/modules/workflow/session-ontology.service.js.map +0 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts +0 -114
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +0 -1
- package/dist/modules/workspace/session-ontology.gate.js +0 -161
- package/dist/modules/workspace/session-ontology.gate.js.map +0 -1
- package/dist/shared/kb-layout.d.ts +0 -39
- package/dist/shared/kb-layout.d.ts.map +0 -1
- package/dist/shared/kb-layout.js +0 -103
- package/dist/shared/kb-layout.js.map +0 -1
- package/dist/shared/kb-layout.test.d.ts +0 -2
- package/dist/shared/kb-layout.test.d.ts.map +0 -1
- package/dist/shared/kb-layout.test.js +0 -75
- package/dist/shared/kb-layout.test.js.map +0 -1
- package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +0 -62
- package/src/modules/workflow/__tests__/session-ontology.service.test.ts +0 -201
- package/src/modules/workflow/session-ontology.policy.ts +0 -70
- package/src/modules/workflow/session-ontology.service.ts +0 -183
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +0 -239
- package/src/modules/workspace/session-ontology.gate.ts +0 -191
- package/src/shared/kb-layout.test.ts +0 -98
- package/src/shared/kb-layout.ts +0 -102
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent access gate — the per-call seam every gated agent tool runs through.
|
|
3
|
+
*
|
|
4
|
+
* It decides nothing of its own. It calls the hooks a DEPLOYMENT registered
|
|
5
|
+
* (`WorkflowHooks`): one before an agent read of a knowledge-base path, one
|
|
6
|
+
* before an agent write. A hook that throws refuses the operation and the
|
|
7
|
+
* caller sees that hook's own message and status. With no hook registered —
|
|
8
|
+
* every core-only deployment — nothing is refused and nothing is recorded, and
|
|
9
|
+
* a call that carries no `sessionId` is an ordinary call.
|
|
10
|
+
*
|
|
11
|
+
* The gate owns exactly two skips, and they are the ones core can decide on
|
|
12
|
+
* its own identity model: the caller is not an agent (a person in the app, a
|
|
13
|
+
* browser JWT), or the caller is the recovery/merge bot. Everything else —
|
|
14
|
+
* which paths matter, whether a session id is required, what the refusal says
|
|
15
|
+
* — belongs to the hook.
|
|
16
|
+
*
|
|
17
|
+
* Lives beside `assertCanRead` in the workspace tools; it does NOT touch
|
|
18
|
+
* `acquireLock` (that gate is protected-branch-only, and this one applies on
|
|
19
|
+
* all branches and to reads).
|
|
20
|
+
*/
|
|
21
|
+
import type { JsonSchema } from '../tool-registry/tool.contract.js';
|
|
22
|
+
import type { ToolContext } from '../tool-helpers/tool.contract.js';
|
|
23
|
+
import type { WorkflowHooks } from '../workflow/workflow-hooks.js';
|
|
24
|
+
/**
|
|
25
|
+
* What the `sessionId` input says on its own, before any deployment note. The
|
|
26
|
+
* id is the CONVERSATION's id: the in-process agent supplies its thread id
|
|
27
|
+
* automatically (via its per-run internal token), and an external agent mints
|
|
28
|
+
* one with `start_session` and passes it explicitly, because the MCP proxy is
|
|
29
|
+
* a pure passthrough and never injects args. The same id is the one `ask`
|
|
30
|
+
* takes, so a run's reads and its questions share one conversation.
|
|
31
|
+
*/
|
|
32
|
+
export declare const SESSION_ID_DESCRIPTION = "This conversation's id, so the server can tell one run's calls apart. External agents (direct MCP calls and `call_tool_chain` alike): call the `start_session` tool ONCE at the very start of your work to obtain it, then pass that id here \u2014 the same id `ask` takes. The in-process agent runtime supplies this automatically (do not set it yourself).";
|
|
33
|
+
/**
|
|
34
|
+
* The `sessionId` input every gated tool declares. The description a MOUNTED
|
|
35
|
+
* tool ends up advertising is {@link ToolDescriptionNotes.sessionIdDescription},
|
|
36
|
+
* which appends whatever note the deployment registered; this constant is the
|
|
37
|
+
* unadorned default the notes are applied on top of.
|
|
38
|
+
*/
|
|
39
|
+
export declare const SESSION_ID_INPUT: JsonSchema;
|
|
40
|
+
/**
|
|
41
|
+
* The registration point for the wording a DEPLOYMENT wants agents to read
|
|
42
|
+
* about the gated tools — the counterpart of the hooks, which carry its rule.
|
|
43
|
+
* A deployment that refuses some calls can say so here; core registers
|
|
44
|
+
* nothing, so a core-only deployment advertises the plain descriptions.
|
|
45
|
+
*
|
|
46
|
+
* Registration may land AFTER the tools are mounted (an overlay registers from
|
|
47
|
+
* the tool-surface hook), so mounted tools subscribe with {@link onChange} and
|
|
48
|
+
* rewrite their own description and `sessionId` input when a note arrives.
|
|
49
|
+
*/
|
|
50
|
+
export declare class ToolDescriptionNotes {
|
|
51
|
+
private gatedTool;
|
|
52
|
+
private sessionId;
|
|
53
|
+
private readonly listeners;
|
|
54
|
+
/** Append `note` to the description of every gated tool. */
|
|
55
|
+
registerGatedToolNote(note: string): void;
|
|
56
|
+
/** Append `note` to the description of the `sessionId` input. */
|
|
57
|
+
registerSessionIdNote(note: string): void;
|
|
58
|
+
/** The registered gated-tool note, `''` when none is registered. */
|
|
59
|
+
gatedToolNote(): string;
|
|
60
|
+
/** The `sessionId` input's description, with any registered note appended. */
|
|
61
|
+
sessionIdDescription(): string;
|
|
62
|
+
/** Re-run `listener` whenever a note is registered (mounted tools re-describe). */
|
|
63
|
+
onChange(listener: () => void): void;
|
|
64
|
+
private announce;
|
|
65
|
+
}
|
|
66
|
+
/** The per-deployment configuration the gate needs, built once in the composition root. */
|
|
67
|
+
export interface AgentAccessGate {
|
|
68
|
+
/** Lowercased email of the recovery/merge bot, which never reaches the hooks. */
|
|
69
|
+
recoveryBotEmail: string;
|
|
70
|
+
/**
|
|
71
|
+
* The workflow lifecycle-hook registry holding the BLOCKING `agentRead` and
|
|
72
|
+
* `preWrite` hooks. Core registers none; a deployment registers against
|
|
73
|
+
* `workflowService.hooks` — pass that same instance here.
|
|
74
|
+
*/
|
|
75
|
+
hooks: WorkflowHooks;
|
|
76
|
+
/** The notes a deployment adds to what agents read about these tools. */
|
|
77
|
+
notes: ToolDescriptionNotes;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Tell the registered read hooks about an agent read of `wsPath`. A hook that
|
|
81
|
+
* throws refuses the read with its own message and status; with none
|
|
82
|
+
* registered this is a no-op.
|
|
83
|
+
*/
|
|
84
|
+
export declare function notifyAgentRead(gate: AgentAccessGate, ctx: ToolContext, branch: string, wsPath: string): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Ask the registered write hooks whether an agent write may proceed. A hook
|
|
87
|
+
* that throws refuses it with its own message and status; with none registered
|
|
88
|
+
* this is a no-op.
|
|
89
|
+
*
|
|
90
|
+
* `wsPath` is omitted by the one write-capable tool with no resolvable target,
|
|
91
|
+
* `execute_command` — the hook then decides at the coarser session level.
|
|
92
|
+
*/
|
|
93
|
+
export declare function assertAgentWriteAllowed(gate: AgentAccessGate, ctx: ToolContext, branch: string, wsPath?: string): Promise<void>;
|
|
94
|
+
//# sourceMappingURL=agent-access.gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-access.gate.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/agent-access.gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,oWAC2T,CAAC;AAE/V;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,UAG9B,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAEnD,4DAA4D;IAC5D,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKzC,iEAAiE;IACjE,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKzC,oEAAoE;IACpE,aAAa,IAAI,MAAM;IAIvB,8EAA8E;IAC9E,oBAAoB,IAAI,MAAM;IAI9B,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpC,OAAO,CAAC,QAAQ;CAGjB;AAED,2FAA2F;AAC3F,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,KAAK,EAAE,aAAa,CAAC;IACrB,yEAAyE;IACzE,KAAK,EAAE,oBAAoB,CAAC;CAC7B;AAwBD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent access gate — the per-call seam every gated agent tool runs through.
|
|
3
|
+
*
|
|
4
|
+
* It decides nothing of its own. It calls the hooks a DEPLOYMENT registered
|
|
5
|
+
* (`WorkflowHooks`): one before an agent read of a knowledge-base path, one
|
|
6
|
+
* before an agent write. A hook that throws refuses the operation and the
|
|
7
|
+
* caller sees that hook's own message and status. With no hook registered —
|
|
8
|
+
* every core-only deployment — nothing is refused and nothing is recorded, and
|
|
9
|
+
* a call that carries no `sessionId` is an ordinary call.
|
|
10
|
+
*
|
|
11
|
+
* The gate owns exactly two skips, and they are the ones core can decide on
|
|
12
|
+
* its own identity model: the caller is not an agent (a person in the app, a
|
|
13
|
+
* browser JWT), or the caller is the recovery/merge bot. Everything else —
|
|
14
|
+
* which paths matter, whether a session id is required, what the refusal says
|
|
15
|
+
* — belongs to the hook.
|
|
16
|
+
*
|
|
17
|
+
* Lives beside `assertCanRead` in the workspace tools; it does NOT touch
|
|
18
|
+
* `acquireLock` (that gate is protected-branch-only, and this one applies on
|
|
19
|
+
* all branches and to reads).
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* What the `sessionId` input says on its own, before any deployment note. The
|
|
23
|
+
* id is the CONVERSATION's id: the in-process agent supplies its thread id
|
|
24
|
+
* automatically (via its per-run internal token), and an external agent mints
|
|
25
|
+
* one with `start_session` and passes it explicitly, because the MCP proxy is
|
|
26
|
+
* a pure passthrough and never injects args. The same id is the one `ask`
|
|
27
|
+
* takes, so a run's reads and its questions share one conversation.
|
|
28
|
+
*/
|
|
29
|
+
export const SESSION_ID_DESCRIPTION = "This conversation's id, so the server can tell one run's calls apart. External agents (direct MCP calls and `call_tool_chain` alike): call the `start_session` tool ONCE at the very start of your work to obtain it, then pass that id here — the same id `ask` takes. The in-process agent runtime supplies this automatically (do not set it yourself).";
|
|
30
|
+
/**
|
|
31
|
+
* The `sessionId` input every gated tool declares. The description a MOUNTED
|
|
32
|
+
* tool ends up advertising is {@link ToolDescriptionNotes.sessionIdDescription},
|
|
33
|
+
* which appends whatever note the deployment registered; this constant is the
|
|
34
|
+
* unadorned default the notes are applied on top of.
|
|
35
|
+
*/
|
|
36
|
+
export const SESSION_ID_INPUT = {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: SESSION_ID_DESCRIPTION,
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The registration point for the wording a DEPLOYMENT wants agents to read
|
|
42
|
+
* about the gated tools — the counterpart of the hooks, which carry its rule.
|
|
43
|
+
* A deployment that refuses some calls can say so here; core registers
|
|
44
|
+
* nothing, so a core-only deployment advertises the plain descriptions.
|
|
45
|
+
*
|
|
46
|
+
* Registration may land AFTER the tools are mounted (an overlay registers from
|
|
47
|
+
* the tool-surface hook), so mounted tools subscribe with {@link onChange} and
|
|
48
|
+
* rewrite their own description and `sessionId` input when a note arrives.
|
|
49
|
+
*/
|
|
50
|
+
export class ToolDescriptionNotes {
|
|
51
|
+
gatedTool = '';
|
|
52
|
+
sessionId = '';
|
|
53
|
+
listeners = new Set();
|
|
54
|
+
/** Append `note` to the description of every gated tool. */
|
|
55
|
+
registerGatedToolNote(note) {
|
|
56
|
+
this.gatedTool = note;
|
|
57
|
+
this.announce();
|
|
58
|
+
}
|
|
59
|
+
/** Append `note` to the description of the `sessionId` input. */
|
|
60
|
+
registerSessionIdNote(note) {
|
|
61
|
+
this.sessionId = note;
|
|
62
|
+
this.announce();
|
|
63
|
+
}
|
|
64
|
+
/** The registered gated-tool note, `''` when none is registered. */
|
|
65
|
+
gatedToolNote() {
|
|
66
|
+
return this.gatedTool;
|
|
67
|
+
}
|
|
68
|
+
/** The `sessionId` input's description, with any registered note appended. */
|
|
69
|
+
sessionIdDescription() {
|
|
70
|
+
return SESSION_ID_DESCRIPTION + this.sessionId;
|
|
71
|
+
}
|
|
72
|
+
/** Re-run `listener` whenever a note is registered (mounted tools re-describe). */
|
|
73
|
+
onChange(listener) {
|
|
74
|
+
this.listeners.add(listener);
|
|
75
|
+
}
|
|
76
|
+
announce() {
|
|
77
|
+
for (const listener of this.listeners)
|
|
78
|
+
listener();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Only agent callers are gated; `session` (browser JWT) and humans are not. */
|
|
82
|
+
function agentSourceOf(ctx) {
|
|
83
|
+
return ctx.source === 'internal' || ctx.source === 'external' ? ctx.source : null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The hook context for this call, or `null` when the call never reaches a hook:
|
|
87
|
+
* the caller is a person rather than an agent, or is the recovery/merge bot,
|
|
88
|
+
* which legitimately spans everything.
|
|
89
|
+
*/
|
|
90
|
+
function operationContext(gate, ctx, branch, wsPath) {
|
|
91
|
+
const source = agentSourceOf(ctx);
|
|
92
|
+
if (source === null)
|
|
93
|
+
return null;
|
|
94
|
+
if (ctx.user.email.toLowerCase() === gate.recoveryBotEmail)
|
|
95
|
+
return null;
|
|
96
|
+
return { sessionId: ctx.sessionId, wsPath, branch, user: ctx.user, source };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Tell the registered read hooks about an agent read of `wsPath`. A hook that
|
|
100
|
+
* throws refuses the read with its own message and status; with none
|
|
101
|
+
* registered this is a no-op.
|
|
102
|
+
*/
|
|
103
|
+
export async function notifyAgentRead(gate, ctx, branch, wsPath) {
|
|
104
|
+
const op = operationContext(gate, ctx, branch, wsPath);
|
|
105
|
+
if (op === null)
|
|
106
|
+
return;
|
|
107
|
+
await gate.hooks.runAgentRead(op);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Ask the registered write hooks whether an agent write may proceed. A hook
|
|
111
|
+
* that throws refuses it with its own message and status; with none registered
|
|
112
|
+
* this is a no-op.
|
|
113
|
+
*
|
|
114
|
+
* `wsPath` is omitted by the one write-capable tool with no resolvable target,
|
|
115
|
+
* `execute_command` — the hook then decides at the coarser session level.
|
|
116
|
+
*/
|
|
117
|
+
export async function assertAgentWriteAllowed(gate, ctx, branch, wsPath) {
|
|
118
|
+
const op = operationContext(gate, ctx, branch, wsPath);
|
|
119
|
+
if (op === null)
|
|
120
|
+
return;
|
|
121
|
+
await gate.hooks.runPreWrite(op);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=agent-access.gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-access.gate.js","sourceRoot":"","sources":["../../../src/modules/workspace/agent-access.gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GACjC,4VAA4V,CAAC;AAE/V;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAe;IAC1C,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,sBAAsB;CACpC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,OAAO,oBAAoB;IACvB,SAAS,GAAG,EAAE,CAAC;IACf,SAAS,GAAG,EAAE,CAAC;IACN,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IAEnD,4DAA4D;IAC5D,qBAAqB,CAAC,IAAY;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAED,iEAAiE;IACjE,qBAAqB,CAAC,IAAY;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAED,oEAAoE;IACpE,aAAa;QACX,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,8EAA8E;IAC9E,oBAAoB;QAClB,OAAO,sBAAsB,GAAG,IAAI,CAAC,SAAS,CAAC;IACjD,CAAC;IAED,mFAAmF;IACnF,QAAQ,CAAC,QAAoB;QAC3B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAEO,QAAQ;QACd,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS;YAAE,QAAQ,EAAE,CAAC;IACpD,CAAC;CACF;AAgBD,gFAAgF;AAChF,SAAS,aAAa,CAAC,GAAgB;IACrC,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CACvB,IAAqB,EACrB,GAAgB,EAChB,MAAc,EACd,MAAe;IAEf,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAqB,EACrB,GAAgB,EAChB,MAAc,EACd,MAAc;IAEd,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO;IACxB,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,IAAqB,EACrB,GAAgB,EAChB,MAAc,EACd,MAAe;IAEf,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO;IACxB,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { AgentUploadStore } from './agent-upload.store.js';
|
|
3
|
+
/** The route's own prefix, under `/api`. One spelling, shared with the raw-body test below. */
|
|
4
|
+
export declare const AGENT_UPLOAD_ROUTE = "/agent/uploads/:token";
|
|
5
|
+
/**
|
|
6
|
+
* The same endpoint with the token in the `x-upload-token` HEADER instead of
|
|
7
|
+
* the path — the address `uploadUrl` is, without its last segment.
|
|
8
|
+
*
|
|
9
|
+
* Two spellings because a token in a URL is a credential in a place that keeps
|
|
10
|
+
* copies: an access log, a proxy log, a shell history, a `ps` listing of the
|
|
11
|
+
* `curl` that sent it. The path form is what `request_file_upload` answers,
|
|
12
|
+
* because one address an agent can paste into any client is the thing that
|
|
13
|
+
* makes this route usable at all; the header form is for a caller that would
|
|
14
|
+
* rather its credential not be written down on the way. Both reach the same
|
|
15
|
+
* handler and are judged identically — same token, same single use.
|
|
16
|
+
*/
|
|
17
|
+
export declare const AGENT_UPLOAD_HEADER_ROUTE = "/agent/uploads";
|
|
18
|
+
/**
|
|
19
|
+
* Whether `path` is the agent upload route, and so must reach its handler with
|
|
20
|
+
* the body still a STREAM.
|
|
21
|
+
*
|
|
22
|
+
* The app installs a global `express.json()`. It only claims a JSON
|
|
23
|
+
* content-type, but `curl --data-binary @file.zip -H 'content-type:
|
|
24
|
+
* application/json'` is a request an agent can and will make — and once the
|
|
25
|
+
* parser has drained the stream there are no bytes left to store, so the
|
|
26
|
+
* upload would answer "0 bytes received" for a file that was sent in full.
|
|
27
|
+
* Exempting the path is the same move `/api/sync` makes for its HMAC body, and
|
|
28
|
+
* for the same reason: whoever needs the exact bytes has to see them first.
|
|
29
|
+
*
|
|
30
|
+
* Case-insensitive, because Express routing is: `/api/Agent/uploads/x` reaches
|
|
31
|
+
* this router, and a check that said no would let the parser eat that body.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isAgentUploadRawBodyPath(path: string): boolean;
|
|
34
|
+
export interface AgentUploadRouteDeps {
|
|
35
|
+
uploads: AgentUploadStore;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `POST /api/agent/uploads/:token` — the one endpoint on this server
|
|
39
|
+
* authenticated by a single-use token and nothing else.
|
|
40
|
+
*
|
|
41
|
+
* It exists because MCP tool arguments are JSON, so every byte an agent sends
|
|
42
|
+
* through a tool passes through the model first. A 37 KB page gets truncated
|
|
43
|
+
* mid-response; a file of regex backslashes fails to parse as a JSON string; a
|
|
44
|
+
* PNG cannot be sent at all. Here the agent asks for a token, sends the file
|
|
45
|
+
* with any HTTP client it has, and then names the token in
|
|
46
|
+
* `apply_file_upload`. The bytes never enter a prompt.
|
|
47
|
+
*
|
|
48
|
+
* What the route itself may do is deliberately almost nothing: it stores bytes
|
|
49
|
+
* against a token, in a directory beside the workspaces root, and answers what
|
|
50
|
+
* it received. It resolves no workspace, writes nothing into one, and commits
|
|
51
|
+
* nothing — every access, platform-file and branch rule is applied later, by
|
|
52
|
+
* the apply tool, against the branch the caller then names. A token that is
|
|
53
|
+
* unknown, spent, expired or someone else's gets one 404 that says which of
|
|
54
|
+
* those it was: none of them.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createAgentUploadRoutes(deps: AgentUploadRouteDeps): express.Router;
|
|
57
|
+
/**
|
|
58
|
+
* The `filename` a `content-disposition` names, or null when it names none.
|
|
59
|
+
*
|
|
60
|
+
* A QUOTED value is read to its closing quote, not to the first semicolon: a
|
|
61
|
+
* semicolon separates the header's parameters only OUTSIDE the quotes, and
|
|
62
|
+
* `filename="report;final.md"` is one perfectly ordinary name that a
|
|
63
|
+
* semicolon-first reading landed as `report`. An unquoted value ends at the
|
|
64
|
+
* next parameter, as it must.
|
|
65
|
+
*
|
|
66
|
+
* Percent-decoded only in the extended `filename*=UTF-8''…` form, which is the
|
|
67
|
+
* only one where the encoding is part of the grammar. A plain `filename=` value
|
|
68
|
+
* is the name itself, so `50%20off.md` stays `50%20off.md` rather than losing
|
|
69
|
+
* its `%20` to a decode nobody asked for.
|
|
70
|
+
*
|
|
71
|
+
* The parameter name is matched at a boundary — the start of the header or a
|
|
72
|
+
* `;` — because `filename` is a suffix of other perfectly legal parameter
|
|
73
|
+
* names: without it, `inline; xfilename=wrong.md` read `wrong.md` as the name
|
|
74
|
+
* the sender gave, from a parameter that says nothing of the kind.
|
|
75
|
+
*/
|
|
76
|
+
export declare function dispositionFilename(disposition: string): string | null;
|
|
77
|
+
//# sourceMappingURL=agent-upload.routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-upload.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/agent-upload.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,OAAO,EAAE,gBAAgB,EAA+B,MAAM,yBAAyB,CAAC;AAIxF,+FAA+F;AAC/F,eAAO,MAAM,kBAAkB,0BAA0B,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAO9D;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAmElF;AAwCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBtE"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { validateFilename } from '@bevel-software/platform-shared';
|
|
3
|
+
import { logger } from '../../shared/logging.js';
|
|
4
|
+
import { UploadTokenError, overLimit } from './agent-upload.store.js';
|
|
5
|
+
const log = logger('agent-uploads');
|
|
6
|
+
/** The route's own prefix, under `/api`. One spelling, shared with the raw-body test below. */
|
|
7
|
+
export const AGENT_UPLOAD_ROUTE = '/agent/uploads/:token';
|
|
8
|
+
/**
|
|
9
|
+
* The same endpoint with the token in the `x-upload-token` HEADER instead of
|
|
10
|
+
* the path — the address `uploadUrl` is, without its last segment.
|
|
11
|
+
*
|
|
12
|
+
* Two spellings because a token in a URL is a credential in a place that keeps
|
|
13
|
+
* copies: an access log, a proxy log, a shell history, a `ps` listing of the
|
|
14
|
+
* `curl` that sent it. The path form is what `request_file_upload` answers,
|
|
15
|
+
* because one address an agent can paste into any client is the thing that
|
|
16
|
+
* makes this route usable at all; the header form is for a caller that would
|
|
17
|
+
* rather its credential not be written down on the way. Both reach the same
|
|
18
|
+
* handler and are judged identically — same token, same single use.
|
|
19
|
+
*/
|
|
20
|
+
export const AGENT_UPLOAD_HEADER_ROUTE = '/agent/uploads';
|
|
21
|
+
/**
|
|
22
|
+
* Whether `path` is the agent upload route, and so must reach its handler with
|
|
23
|
+
* the body still a STREAM.
|
|
24
|
+
*
|
|
25
|
+
* The app installs a global `express.json()`. It only claims a JSON
|
|
26
|
+
* content-type, but `curl --data-binary @file.zip -H 'content-type:
|
|
27
|
+
* application/json'` is a request an agent can and will make — and once the
|
|
28
|
+
* parser has drained the stream there are no bytes left to store, so the
|
|
29
|
+
* upload would answer "0 bytes received" for a file that was sent in full.
|
|
30
|
+
* Exempting the path is the same move `/api/sync` makes for its HMAC body, and
|
|
31
|
+
* for the same reason: whoever needs the exact bytes has to see them first.
|
|
32
|
+
*
|
|
33
|
+
* Case-insensitive, because Express routing is: `/api/Agent/uploads/x` reaches
|
|
34
|
+
* this router, and a check that said no would let the parser eat that body.
|
|
35
|
+
*/
|
|
36
|
+
export function isAgentUploadRawBodyPath(path) {
|
|
37
|
+
// Trailing slashes trimmed first, so the header form's bare address matches
|
|
38
|
+
// in every spelling Express routes to it (`/api/agent/uploads` and
|
|
39
|
+
// `/api/agent/uploads/` are one route): missing one of them would hand that
|
|
40
|
+
// request to the JSON parser, and the bytes it drains are gone.
|
|
41
|
+
const lower = path.toLowerCase().replace(/\/+$/, '');
|
|
42
|
+
return lower === '/api/agent/uploads' || lower.startsWith('/api/agent/uploads/');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* `POST /api/agent/uploads/:token` — the one endpoint on this server
|
|
46
|
+
* authenticated by a single-use token and nothing else.
|
|
47
|
+
*
|
|
48
|
+
* It exists because MCP tool arguments are JSON, so every byte an agent sends
|
|
49
|
+
* through a tool passes through the model first. A 37 KB page gets truncated
|
|
50
|
+
* mid-response; a file of regex backslashes fails to parse as a JSON string; a
|
|
51
|
+
* PNG cannot be sent at all. Here the agent asks for a token, sends the file
|
|
52
|
+
* with any HTTP client it has, and then names the token in
|
|
53
|
+
* `apply_file_upload`. The bytes never enter a prompt.
|
|
54
|
+
*
|
|
55
|
+
* What the route itself may do is deliberately almost nothing: it stores bytes
|
|
56
|
+
* against a token, in a directory beside the workspaces root, and answers what
|
|
57
|
+
* it received. It resolves no workspace, writes nothing into one, and commits
|
|
58
|
+
* nothing — every access, platform-file and branch rule is applied later, by
|
|
59
|
+
* the apply tool, against the branch the caller then names. A token that is
|
|
60
|
+
* unknown, spent, expired or someone else's gets one 404 that says which of
|
|
61
|
+
* those it was: none of them.
|
|
62
|
+
*/
|
|
63
|
+
export function createAgentUploadRoutes(deps) {
|
|
64
|
+
const router = express.Router();
|
|
65
|
+
const { uploads } = deps;
|
|
66
|
+
const handle = async (req, res) => {
|
|
67
|
+
const token = tokenOf(req);
|
|
68
|
+
try {
|
|
69
|
+
// THE TOKEN FIRST, before anything about the request is read, parsed,
|
|
70
|
+
// judged or quoted back. This route is authenticated by the token and
|
|
71
|
+
// nothing else, which cuts two ways. Without the check up here, anyone
|
|
72
|
+
// could make the process buffer the deployment's whole upload limit per
|
|
73
|
+
// request against tokens they invented, as many at a time as they liked;
|
|
74
|
+
// and a caller holding no token could learn which of its OTHER guesses
|
|
75
|
+
// were well formed — "that is not a usable file name" is an answer only
|
|
76
|
+
// somebody entitled to send a file should get. One refusal, nothing else.
|
|
77
|
+
// `receive` below asks again, under the same record, because that is
|
|
78
|
+
// where the token is actually spent.
|
|
79
|
+
uploads.assertOpen(token);
|
|
80
|
+
const filename = fileNameOf(req);
|
|
81
|
+
if (filename === null) {
|
|
82
|
+
res.status(400).json({
|
|
83
|
+
error: 'Name the file you are sending: add `?filename=<name>` to the upload URL (or send it as the ' +
|
|
84
|
+
'`x-upload-filename` header). A single file lands under that name, and a name ending in `.zip` is ' +
|
|
85
|
+
'read as an archive.',
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const invalid = validateFilename(filename);
|
|
90
|
+
if (invalid !== null || filename.includes('/')) {
|
|
91
|
+
res.status(400).json({
|
|
92
|
+
error: `"${filename}" is not a usable file name: ${invalid ?? 'a name cannot contain "/"'}. Send one plain file name.`,
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Then the declared length, so a caller sending something far too large
|
|
97
|
+
// is told the limit before it spends the bandwidth. The real total is
|
|
98
|
+
// counted by the store as the bytes arrive — `content-length` is the
|
|
99
|
+
// sender's claim, not a fact.
|
|
100
|
+
const declared = Number.parseInt(req.headers['content-length'] ?? '', 10);
|
|
101
|
+
if (Number.isFinite(declared) && declared > uploads.maxBytes) {
|
|
102
|
+
res.status(413).json({ error: overLimit(declared, uploads.maxBytes) });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// The request itself, as a stream: the store writes the bytes to disk as
|
|
106
|
+
// they arrive and never holds the body. It refuses an empty body, one
|
|
107
|
+
// past the limit and an unreadable archive with its own status.
|
|
108
|
+
res.json(await uploads.receive(token, filename, req));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err instanceof UploadTokenError) {
|
|
112
|
+
res.status(err.status).json({ error: err.message });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// The detail stays in the log. An unexpected failure here is a
|
|
116
|
+
// filesystem error, and its message quotes the absolute path of the
|
|
117
|
+
// store's root — a place the caller is told nothing else about.
|
|
118
|
+
log.error('upload failed:', { err });
|
|
119
|
+
res.status(500).json({ error: 'Upload failed' });
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
// Both spellings of the one endpoint: the token in the path, or in the
|
|
123
|
+
// `x-upload-token` header on the bare address.
|
|
124
|
+
router.post(AGENT_UPLOAD_ROUTE, handle);
|
|
125
|
+
router.post(AGENT_UPLOAD_HEADER_ROUTE, handle);
|
|
126
|
+
return router;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The token the sender presented: the `:token` path segment, or the
|
|
130
|
+
* `x-upload-token` header when the bytes went to the bare address.
|
|
131
|
+
*
|
|
132
|
+
* The path wins when both are present — it is the address the sender actually
|
|
133
|
+
* POSTed to, and a header left over from an earlier upload must not quietly
|
|
134
|
+
* redirect these bytes onto a different token.
|
|
135
|
+
*
|
|
136
|
+
* No token at all answers the empty string rather than its own refusal, so it
|
|
137
|
+
* goes through `assertOpen` like any other unusable token and gets the same
|
|
138
|
+
* single 404. A caller holding nothing is told nothing it did not already
|
|
139
|
+
* know — not even whether this endpoint wanted a header.
|
|
140
|
+
*/
|
|
141
|
+
function tokenOf(req) {
|
|
142
|
+
const inPath = req.params.token;
|
|
143
|
+
if (typeof inPath === 'string' && inPath.trim() !== '')
|
|
144
|
+
return inPath.trim();
|
|
145
|
+
const header = req.headers['x-upload-token'];
|
|
146
|
+
if (typeof header === 'string' && header.trim() !== '')
|
|
147
|
+
return header.trim();
|
|
148
|
+
return '';
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The name the sender gave the file: the `filename` query parameter, the
|
|
152
|
+
* `x-upload-filename` header, or a `content-disposition`'s own `filename=`.
|
|
153
|
+
* Three spellings because three kinds of client are expected to use this —
|
|
154
|
+
* `curl` with a query string, a scripted `fetch` with a header, and a client
|
|
155
|
+
* that sends the disposition it would send to any upload endpoint.
|
|
156
|
+
*/
|
|
157
|
+
function fileNameOf(req) {
|
|
158
|
+
const query = req.query.filename;
|
|
159
|
+
if (typeof query === 'string' && query.trim() !== '')
|
|
160
|
+
return query.trim();
|
|
161
|
+
const header = req.headers['x-upload-filename'];
|
|
162
|
+
if (typeof header === 'string' && header.trim() !== '')
|
|
163
|
+
return header.trim();
|
|
164
|
+
const disposition = req.headers['content-disposition'];
|
|
165
|
+
if (typeof disposition === 'string')
|
|
166
|
+
return dispositionFilename(disposition);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The `filename` a `content-disposition` names, or null when it names none.
|
|
171
|
+
*
|
|
172
|
+
* A QUOTED value is read to its closing quote, not to the first semicolon: a
|
|
173
|
+
* semicolon separates the header's parameters only OUTSIDE the quotes, and
|
|
174
|
+
* `filename="report;final.md"` is one perfectly ordinary name that a
|
|
175
|
+
* semicolon-first reading landed as `report`. An unquoted value ends at the
|
|
176
|
+
* next parameter, as it must.
|
|
177
|
+
*
|
|
178
|
+
* Percent-decoded only in the extended `filename*=UTF-8''…` form, which is the
|
|
179
|
+
* only one where the encoding is part of the grammar. A plain `filename=` value
|
|
180
|
+
* is the name itself, so `50%20off.md` stays `50%20off.md` rather than losing
|
|
181
|
+
* its `%20` to a decode nobody asked for.
|
|
182
|
+
*
|
|
183
|
+
* The parameter name is matched at a boundary — the start of the header or a
|
|
184
|
+
* `;` — because `filename` is a suffix of other perfectly legal parameter
|
|
185
|
+
* names: without it, `inline; xfilename=wrong.md` read `wrong.md` as the name
|
|
186
|
+
* the sender gave, from a parameter that says nothing of the kind.
|
|
187
|
+
*/
|
|
188
|
+
export function dispositionFilename(disposition) {
|
|
189
|
+
const match = /(?:^|;)\s*filename(\*?)\s*=\s*(?:"([^"]*)"|([^;]*))/i.exec(disposition);
|
|
190
|
+
if (!match)
|
|
191
|
+
return null;
|
|
192
|
+
const extended = match[1] === '*';
|
|
193
|
+
const raw = (match[2] ?? match[3] ?? '').trim();
|
|
194
|
+
if (!extended)
|
|
195
|
+
return raw === '' ? null : raw;
|
|
196
|
+
// `UTF-8''name`, or any other charset and language the sender declares.
|
|
197
|
+
const encoded = raw.replace(/^[^']*'[^']*'/, '');
|
|
198
|
+
try {
|
|
199
|
+
const decoded = decodeURIComponent(encoded).trim();
|
|
200
|
+
if (decoded !== '')
|
|
201
|
+
return decoded;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
const kept = encoded.trim();
|
|
205
|
+
if (kept !== '')
|
|
206
|
+
return kept;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=agent-upload.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-upload.routes.js","sourceRoot":"","sources":["../../../src/modules/workspace/agent-upload.routes.ts"],"names":[],"mappings":"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"}
|