@bevel-software/platform-core-backend 0.26.0 → 0.27.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/agent-guide/access-control.md +17 -0
- package/agent-guide/conventions.md +8 -0
- package/agent-guide/html-views.md +83 -0
- package/agent-guide/new-knowledge-base.md +24 -0
- package/agent-guide/tool-manuals.md +10 -0
- package/dist/assets.d.ts +19 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +26 -2
- package/dist/assets.js.map +1 -1
- package/dist/core/catalog-revision.d.ts +19 -1
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +14 -3
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/create-core-server.d.ts +5 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +54 -3
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +26 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +100 -8
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +16 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +18 -1
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +21 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +22 -1
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +8 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +1 -0
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +11 -0
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +1 -1
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +6 -0
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.js +8 -0
- package/dist/modules/agent-guide/agent-guide.js.map +1 -1
- package/dist/modules/agent-guide/index.d.ts +1 -1
- package/dist/modules/agent-guide/index.d.ts.map +1 -1
- package/dist/modules/agent-guide/index.js +1 -1
- package/dist/modules/agent-guide/index.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +5 -0
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +19 -2
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +19 -19
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/audit/agent-audit.service.d.ts +21 -2
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +43 -2
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/agent-connection.routes.d.ts +34 -0
- package/dist/modules/audit/agent-connection.routes.d.ts.map +1 -0
- package/dist/modules/audit/agent-connection.routes.js +38 -0
- package/dist/modules/audit/agent-connection.routes.js.map +1 -0
- package/dist/modules/audit/audit.contract.d.ts +24 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/audit/index.d.ts +1 -0
- package/dist/modules/audit/index.d.ts.map +1 -1
- package/dist/modules/audit/index.js +1 -0
- package/dist/modules/audit/index.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +10 -3
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +7 -0
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +1 -1
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +11 -3
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +96 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +32 -3
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/embed/account-link.service.d.ts +42 -0
- package/dist/modules/embed/account-link.service.d.ts.map +1 -0
- package/dist/modules/embed/account-link.service.js +50 -0
- package/dist/modules/embed/account-link.service.js.map +1 -0
- package/dist/modules/embed/embed-link.d.ts +58 -0
- package/dist/modules/embed/embed-link.d.ts.map +1 -0
- package/dist/modules/embed/embed-link.js +160 -0
- package/dist/modules/embed/embed-link.js.map +1 -0
- package/dist/modules/embed/embed.errors.d.ts +30 -0
- package/dist/modules/embed/embed.errors.d.ts.map +1 -0
- package/dist/modules/embed/embed.errors.js +42 -0
- package/dist/modules/embed/embed.errors.js.map +1 -0
- package/dist/modules/embed/embed.interface.d.ts +204 -0
- package/dist/modules/embed/embed.interface.d.ts.map +1 -0
- package/dist/modules/embed/embed.interface.js +2 -0
- package/dist/modules/embed/embed.interface.js.map +1 -0
- package/dist/modules/embed/embed.routes.d.ts +59 -0
- package/dist/modules/embed/embed.routes.d.ts.map +1 -0
- package/dist/modules/embed/embed.routes.js +343 -0
- package/dist/modules/embed/embed.routes.js.map +1 -0
- package/dist/modules/embed/embed.service.d.ts +169 -0
- package/dist/modules/embed/embed.service.d.ts.map +1 -0
- package/dist/modules/embed/embed.service.js +627 -0
- package/dist/modules/embed/embed.service.js.map +1 -0
- package/dist/modules/embed/embed.tools.d.ts +51 -0
- package/dist/modules/embed/embed.tools.d.ts.map +1 -0
- package/dist/modules/embed/embed.tools.js +170 -0
- package/dist/modules/embed/embed.tools.js.map +1 -0
- package/dist/modules/embed/mcp-app.d.ts +64 -0
- package/dist/modules/embed/mcp-app.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.js +147 -0
- package/dist/modules/embed/mcp-app.js.map +1 -0
- package/dist/modules/embed/mcp-app.routes.d.ts +21 -0
- package/dist/modules/embed/mcp-app.routes.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.routes.js +36 -0
- package/dist/modules/embed/mcp-app.routes.js.map +1 -0
- package/dist/modules/github-app/github-app.connection.d.ts +15 -0
- package/dist/modules/github-app/github-app.connection.d.ts.map +1 -1
- package/dist/modules/github-app/github-app.connection.js +34 -3
- package/dist/modules/github-app/github-app.connection.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts +65 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +300 -0
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +5 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.js +6 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +2 -1
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +28 -1
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +64 -8
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +32 -4
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/onboarding/onboarding.contract.d.ts +80 -0
- package/dist/modules/onboarding/onboarding.contract.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.contract.js +7 -0
- package/dist/modules/onboarding/onboarding.contract.js.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts +18 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.js +81 -0
- package/dist/modules/onboarding/onboarding.routes.js.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts +213 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.js +492 -0
- package/dist/modules/onboarding/starter-pack.service.js.map +1 -0
- package/dist/modules/onboarding/starter-packs.d.ts +78 -0
- package/dist/modules/onboarding/starter-packs.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-packs.js +156 -0
- package/dist/modules/onboarding/starter-packs.js.map +1 -0
- package/dist/modules/plugins/index.d.ts +1 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.d.ts +19 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +18 -1
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +0 -36
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +31 -0
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +34 -0
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +134 -1
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/repository-source.d.ts +1 -0
- package/dist/modules/settings/repository-source.d.ts.map +1 -1
- package/dist/modules/settings/repository-source.js +3 -3
- package/dist/modules/settings/repository-source.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +50 -32
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts +10 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.js +11 -3
- package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +8 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +24 -2
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +59 -2
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +9 -4
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/index.d.ts +28 -1
- package/dist/modules/tool-helpers/index.d.ts.map +1 -1
- package/dist/modules/tool-helpers/index.js +28 -1
- package/dist/modules/tool-helpers/index.js.map +1 -1
- package/dist/modules/tool-helpers/route-argument-check.d.ts +10 -0
- package/dist/modules/tool-helpers/route-argument-check.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-argument-check.js +132 -0
- package/dist/modules/tool-helpers/route-argument-check.js.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts +89 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js +113 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js.map +1 -0
- package/dist/modules/tool-helpers/tool-def.d.ts +71 -0
- package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-def.js +70 -6
- package/dist/modules/tool-helpers/tool-def.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +46 -6
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +157 -8
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +9 -0
- 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-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +14 -5
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +6 -2
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
- package/dist/modules/tool-registry/description-length.js +27 -5
- package/dist/modules/tool-registry/description-length.js.map +1 -1
- package/dist/modules/tool-registry/guide-first.d.ts +4 -0
- package/dist/modules/tool-registry/guide-first.d.ts.map +1 -1
- package/dist/modules/tool-registry/guide-first.js +9 -1
- package/dist/modules/tool-registry/guide-first.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +53 -0
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js +97 -68
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +8 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +124 -37
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +29 -5
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +62 -5
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +72 -1
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +172 -36
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +15 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +78 -6
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +15 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +25 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +6 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +23 -6
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +1 -1
- 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 +2 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/sanitize-error.d.ts +16 -0
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
- package/dist/modules/workflow/sanitize-error.js +45 -0
- package/dist/modules/workflow/sanitize-error.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +183 -15
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +805 -106
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-download.builder.d.ts +105 -0
- package/dist/modules/workspace/agent-download.builder.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.builder.js +238 -0
- package/dist/modules/workspace/agent-download.builder.js.map +1 -0
- package/dist/modules/workspace/agent-download.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-download.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.routes.js +156 -0
- package/dist/modules/workspace/agent-download.routes.js.map +1 -0
- package/dist/modules/workspace/agent-download.store.d.ts +225 -0
- package/dist/modules/workspace/agent-download.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.store.js +423 -0
- package/dist/modules/workspace/agent-download.store.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +10 -1
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -1
- package/dist/modules/workspace/agent-upload.store.js +16 -6
- package/dist/modules/workspace/agent-upload.store.js.map +1 -1
- package/dist/modules/workspace/file-readers/raw-mime.d.ts +6 -0
- package/dist/modules/workspace/file-readers/raw-mime.d.ts.map +1 -0
- package/dist/modules/workspace/file-readers/raw-mime.js +38 -0
- package/dist/modules/workspace/file-readers/raw-mime.js.map +1 -0
- package/dist/modules/workspace/file-readers/text-reader.d.ts +5 -5
- package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.js +5 -8
- package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
- package/dist/modules/workspace/first-run.d.ts +111 -0
- package/dist/modules/workspace/first-run.d.ts.map +1 -0
- package/dist/modules/workspace/first-run.js +181 -0
- package/dist/modules/workspace/first-run.js.map +1 -0
- package/dist/modules/workspace/move-links.d.ts +92 -0
- package/dist/modules/workspace/move-links.d.ts.map +1 -0
- package/dist/modules/workspace/move-links.js +173 -0
- package/dist/modules/workspace/move-links.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +3 -13
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +63 -22
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +75 -2
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +197 -24
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +59 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +443 -65
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/write-access/write-access.d.ts.map +1 -1
- package/dist/modules/write-access/write-access.js +9 -0
- package/dist/modules/write-access/write-access.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +73 -1
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +86 -12
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +10 -1
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/utf8-text.d.ts +15 -0
- package/dist/shared/utf8-text.d.ts.map +1 -0
- package/dist/shared/utf8-text.js +20 -0
- package/dist/shared/utf8-text.js.map +1 -0
- package/dist/tenancy/static-tenant-source.d.ts +2 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +9 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/mcp-app/page.html +349 -0
- package/migrations/0017_atlassian_account_links.sql +37 -0
- package/migrations/meta/0017_snapshot.json +2396 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +5 -3
- package/src/__tests__/packaged-assets-ship.test.ts +16 -4
- package/src/assets.ts +28 -2
- package/src/core/__tests__/catalog-revision.test.ts +40 -0
- package/src/core/__tests__/gated-boot.test.ts +1 -0
- package/src/core/__tests__/lifecycle.test.ts +14 -0
- package/src/core/catalog-revision.ts +15 -2
- package/src/core/create-core-server.ts +67 -3
- package/src/core/create-core-services.ts +140 -6
- package/src/core/lifecycle.ts +28 -2
- package/src/core-config.ts +24 -1
- package/src/index.ts +2 -2
- package/src/modules/access/__tests__/access-control.service.test.ts +19 -0
- package/src/modules/access/access-control.interface.ts +13 -0
- package/src/modules/access/access-control.service.ts +16 -0
- package/src/modules/access/directory-sync-bot.ts +1 -1
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +58 -0
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +19 -1
- package/src/modules/agent-guide/agent-guide.ts +9 -0
- package/src/modules/agent-guide/index.ts +1 -0
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +14 -0
- package/src/modules/agent-instructions/compose.ts +17 -2
- package/src/modules/agent-instructions/shared-file-rules.ts +19 -19
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +64 -4
- package/src/modules/audit/__tests__/agent-connection.routes.test.ts +102 -0
- package/src/modules/audit/agent-audit.service.ts +58 -3
- package/src/modules/audit/agent-connection.routes.ts +56 -0
- package/src/modules/audit/audit.contract.ts +26 -0
- package/src/modules/audit/index.ts +1 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +5 -3
- package/src/modules/auth/account.routes.ts +10 -3
- package/src/modules/auth/auth.middleware.ts +1 -1
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +18 -2
- package/src/modules/code-mode/code-mode.tool.ts +13 -2
- package/src/modules/database/core-schema.ts +33 -3
- package/src/modules/embed/__tests__/account-links.migration.test.ts +98 -0
- package/src/modules/embed/__tests__/embed-link.test.ts +122 -0
- package/src/modules/embed/__tests__/embed.routes.test.ts +362 -0
- package/src/modules/embed/__tests__/embed.service.lock.test.ts +193 -0
- package/src/modules/embed/__tests__/embed.service.test.ts +805 -0
- package/src/modules/embed/__tests__/embed.tools.test.ts +304 -0
- package/src/modules/embed/__tests__/mcp-app.test.ts +207 -0
- package/src/modules/embed/account-link.service.ts +76 -0
- package/src/modules/embed/embed-link.ts +180 -0
- package/src/modules/embed/embed.errors.ts +43 -0
- package/src/modules/embed/embed.interface.ts +203 -0
- package/src/modules/embed/embed.routes.ts +363 -0
- package/src/modules/embed/embed.service.ts +705 -0
- package/src/modules/embed/embed.tools.ts +206 -0
- package/src/modules/embed/mcp-app.routes.ts +41 -0
- package/src/modules/embed/mcp-app.ts +173 -0
- package/src/modules/github-app/__tests__/github-app.test.ts +76 -0
- package/src/modules/github-app/github-app.connection.ts +38 -4
- package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +304 -0
- package/src/modules/kb-fs/locking-filesystem.ts +287 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +35 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +2 -0
- package/src/modules/kb-sync/kb-sync.service.ts +6 -0
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +68 -11
- package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +37 -2
- package/src/modules/mcp/__tests__/mcp.app.e2e.test.ts +200 -0
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +3 -1
- package/src/modules/mcp/__tests__/mcp.service.test.ts +23 -13
- package/src/modules/mcp/__tests__/tool-call-interface.e2e.test.ts +499 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +2 -1
- package/src/modules/mcp/mcp.service.ts +81 -12
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +42 -4
- package/src/modules/onboarding/__tests__/onboarding.routes.test.ts +163 -0
- package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +635 -0
- package/src/modules/onboarding/__tests__/starter-packs.test.ts +169 -0
- package/src/modules/onboarding/onboarding.contract.ts +85 -0
- package/src/modules/onboarding/onboarding.routes.ts +85 -0
- package/src/modules/onboarding/starter-pack.service.ts +580 -0
- package/src/modules/onboarding/starter-packs.ts +182 -0
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +25 -2
- package/src/modules/plugins/index.ts +1 -0
- package/src/modules/plugins/plugin-provision.service.ts +22 -1
- package/src/modules/plugins/plugins.routes.ts +29 -0
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +150 -1
- package/src/modules/settings/__tests__/repository-source.test.ts +9 -0
- package/src/modules/settings/deployment-settings.service.ts +131 -1
- package/src/modules/settings/repository-source.ts +4 -4
- package/src/modules/skills/__tests__/skills.tools.description.test.ts +5 -1
- package/src/modules/skills/skills.tools.ts +55 -35
- package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +32 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +65 -0
- package/src/modules/tool-auth/connection-key-rejection.ts +13 -3
- package/src/modules/tool-auth/external-api-key.interface.ts +9 -0
- package/src/modules/tool-auth/external-api-key.service.ts +61 -4
- package/src/modules/tool-auth/tool-auth.middleware.ts +13 -4
- package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +502 -0
- package/src/modules/tool-helpers/__tests__/route-argument-check.test.ts +337 -0
- package/src/modules/tool-helpers/index.ts +34 -1
- package/src/modules/tool-helpers/route-argument-check.ts +149 -0
- package/src/modules/tool-helpers/route-tool-schemas.ts +171 -0
- package/src/modules/tool-helpers/tool-def.ts +132 -7
- package/src/modules/tool-helpers/tool-handler.ts +196 -8
- package/src/modules/tool-helpers/tool.contract.ts +9 -0
- package/src/modules/tool-manuals/tool-manuals.tools.ts +15 -5
- package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +14 -1
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +23 -10
- package/src/modules/tool-registry/description-length.ts +34 -7
- package/src/modules/tool-registry/guide-first.ts +8 -1
- package/src/modules/workflow/__tests__/file-lock.service.deletion-gate.test.ts +104 -0
- package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +66 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +31 -2
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +110 -6
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +89 -1
- package/src/modules/workflow/__tests__/workflow.routes.push-refused.test.ts +202 -0
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +6 -6
- package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +1026 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +468 -10
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +4 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +5 -4
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +54 -2
- package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +41 -5
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +127 -2
- package/src/modules/workflow/agent-tools/change-request-read.tools.ts +157 -122
- package/src/modules/workflow/agent-tools/workflow.tools.ts +129 -35
- package/src/modules/workflow/file-lock.service.ts +67 -5
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +164 -0
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +48 -1
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +231 -2
- package/src/modules/workflow/git/git.service.ts +176 -38
- package/src/modules/workflow/git/node-git-runner.ts +82 -7
- package/src/modules/workflow/pending-commits.service.ts +29 -1
- package/src/modules/workflow/pending-commits.worker.ts +24 -6
- package/src/modules/workflow/recovery-bot.ts +1 -1
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +2 -1
- package/src/modules/workflow/sanitize-error.ts +50 -0
- package/src/modules/workflow/workflow.service.ts +879 -112
- package/src/modules/workspace/__tests__/agent-downloads.test.ts +823 -0
- package/src/modules/workspace/__tests__/first-run.test.ts +234 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +5 -2
- package/src/modules/workspace/__tests__/md-links.test.ts +353 -0
- package/src/modules/workspace/__tests__/move-links.test.ts +261 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +219 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -0
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +102 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +52 -5
- package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +190 -0
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +8 -4
- package/src/modules/workspace/__tests__/workspace.service.test.ts +50 -8
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +44 -1
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +409 -12
- package/src/modules/workspace/agent-download.builder.ts +308 -0
- package/src/modules/workspace/agent-download.routes.ts +183 -0
- package/src/modules/workspace/agent-download.store.ts +533 -0
- package/src/modules/workspace/agent-upload.store.ts +23 -7
- package/src/modules/workspace/file-readers/raw-mime.ts +40 -0
- package/src/modules/workspace/file-readers/text-reader.ts +5 -8
- package/src/modules/workspace/first-run.ts +222 -0
- package/src/modules/workspace/move-links.ts +237 -0
- package/src/modules/workspace/startup/steps/seed-tree.ts +3 -12
- package/src/modules/workspace/workspace.routes.ts +64 -22
- package/src/modules/workspace/workspace.service.ts +202 -25
- package/src/modules/workspace/workspace.tools.ts +492 -63
- package/src/modules/write-access/__tests__/write-access.test.ts +13 -0
- package/src/modules/write-access/write-access.ts +9 -0
- package/src/shared/domain-errors.ts +108 -11
- package/src/shared/git.contract.ts +8 -1
- package/src/shared/utf8-text.ts +21 -0
- package/src/tenancy/__tests__/static-tenant-source.test.ts +1 -0
- package/src/tenancy/static-tenant-source.ts +10 -2
- package/starter-packs/README.md +103 -0
- package/starter-packs/engineering/KnowledgeBase/About us.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Architecture.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/How we ship.md +9 -0
- package/starter-packs/engineering/Plugins/engineering-starter/LICENSE +21 -0
- package/starter-packs/engineering/Plugins/engineering-starter/SOURCE.md +20 -0
- package/starter-packs/engineering/Plugins/engineering-starter/access.md +19 -0
- package/starter-packs/engineering/Plugins/engineering-starter/plugin.json +10 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/SKILL.md +171 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2a-startup-diagnostic.md +157 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2b-builder-brainstorm.md +39 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-ceo-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-eng-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/SKILL.md +80 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/checklist.md +184 -0
- package/starter-packs/engineering/pack.yaml +11 -0
- package/starter-packs/general/KnowledgeBase/About us.md +9 -0
- package/starter-packs/general/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/general/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/general/Plugins/general-starter/LICENSE +202 -0
- package/starter-packs/general/Plugins/general-starter/SOURCE.md +10 -0
- package/starter-packs/general/Plugins/general-starter/access.md +19 -0
- package/starter-packs/general/Plugins/general-starter/plugin.json +10 -0
- package/starter-packs/general/Plugins/general-starter/skills/memory-management/SKILL.md +332 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-start/SKILL.md +163 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-update/SKILL.md +167 -0
- package/starter-packs/general/Plugins/general-starter/skills/task-management/SKILL.md +83 -0
- package/starter-packs/general/pack.yaml +10 -0
- package/starter-packs/operations/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/operations/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Processes.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Tools we use.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Vendors.md +9 -0
- package/starter-packs/operations/Plugins/operations-starter/LICENSE +202 -0
- package/starter-packs/operations/Plugins/operations-starter/SOURCE.md +10 -0
- package/starter-packs/operations/Plugins/operations-starter/access.md +19 -0
- package/starter-packs/operations/Plugins/operations-starter/plugin.json +10 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/capacity-plan/SKILL.md +111 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/change-request/SKILL.md +119 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/compliance-tracking/SKILL.md +44 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-doc/SKILL.md +80 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-optimization/SKILL.md +39 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/risk-assessment/SKILL.md +40 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/runbook/SKILL.md +85 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/status-report/SKILL.md +75 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/vendor-review/SKILL.md +102 -0
- package/starter-packs/operations/pack.yaml +12 -0
- package/starter-packs/sales/KnowledgeBase/About us.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Customers.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/sales/KnowledgeBase/How we sell.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Objections.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Pricing.md +9 -0
- package/starter-packs/sales/Plugins/sales-starter/LICENSE +202 -0
- package/starter-packs/sales/Plugins/sales-starter/SOURCE.md +10 -0
- package/starter-packs/sales/Plugins/sales-starter/access.md +19 -0
- package/starter-packs/sales/Plugins/sales-starter/plugin.json +10 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-context/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-plan/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-research/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-tiering/SKILL.md +101 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-prep/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-summary/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/close-plan/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/competitive-intelligence/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/create-an-asset/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/crm-hygiene-check/SKILL.md +103 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-health/SKILL.md +108 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-voice/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/daily-briefing/SKILL.md +61 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-advance-gap/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-review/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-signals/SKILL.md +96 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-slip-scenario/SKILL.md +104 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/draft-outreach/SKILL.md +116 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/end-of-day/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/expansion-whitespace/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/forecast/SKILL.md +100 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/handle-objection/SKILL.md +93 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/inbox-sweep/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/lead-triage/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/log-activity/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/pipeline-review/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/renewal-radar/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/rep-context/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/route-lead/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/sales-setup/SKILL.md +200 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/schedule-meeting/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/stakeholder-map/SKILL.md +107 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/team-pipeline/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/update-opportunity/SKILL.md +112 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/weekly-wrap/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/win-loss-review/SKILL.md +88 -0
- package/starter-packs/sales/pack.yaml +13 -0
- package/starter-packs/support/KnowledgeBase/Escalations.md +9 -0
- package/starter-packs/support/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/support/KnowledgeBase/Known issues.md +9 -0
- package/starter-packs/support/KnowledgeBase/Products.md +9 -0
- package/starter-packs/support/KnowledgeBase/Tone of voice.md +9 -0
- package/starter-packs/support/Plugins/support-starter/LICENSE +202 -0
- package/starter-packs/support/Plugins/support-starter/SOURCE.md +10 -0
- package/starter-packs/support/Plugins/support-starter/access.md +19 -0
- package/starter-packs/support/Plugins/support-starter/plugin.json +10 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-escalation/SKILL.md +246 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-research/SKILL.md +250 -0
- package/starter-packs/support/Plugins/support-starter/skills/draft-response/SKILL.md +416 -0
- package/starter-packs/support/Plugins/support-starter/skills/kb-article/SKILL.md +350 -0
- package/starter-packs/support/Plugins/support-starter/skills/ticket-triage/SKILL.md +273 -0
- package/starter-packs/support/pack.yaml +12 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import AdmZip from 'adm-zip';
|
|
3
|
+
import { hasHttpStatus, ToolError } from '../tool-helpers/tool.contract.js';
|
|
4
|
+
import { isAbsence } from '../../shared/fs.contract.js';
|
|
5
|
+
/** Why a requested path was left out. The first two are the only verdicts the access rules give. */
|
|
6
|
+
export const NOT_FOUND = 'not found';
|
|
7
|
+
export const DOWNLOAD_PERMISSION_REQUIRED = 'download permission required';
|
|
8
|
+
const EMPTY_FOLDER = 'the folder holds no files';
|
|
9
|
+
/** The refusal a request over the size limit gets: the total and the limit, and no link. */
|
|
10
|
+
export function downloadTooLarge(total, maxBytes) {
|
|
11
|
+
return new ToolError(`Those files total ${total} bytes uncompressed, over this deployment's ${maxBytes} byte download limit, so no ` +
|
|
12
|
+
'link was issued. Ask for fewer paths at a time.', 413, { kind: 'download-too-large', totalBytes: total, maxBytes });
|
|
13
|
+
}
|
|
14
|
+
/** The refusal a request naming more files than one request may carry gets: no link. */
|
|
15
|
+
export function downloadTooManyFiles(maxFiles) {
|
|
16
|
+
return new ToolError(`Those paths hold more than ${maxFiles} files, the most one download request may carry, so no link was ` +
|
|
17
|
+
'issued. Ask for fewer or smaller folders at a time.', 413, { kind: 'download-too-many-files', maxFiles });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The refusal a request gets when its folders' zips together would carry more
|
|
21
|
+
* than the limit — a file inside two requested folders (`a/` and `a/b/`) is
|
|
22
|
+
* packed into both, so the zips can outgrow the files they hold.
|
|
23
|
+
*/
|
|
24
|
+
function zipsTooLarge(total, maxBytes) {
|
|
25
|
+
return new ToolError(`The requested folders' zips would total ${total} bytes uncompressed (a file inside two requested folders ` +
|
|
26
|
+
`is packed into each), over this deployment's ${maxBytes} byte download limit, so no link was issued. Ask ` +
|
|
27
|
+
'for fewer or non-overlapping folders at a time.', 413, { kind: 'download-too-large', totalBytes: total, maxBytes });
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Judge every file of a download request on its own, and capture the ones
|
|
31
|
+
* that pass.
|
|
32
|
+
*
|
|
33
|
+
* A requested folder is expanded with the explorer's walk, and each file in
|
|
34
|
+
* it is judged exactly as a file named directly is: included only when the
|
|
35
|
+
* caller holds BOTH `read` and `download` on THAT file. `download` on the
|
|
36
|
+
* folder is not enough — a file's own frontmatter or a nested `access.md` can
|
|
37
|
+
* withhold it, and the app's folder zip once packed such files.
|
|
38
|
+
*
|
|
39
|
+
* A file the caller NAMED and may not read is `not found`, the same answer
|
|
40
|
+
* as a path with nothing at it; a file met inside a requested folder that
|
|
41
|
+
* they may not read is left out without a word — its name is what the read
|
|
42
|
+
* model hides, and the explorer leaves it out the same way — and a folder
|
|
43
|
+
* the explorer would drop (one they may not read, with nothing readable
|
|
44
|
+
* beneath it) is `not found` too, while a folder they may read that holds
|
|
45
|
+
* nothing they may read "holds no files", as the explorer keeps it. A
|
|
46
|
+
* readable file they may not download is `download permission
|
|
47
|
+
* required`, named either way: the explorer shows it. The file count a
|
|
48
|
+
* request is held to counts only what the caller may read, so no refusal
|
|
49
|
+
* tells them how many files they cannot see. Every included file then goes to the deployment's read
|
|
50
|
+
* hook once, as a `read_file` of it would, and one the hook refuses is listed
|
|
51
|
+
* with the hook's own words. One refused path never stops the others.
|
|
52
|
+
*
|
|
53
|
+
* The size limit is checked on the sizes on disk BEFORE any file is read, and
|
|
54
|
+
* again on what was read; past it the whole request is refused. It holds twice
|
|
55
|
+
* over: once on the distinct files (each file's own link), and once on the
|
|
56
|
+
* folders' zips together, where a file is counted in every zip that packs it —
|
|
57
|
+
* so what one request stores is bounded by twice the limit, however its
|
|
58
|
+
* folders overlap. A request holding more than `maxFiles` readable files is
|
|
59
|
+
* refused before any of them is read.
|
|
60
|
+
*/
|
|
61
|
+
export async function buildDownload(requested, deps) {
|
|
62
|
+
const { kbDirName } = deps;
|
|
63
|
+
const toWs = (rel) => (rel ? `${kbDirName}/${rel}` : kbDirName);
|
|
64
|
+
const refused = [];
|
|
65
|
+
const refusedPaths = new Set();
|
|
66
|
+
const refuse = (path, reason) => {
|
|
67
|
+
if (refusedPaths.has(path))
|
|
68
|
+
return;
|
|
69
|
+
refusedPaths.add(path);
|
|
70
|
+
refused.push({ path, reason });
|
|
71
|
+
};
|
|
72
|
+
/** Every candidate file, named directly or found in a folder, once, in the order first met. */
|
|
73
|
+
const sizes = new Map();
|
|
74
|
+
/** The files named directly, and each requested folder's files, before any verdict. */
|
|
75
|
+
const direct = [];
|
|
76
|
+
const candidates = [];
|
|
77
|
+
for (const wsPath of [...new Set(requested)]) {
|
|
78
|
+
let found;
|
|
79
|
+
try {
|
|
80
|
+
found = await deps.candidatesAt(wsPath);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
// A spelling the reads refuse (the git folder, a link) is a path the
|
|
84
|
+
// caller may not read: answered as nothing there, like any other.
|
|
85
|
+
if (hasHttpStatus(err) || isAbsence(err)) {
|
|
86
|
+
refuse(wsPath, NOT_FOUND);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
if (found.kind === 'missing') {
|
|
92
|
+
refuse(wsPath, NOT_FOUND);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const f of found.files)
|
|
96
|
+
if (!sizes.has(f.path))
|
|
97
|
+
sizes.set(f.path, f.bytes);
|
|
98
|
+
if (found.kind === 'folder')
|
|
99
|
+
candidates.push({ path: wsPath, files: found.files.map((f) => f.path) });
|
|
100
|
+
else
|
|
101
|
+
direct.push(...found.files.map((f) => f.path));
|
|
102
|
+
}
|
|
103
|
+
// READ first, over everything found — the files and the requested folders
|
|
104
|
+
// themselves (the root is every caller's) — before any other answer is
|
|
105
|
+
// given. One batch, the primitive the explorer tree judges with.
|
|
106
|
+
const relOfFolder = (wsPath) => wsPath === kbDirName ? '' : wsPath.startsWith(`${kbDirName}/`) ? wsPath.slice(kbDirName.length + 1) : wsPath;
|
|
107
|
+
const folderRels = candidates.map((f) => relOfFolder(f.path)).filter((rel) => rel !== '');
|
|
108
|
+
const asked = [...new Set([...sizes.keys(), ...folderRels])];
|
|
109
|
+
const readable = asked.length > 0 ? await deps.canReadBatch(asked) : new Map();
|
|
110
|
+
// The count the caller is held to is the count of what they may read: a
|
|
111
|
+
// refusal that counted hidden files would tell them how many there are.
|
|
112
|
+
const visibleCount = [...sizes.keys()].filter((p) => readable.get(p) === true).length;
|
|
113
|
+
if (visibleCount > deps.maxFiles)
|
|
114
|
+
throw downloadTooManyFiles(deps.maxFiles);
|
|
115
|
+
// A folder is answered as the explorer shows it. The explorer keeps a
|
|
116
|
+
// folder the caller may read — left empty when its files are filtered, it
|
|
117
|
+
// stays, "holds no files" — and a folder with something readable beneath
|
|
118
|
+
// it; one that is neither it drops outright, so here it is `not found`,
|
|
119
|
+
// the answer a folder that is not there gets. Only a folder with files to
|
|
120
|
+
// pack is then asked for its own `download`: a zip of it is asked for as
|
|
121
|
+
// the app's folder download is, before any file inside is judged — a
|
|
122
|
+
// file's own grant must not open a folder its rules deny.
|
|
123
|
+
const folders = [];
|
|
124
|
+
for (const folder of candidates) {
|
|
125
|
+
const rel = relOfFolder(folder.path);
|
|
126
|
+
const folderReadable = rel === '' || readable.get(rel) === true;
|
|
127
|
+
const somethingReadable = folder.files.some((f) => readable.get(f) === true);
|
|
128
|
+
if (!folderReadable && !somethingReadable)
|
|
129
|
+
refuse(folder.path, NOT_FOUND);
|
|
130
|
+
else if (!somethingReadable)
|
|
131
|
+
refuse(folder.path, EMPTY_FOLDER);
|
|
132
|
+
else if (deps.canDownloadFolder && !(await deps.canDownloadFolder(folder.path))) {
|
|
133
|
+
refuse(folder.path, DOWNLOAD_PERMISSION_REQUIRED);
|
|
134
|
+
}
|
|
135
|
+
else
|
|
136
|
+
folders.push(folder);
|
|
137
|
+
}
|
|
138
|
+
// Every file judged on its own: the named ones and the kept folders' —
|
|
139
|
+
// read first, download on what may be read.
|
|
140
|
+
const all = [...new Set([...direct, ...folders.flatMap((f) => f.files)])];
|
|
141
|
+
const visible = all.filter((p) => readable.get(p) === true);
|
|
142
|
+
const downloadable = visible.length > 0 ? await deps.canDownloadBatch(visible) : new Map();
|
|
143
|
+
const named = new Set(requested);
|
|
144
|
+
const allowed = [];
|
|
145
|
+
for (const rel of all) {
|
|
146
|
+
if (readable.get(rel) !== true) {
|
|
147
|
+
// Named by the caller: answered as nothing there. Found inside a folder:
|
|
148
|
+
// not answered at all — listing it as refused would hand the caller the
|
|
149
|
+
// name of a file the read model hides from them.
|
|
150
|
+
if (named.has(toWs(rel)))
|
|
151
|
+
refuse(toWs(rel), NOT_FOUND);
|
|
152
|
+
}
|
|
153
|
+
else if (downloadable.get(rel) !== true)
|
|
154
|
+
refuse(toWs(rel), DOWNLOAD_PERMISSION_REQUIRED);
|
|
155
|
+
else
|
|
156
|
+
allowed.push(rel);
|
|
157
|
+
}
|
|
158
|
+
// The read hook, once per file that would be included.
|
|
159
|
+
const passed = [];
|
|
160
|
+
for (const rel of allowed) {
|
|
161
|
+
try {
|
|
162
|
+
await deps.notifyRead(toWs(rel));
|
|
163
|
+
passed.push(rel);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
if (!hasHttpStatus(err))
|
|
167
|
+
throw err;
|
|
168
|
+
refuse(toWs(rel), err.message);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// The limit, on the sizes on disk, before a byte is read.
|
|
172
|
+
const declared = passed.reduce((sum, rel) => sum + (sizes.get(rel) ?? 0), 0);
|
|
173
|
+
if (declared > deps.maxBytes)
|
|
174
|
+
throw downloadTooLarge(declared, deps.maxBytes);
|
|
175
|
+
const passedSet = new Set(passed);
|
|
176
|
+
const zipsDeclared = folders.reduce((sum, folder) => sum + folder.files.reduce((n, rel) => n + (passedSet.has(rel) ? (sizes.get(rel) ?? 0) : 0), 0), 0);
|
|
177
|
+
if (zipsDeclared > deps.maxBytes)
|
|
178
|
+
throw zipsTooLarge(zipsDeclared, deps.maxBytes);
|
|
179
|
+
const files = [];
|
|
180
|
+
const artifacts = [];
|
|
181
|
+
const captured = new Map();
|
|
182
|
+
let total = 0;
|
|
183
|
+
for (const rel of passed) {
|
|
184
|
+
const wsPath = toWs(rel);
|
|
185
|
+
let data;
|
|
186
|
+
try {
|
|
187
|
+
data = await deps.readFile(wsPath);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
// Gone between the walk and the read: what a read a moment later says.
|
|
191
|
+
if (isAbsence(err) || hasHttpStatus(err)) {
|
|
192
|
+
refuse(wsPath, NOT_FOUND);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
// Again on what was read: a file may have grown since it was measured.
|
|
198
|
+
total += data.byteLength;
|
|
199
|
+
if (total > deps.maxBytes)
|
|
200
|
+
throw downloadTooLarge(total, deps.maxBytes);
|
|
201
|
+
captured.set(rel, data);
|
|
202
|
+
files.push({ path: wsPath, bytes: data.byteLength, sha256: createHash('sha256').update(data).digest('hex') });
|
|
203
|
+
artifacts.push({ data, contentType: deps.contentTypeOf(wsPath, data), filename: baseName(rel) || 'file' });
|
|
204
|
+
}
|
|
205
|
+
// One zip per requested folder that kept at least one file, its entries at
|
|
206
|
+
// their full repository paths, so an upload of it at the repository root
|
|
207
|
+
// lands every file where it came from.
|
|
208
|
+
// Again on what was read, before a zip is built.
|
|
209
|
+
const kept = folders.map((folder) => folder.files.filter((rel) => captured.has(rel)));
|
|
210
|
+
const zipsTotal = kept.flat().reduce((sum, rel) => sum + captured.get(rel).byteLength, 0);
|
|
211
|
+
if (zipsTotal > deps.maxBytes)
|
|
212
|
+
throw zipsTooLarge(zipsTotal, deps.maxBytes);
|
|
213
|
+
const zipped = [];
|
|
214
|
+
for (const [i, folder] of folders.entries()) {
|
|
215
|
+
const files = kept[i];
|
|
216
|
+
if (files.length === 0)
|
|
217
|
+
continue;
|
|
218
|
+
const zip = new AdmZip();
|
|
219
|
+
let bytes = 0;
|
|
220
|
+
for (const rel of files) {
|
|
221
|
+
const data = captured.get(rel);
|
|
222
|
+
zip.addFile(rel, data);
|
|
223
|
+
bytes += data.byteLength;
|
|
224
|
+
}
|
|
225
|
+
zipped.push({ path: folder.path, bytes, files: files.map(toWs) });
|
|
226
|
+
const rel = folder.path === kbDirName ? '' : folder.path.slice(kbDirName.length + 1).replace(/\/+$/, '');
|
|
227
|
+
artifacts.push({
|
|
228
|
+
data: zip.toBuffer(),
|
|
229
|
+
contentType: 'application/zip',
|
|
230
|
+
filename: `${baseName(rel) || kbDirName}.zip`,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return { files, folders: zipped, refused, artifacts };
|
|
234
|
+
}
|
|
235
|
+
function baseName(rel) {
|
|
236
|
+
return rel.split('/').filter(Boolean).pop() ?? '';
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=agent-download.builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-download.builder.js","sourceRoot":"","sources":["../../../src/modules/workspace/agent-download.builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAGxD,oGAAoG;AACpG,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AACrC,MAAM,CAAC,MAAM,4BAA4B,GAAG,8BAA8B,CAAC;AAC3E,MAAM,YAAY,GAAG,2BAA2B,CAAC;AAgEjD,4FAA4F;AAC5F,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,QAAgB;IAC9D,OAAO,IAAI,SAAS,CAClB,qBAAqB,KAAK,+CAA+C,QAAQ,8BAA8B;QAC7G,iDAAiD,EACnD,GAAG,EACH,EAAE,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,CAC5D,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,OAAO,IAAI,SAAS,CAClB,8BAA8B,QAAQ,kEAAkE;QACtG,qDAAqD,EACvD,GAAG,EACH,EAAE,IAAI,EAAE,yBAAyB,EAAE,QAAQ,EAAE,CAC9C,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAa,EAAE,QAAgB;IACnD,OAAO,IAAI,SAAS,CAClB,2CAA2C,KAAK,2DAA2D;QACzG,gDAAgD,QAAQ,mDAAmD;QAC3G,iDAAiD,EACnD,GAAG,EACH,EAAE,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,CAC5D,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAmB,EAAE,IAAuB;IAC9E,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC3B,MAAM,IAAI,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAChF,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,MAAc,EAAQ,EAAE;QACpD,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO;QACnC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,+FAA+F;IAC/F,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,uFAAuF;IACvF,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAwC,EAAE,CAAC;IAC3D,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QAC7C,IAAI,KAAyB,CAAC;QAC9B,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,kEAAkE;YAClE,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBAC1B,SAAS;YACX,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QAChF,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;;YACjG,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,0EAA0E;IAC1E,uEAAuE;IACvE,iEAAiE;IACjE,MAAM,WAAW,GAAG,CAAC,MAAc,EAAU,EAAE,CAC7C,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/G,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;IAC1F,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAmB,CAAC;IAChG,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,YAAY,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;IACtF,IAAI,YAAY,GAAG,IAAI,CAAC,QAAQ;QAAE,MAAM,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE5E,sEAAsE;IACtE,0EAA0E;IAC1E,yEAAyE;IACzE,wEAAwE;IACxE,0EAA0E;IAC1E,yEAAyE;IACzE,qEAAqE;IACrE,0DAA0D;IAC1D,MAAM,OAAO,GAAwC,EAAE,CAAC;IACxD,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,cAAc,GAAG,GAAG,KAAK,EAAE,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;QAChE,MAAM,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QAC7E,IAAI,CAAC,cAAc,IAAI,CAAC,iBAAiB;YAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;aACrE,IAAI,CAAC,iBAAiB;YAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;aAC1D,IAAI,IAAI,CAAC,iBAAiB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAChF,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,4BAA4B,CAAC,CAAC;QACpD,CAAC;;YAAM,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,uEAAuE;IACvE,4CAA4C;IAC5C,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAmB,CAAC;IAC5G,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,yEAAyE;YACzE,wEAAwE;YACxE,iDAAiD;YACjD,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;QACzD,CAAC;aAAM,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,4BAA4B,CAAC,CAAC;;YACtF,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,uDAAuD;IACvD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;gBAAE,MAAM,GAAG,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7E,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ;QAAE,MAAM,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CACjC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAC/G,CAAC,CACF,CAAC;IACF,IAAI,YAAY,GAAG,IAAI,CAAC,QAAQ;QAAE,MAAM,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAElF,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,MAAM,SAAS,GAAuB,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,uEAAuE;YACvE,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBAC1B,SAAS;YACX,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,uEAAuE;QACvE,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC;QACzB,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ;YAAE,MAAM,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9G,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;IAC7G,CAAC;IAED,2EAA2E;IAC3E,yEAAyE;IACzE,uCAAuC;IACvC,iDAAiD;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACtF,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAC3F,IAAI,SAAS,GAAG,IAAI,CAAC,QAAQ;QAAE,MAAM,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACvB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,MAAM,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;YAChC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACvB,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC;QAC3B,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzG,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE;YACpB,WAAW,EAAE,iBAAiB;YAC9B,QAAQ,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,SAAS,MAAM;SAC9C,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACxD,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { type IAgentDownloadStore } from './agent-download.store.js';
|
|
3
|
+
/** The route's own prefix, under `/api`. */
|
|
4
|
+
export declare const AGENT_DOWNLOAD_ROUTE = "/agent/downloads/:token";
|
|
5
|
+
/**
|
|
6
|
+
* The same endpoint with the token in the `x-download-token` HEADER instead of
|
|
7
|
+
* the path — the address `downloadUrl` is, without its last segment. For a
|
|
8
|
+
* caller that would rather its credential not land in an access log or a
|
|
9
|
+
* shell history, as the upload route's header form is.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AGENT_DOWNLOAD_HEADER_ROUTE = "/agent/downloads";
|
|
12
|
+
export interface AgentDownloadRouteDeps {
|
|
13
|
+
downloads: IAgentDownloadStore;
|
|
14
|
+
/**
|
|
15
|
+
* Every user a fetch identifies itself as, by the credentials this server
|
|
16
|
+
* recognises — none when it carries none (or none that verifies). The link
|
|
17
|
+
* is the whole credential, so most fetches carry nothing; this only lets the
|
|
18
|
+
* route refuse a fetch that says it is SOMEONE ELSE than the user the link
|
|
19
|
+
* was issued to. Optional: absent, identity is not asked.
|
|
20
|
+
*/
|
|
21
|
+
identify?: (req: express.Request) => Promise<string[]>;
|
|
22
|
+
}
|
|
23
|
+
/** The verifiers {@link createDownloadFetcherIdentifier} asks, as the composition root has them. */
|
|
24
|
+
export interface DownloadFetcherVerifiers {
|
|
25
|
+
/** A connection key or internal token (the tool routes' verifier). Never throws for a bad token. */
|
|
26
|
+
verifyToolToken(token: string): Promise<{
|
|
27
|
+
ok: true;
|
|
28
|
+
auth: {
|
|
29
|
+
userId: string;
|
|
30
|
+
};
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
}>;
|
|
34
|
+
/** An app session JWT's signature and expiry; throws when it does not verify. */
|
|
35
|
+
verifySession(token: string): {
|
|
36
|
+
userId: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Who a download fetch says it is, by EVERY credential this server issues:
|
|
41
|
+
* a connection key or internal token, or an app session — as a bearer, or
|
|
42
|
+
* as the `bevel_token` cookie a browser sends on its own. Every credential
|
|
43
|
+
* is asked, not the first that verifies: a bearer and a cookie may name two
|
|
44
|
+
* different users, and either one being someone else must refuse the link.
|
|
45
|
+
* Empty when it carries none that verifies.
|
|
46
|
+
*
|
|
47
|
+
* Every kind, because the question is "is this fetch somebody OTHER than
|
|
48
|
+
* the user the link was issued to?", and a credential kind left unasked is a
|
|
49
|
+
* fetch that answers "nobody" while carrying another user's identity. A
|
|
50
|
+
* session is asked by its signature alone, not whether its account is still
|
|
51
|
+
* on: a switched-off account still says who it is, and that is all the
|
|
52
|
+
* route needs to refuse it.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createDownloadFetcherIdentifier(verifiers: DownloadFetcherVerifiers, readCookie: (req: express.Request) => string | null): (req: express.Request) => Promise<string[]>;
|
|
55
|
+
/**
|
|
56
|
+
* `GET /api/agent/downloads/:token` — the outgoing twin of the agent upload
|
|
57
|
+
* route, authenticated by a single-use link token and nothing else.
|
|
58
|
+
*
|
|
59
|
+
* Everything that decides WHAT is served happened when the link was issued:
|
|
60
|
+
* `request_file_download` judged every file against the caller's `read` and
|
|
61
|
+
* `download` rules, ran the deployment's read hook and captured the bytes.
|
|
62
|
+
* This route only hands those bytes over, once, and deletes them when the
|
|
63
|
+
* response ends — sent in full, failed, or abandoned by the client. A token
|
|
64
|
+
* that is unknown, already fetched, expired or another user's gets one 404
|
|
65
|
+
* that says none of those apart.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createAgentDownloadRoutes(deps: AgentDownloadRouteDeps): express.Router;
|
|
68
|
+
/**
|
|
69
|
+
* `attachment` with the name twice (RFC 6266): an ASCII `filename` for a
|
|
70
|
+
* client that reads only that — anything outside printable ASCII, and the
|
|
71
|
+
* quote and backslash, become `_` — and the exact name as RFC 5987
|
|
72
|
+
* `filename*`, where `encodeURIComponent`'s leftovers `'()*` are encoded
|
|
73
|
+
* too, as the grammar requires. Without the plain form such a client would
|
|
74
|
+
* save the file under the URL's last segment: the token.
|
|
75
|
+
*/
|
|
76
|
+
export declare function attachmentDisposition(filename: string): string;
|
|
77
|
+
//# sourceMappingURL=agent-download.routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-download.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/agent-download.routes.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAIzF,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB,4BAA4B,CAAC;AAE9D;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAE9D,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,mBAAmB,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACxD;AAED,oGAAoG;AACpG,MAAM,WAAW,wBAAwB;IACvC,oGAAoG;IACpG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAChG,iFAAiF;IACjF,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,wBAAwB,EACnC,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,MAAM,GAAG,IAAI,GAClD,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAuB7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CA0DtF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ9D"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import { logger } from '../../shared/logging.js';
|
|
4
|
+
import { DownloadTokenError } from './agent-download.store.js';
|
|
5
|
+
const log = logger('agent-downloads');
|
|
6
|
+
/** The route's own prefix, under `/api`. */
|
|
7
|
+
export const AGENT_DOWNLOAD_ROUTE = '/agent/downloads/:token';
|
|
8
|
+
/**
|
|
9
|
+
* The same endpoint with the token in the `x-download-token` HEADER instead of
|
|
10
|
+
* the path — the address `downloadUrl` is, without its last segment. For a
|
|
11
|
+
* caller that would rather its credential not land in an access log or a
|
|
12
|
+
* shell history, as the upload route's header form is.
|
|
13
|
+
*/
|
|
14
|
+
export const AGENT_DOWNLOAD_HEADER_ROUTE = '/agent/downloads';
|
|
15
|
+
/**
|
|
16
|
+
* Who a download fetch says it is, by EVERY credential this server issues:
|
|
17
|
+
* a connection key or internal token, or an app session — as a bearer, or
|
|
18
|
+
* as the `bevel_token` cookie a browser sends on its own. Every credential
|
|
19
|
+
* is asked, not the first that verifies: a bearer and a cookie may name two
|
|
20
|
+
* different users, and either one being someone else must refuse the link.
|
|
21
|
+
* Empty when it carries none that verifies.
|
|
22
|
+
*
|
|
23
|
+
* Every kind, because the question is "is this fetch somebody OTHER than
|
|
24
|
+
* the user the link was issued to?", and a credential kind left unasked is a
|
|
25
|
+
* fetch that answers "nobody" while carrying another user's identity. A
|
|
26
|
+
* session is asked by its signature alone, not whether its account is still
|
|
27
|
+
* on: a switched-off account still says who it is, and that is all the
|
|
28
|
+
* route needs to refuse it.
|
|
29
|
+
*/
|
|
30
|
+
export function createDownloadFetcherIdentifier(verifiers, readCookie) {
|
|
31
|
+
return async (req) => {
|
|
32
|
+
const header = req.headers.authorization;
|
|
33
|
+
const bearer = typeof header === 'string' && header.toLowerCase().startsWith('bearer ')
|
|
34
|
+
? header.slice(header.indexOf(' ') + 1).trim()
|
|
35
|
+
: '';
|
|
36
|
+
const users = new Set();
|
|
37
|
+
for (const token of [bearer, readCookie(req) ?? '']) {
|
|
38
|
+
if (token === '')
|
|
39
|
+
continue;
|
|
40
|
+
const tool = await verifiers.verifyToolToken(token).catch(() => ({ ok: false }));
|
|
41
|
+
if (tool.ok) {
|
|
42
|
+
users.add(tool.auth.userId);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
users.add(verifiers.verifySession(token).userId);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Not a session either: this credential names nobody.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return [...users];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `GET /api/agent/downloads/:token` — the outgoing twin of the agent upload
|
|
57
|
+
* route, authenticated by a single-use link token and nothing else.
|
|
58
|
+
*
|
|
59
|
+
* Everything that decides WHAT is served happened when the link was issued:
|
|
60
|
+
* `request_file_download` judged every file against the caller's `read` and
|
|
61
|
+
* `download` rules, ran the deployment's read hook and captured the bytes.
|
|
62
|
+
* This route only hands those bytes over, once, and deletes them when the
|
|
63
|
+
* response ends — sent in full, failed, or abandoned by the client. A token
|
|
64
|
+
* that is unknown, already fetched, expired or another user's gets one 404
|
|
65
|
+
* that says none of those apart.
|
|
66
|
+
*/
|
|
67
|
+
export function createAgentDownloadRoutes(deps) {
|
|
68
|
+
const router = express.Router();
|
|
69
|
+
const { downloads, identify } = deps;
|
|
70
|
+
const handle = async (req, res) => {
|
|
71
|
+
// THE TOKEN FIRST, before anything about the request is answered.
|
|
72
|
+
const token = tokenOf(req);
|
|
73
|
+
let claimed;
|
|
74
|
+
try {
|
|
75
|
+
const fetchers = identify ? await identify(req).catch(() => []) : [];
|
|
76
|
+
claimed = downloads.claim(token, fetchers);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (err instanceof DownloadTokenError) {
|
|
80
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
81
|
+
res.status(err.status).json({ error: err.message });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
log.error('download failed:', { err });
|
|
85
|
+
res.status(500).json({ error: 'Download failed' });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// The bytes go when the response ENDS, however it ends: `close` fires
|
|
89
|
+
// after a complete send, after a failed one, and when the client leaves.
|
|
90
|
+
let finished = false;
|
|
91
|
+
const done = () => {
|
|
92
|
+
if (finished)
|
|
93
|
+
return;
|
|
94
|
+
finished = true;
|
|
95
|
+
void downloads.finish(token);
|
|
96
|
+
};
|
|
97
|
+
res.on('close', done);
|
|
98
|
+
res.setHeader('Content-Type', claimed.contentType);
|
|
99
|
+
res.setHeader('Content-Length', String(claimed.bytes));
|
|
100
|
+
res.setHeader('Content-Disposition', attachmentDisposition(claimed.filename));
|
|
101
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
102
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
103
|
+
const stream = createReadStream(claimed.absolutePath);
|
|
104
|
+
stream.on('error', (err) => {
|
|
105
|
+
// The detail stays in the log: it quotes the store's absolute path.
|
|
106
|
+
log.error('could not read a stored download:', { err });
|
|
107
|
+
if (!res.headersSent)
|
|
108
|
+
res.status(500).json({ error: 'Download failed' });
|
|
109
|
+
else
|
|
110
|
+
res.destroy();
|
|
111
|
+
done();
|
|
112
|
+
});
|
|
113
|
+
stream.pipe(res);
|
|
114
|
+
};
|
|
115
|
+
// Express answers HEAD with the GET handler, which would spend the link on a
|
|
116
|
+
// probe that never receives the bytes. A link is for one GET, so HEAD is
|
|
117
|
+
// refused before the token is looked at.
|
|
118
|
+
const noHead = (_req, res) => {
|
|
119
|
+
res.setHeader('Allow', 'GET');
|
|
120
|
+
res.status(405).end();
|
|
121
|
+
};
|
|
122
|
+
router.head(AGENT_DOWNLOAD_ROUTE, noHead);
|
|
123
|
+
router.head(AGENT_DOWNLOAD_HEADER_ROUTE, noHead);
|
|
124
|
+
router.get(AGENT_DOWNLOAD_ROUTE, handle);
|
|
125
|
+
router.get(AGENT_DOWNLOAD_HEADER_ROUTE, handle);
|
|
126
|
+
return router;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* `attachment` with the name twice (RFC 6266): an ASCII `filename` for a
|
|
130
|
+
* client that reads only that — anything outside printable ASCII, and the
|
|
131
|
+
* quote and backslash, become `_` — and the exact name as RFC 5987
|
|
132
|
+
* `filename*`, where `encodeURIComponent`'s leftovers `'()*` are encoded
|
|
133
|
+
* too, as the grammar requires. Without the plain form such a client would
|
|
134
|
+
* save the file under the URL's last segment: the token.
|
|
135
|
+
*/
|
|
136
|
+
export function attachmentDisposition(filename) {
|
|
137
|
+
const name = filename.replace(/[\r\n]/g, '') || 'download';
|
|
138
|
+
const ascii = name.replace(/[^\x20-\x7e]|["\\]/g, '_');
|
|
139
|
+
const encoded = encodeURIComponent(name).replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
140
|
+
return `attachment; filename="${ascii}"; filename*=UTF-8''${encoded}`;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The token the fetch presented: the `:token` path segment, or the
|
|
144
|
+
* `x-download-token` header on the bare address. The path wins when both are
|
|
145
|
+
* present. No token at all is the empty string, which meets the one refusal.
|
|
146
|
+
*/
|
|
147
|
+
function tokenOf(req) {
|
|
148
|
+
const inPath = req.params.token;
|
|
149
|
+
if (typeof inPath === 'string' && inPath.trim() !== '')
|
|
150
|
+
return inPath.trim();
|
|
151
|
+
const header = req.headers['x-download-token'];
|
|
152
|
+
if (typeof header === 'string' && header.trim() !== '')
|
|
153
|
+
return header.trim();
|
|
154
|
+
return '';
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=agent-download.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-download.routes.js","sourceRoot":"","sources":["../../../src/modules/workspace/agent-download.routes.ts"],"names":[],"mappings":"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"}
|