@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
|
@@ -49,6 +49,8 @@ interface Harness {
|
|
|
49
49
|
server: Server;
|
|
50
50
|
baseUrl: string;
|
|
51
51
|
canDownload: ReturnType<typeof vi.fn>;
|
|
52
|
+
canReadBatch: ReturnType<typeof vi.fn>;
|
|
53
|
+
canDownloadBatch: ReturnType<typeof vi.fn>;
|
|
52
54
|
canWrite: ReturnType<typeof vi.fn>;
|
|
53
55
|
createFolderZip: ReturnType<typeof vi.fn>;
|
|
54
56
|
}
|
|
@@ -70,14 +72,21 @@ async function makeHarness(opts: {
|
|
|
70
72
|
return opts.canDownload;
|
|
71
73
|
});
|
|
72
74
|
const canWrite = vi.fn();
|
|
75
|
+
// The /file/raw route now read-gates before the download gate; these tests
|
|
76
|
+
// exercise the download verb, so reads pass.
|
|
77
|
+
const canReadBatch = vi.fn(async (_w: string, _e: string, paths: string[]) => new Map(paths.map((p) => [p, true])));
|
|
78
|
+
// Every entry of a folder is judged for download too; by default each gets
|
|
79
|
+
// the folder's own verdict.
|
|
80
|
+
const canDownloadBatch = vi.fn(async (_w: string, _e: string, paths: string[]) =>
|
|
81
|
+
new Map(paths.map((p) => [p, opts.canDownload])),
|
|
82
|
+
);
|
|
73
83
|
const accessControl: IAccessControl = {
|
|
74
84
|
canWrite,
|
|
75
85
|
canWriteBatch: vi.fn(),
|
|
76
|
-
// The /file/raw route now read-gates before the download gate; these tests
|
|
77
|
-
// exercise the download verb, so reads pass.
|
|
78
86
|
canRead: vi.fn(async () => true),
|
|
79
|
-
canReadBatch
|
|
87
|
+
canReadBatch,
|
|
80
88
|
canDownload,
|
|
89
|
+
canDownloadBatch,
|
|
81
90
|
eligibleWriters: vi.fn(),
|
|
82
91
|
eligibleWriterEmails: vi.fn(),
|
|
83
92
|
invalidate: vi.fn(),
|
|
@@ -152,7 +161,7 @@ async function makeHarness(opts: {
|
|
|
152
161
|
throw new Error(`Unexpected server.address() shape: ${JSON.stringify(addr)}`);
|
|
153
162
|
}
|
|
154
163
|
const port = (addr as AddressInfo).port;
|
|
155
|
-
return { server, baseUrl: `http://127.0.0.1:${port}`, canDownload, canWrite, createFolderZip };
|
|
164
|
+
return { server, baseUrl: `http://127.0.0.1:${port}`, canDownload, canReadBatch, canDownloadBatch, canWrite, createFolderZip };
|
|
156
165
|
}
|
|
157
166
|
|
|
158
167
|
async function closeServer(server: Server): Promise<void> {
|
|
@@ -338,6 +347,8 @@ describe('GET /workspace/:id/folder/zip — gated on Download role', () => {
|
|
|
338
347
|
expect(res.status).toBe(200);
|
|
339
348
|
expect(res.headers.get('content-type')).toBe('application/zip');
|
|
340
349
|
expect(res.headers.get('x-content-type-options')).toBe('nosniff');
|
|
350
|
+
// The archive holds one caller's verdicts, so a shared cache never keeps it.
|
|
351
|
+
expect(res.headers.get('cache-control')).toBe('private, no-store');
|
|
341
352
|
const dispo = res.headers.get('content-disposition');
|
|
342
353
|
expect(dispo).toBeTruthy();
|
|
343
354
|
expect(dispo).toContain('attachment');
|
|
@@ -349,7 +360,43 @@ describe('GET /workspace/:id/folder/zip — gated on Download role', () => {
|
|
|
349
360
|
// verb is resolved on its repo-relative form, and the service is handed the
|
|
350
361
|
// repository path it was judged as.
|
|
351
362
|
expect(h.canDownload).toHaveBeenCalledWith(WORKSPACE_ID, USER.email, 'Knowledge/Sales');
|
|
352
|
-
expect(h.createFolderZip).
|
|
363
|
+
expect(h.createFolderZip.mock.calls[0]!.slice(0, 2)).toEqual([WORKSPACE_ID, `${KB}/Knowledge/Sales`]);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('judges every file the zip would pack: unreadable ones and undownloadable ones are left out', async () => {
|
|
367
|
+
h = await makeHarness({ canDownload: true });
|
|
368
|
+
// What the service finds under the folder; the route's filter decides.
|
|
369
|
+
const found = ['Knowledge/Sales/open.md', 'Knowledge/Sales/hidden.md', 'Knowledge/Sales/no-save.md'];
|
|
370
|
+
h.canReadBatch.mockImplementation(async (_w: string, _e: string, paths: string[]) =>
|
|
371
|
+
new Map(paths.map((p) => [p, p !== 'Knowledge/Sales/hidden.md'])),
|
|
372
|
+
);
|
|
373
|
+
h.canDownloadBatch.mockImplementation(async (_w: string, _e: string, paths: string[]) =>
|
|
374
|
+
new Map(paths.map((p) => [p, p !== 'Knowledge/Sales/no-save.md'])),
|
|
375
|
+
);
|
|
376
|
+
let packed: ReadonlySet<string> | null = null;
|
|
377
|
+
h.createFolderZip.mockImplementation(
|
|
378
|
+
async (_id: string, _p: string, include: (paths: string[]) => Promise<ReadonlySet<string>>) => {
|
|
379
|
+
packed = await include(found);
|
|
380
|
+
return ZIP_BYTES;
|
|
381
|
+
},
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
const res = await fetch(
|
|
385
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Sales')}&download=1`,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
expect(res.status).toBe(200);
|
|
389
|
+
expect([...packed!]).toEqual(['Knowledge/Sales/open.md']);
|
|
390
|
+
// Read first, then download on what is readable: a file the caller may
|
|
391
|
+
// not see is never asked about for download, so nothing about it is
|
|
392
|
+
// computed or counted.
|
|
393
|
+
expect(h.canReadBatch).toHaveBeenCalledWith(WORKSPACE_ID, USER.email, found);
|
|
394
|
+
expect(h.canDownloadBatch).toHaveBeenCalledWith(WORKSPACE_ID, USER.email, [
|
|
395
|
+
'Knowledge/Sales/open.md',
|
|
396
|
+
'Knowledge/Sales/no-save.md',
|
|
397
|
+
]);
|
|
398
|
+
// The count covers the readable file left out, not the hidden one.
|
|
399
|
+
expect(res.headers.get('x-withheld-files')).toBe('1');
|
|
353
400
|
});
|
|
354
401
|
|
|
355
402
|
it('user without Download role gets 403 and no zip is built', async () => {
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { Server } from 'node:http';
|
|
2
|
+
import type { AddressInfo } from 'node:net';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import AdmZip from 'adm-zip';
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
9
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
10
|
+
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
11
|
+
import type { IWorkflowService } from '@bevel-software/platform-shared';
|
|
12
|
+
import type { ICreatorAccess } from '../../access-model/creator.js';
|
|
13
|
+
import type { IAdminAccessService } from '../../admin/admin.interface.js';
|
|
14
|
+
import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
15
|
+
import type { AuthService } from '../../auth/auth.service.js';
|
|
16
|
+
import { AccessControlService } from '../../access/access-control.service.js';
|
|
17
|
+
import { createWorkspaceRoutes } from '../workspace.routes.js';
|
|
18
|
+
import { WorkspaceService } from '../workspace.service.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The folder download over the REAL resolver and the REAL zip builder.
|
|
22
|
+
*
|
|
23
|
+
* `download` on a folder let the caller ask for its zip, and the zip then
|
|
24
|
+
* packed every file under the folder: no file was judged against its own
|
|
25
|
+
* rules, so a caller got files whose frontmatter denied them `download`, files
|
|
26
|
+
* whose frontmatter denied them `read`, and whole sub-folders a nested
|
|
27
|
+
* access.md closed to them — files the per-file route refused and the file
|
|
28
|
+
* tree never showed (found 2026-10-07). The four cases below are the ones
|
|
29
|
+
* that were verified leaking; each must now be absent from the archive, and
|
|
30
|
+
* the one the caller may download must still be in it.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const KB = 'knowledge-base';
|
|
34
|
+
const WORKSPACE_ID = 'target-company-state';
|
|
35
|
+
|
|
36
|
+
const stubCreatorAccess: ICreatorAccess = {
|
|
37
|
+
planForCreate: async () => null,
|
|
38
|
+
grantInExtractedFile: async () => null,
|
|
39
|
+
noteAccessFileWritten: () => {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let caller = { id: 'user-1', email: 'ana@x.io', name: 'Ana' };
|
|
43
|
+
|
|
44
|
+
const ROLES_YAML = `roles:
|
|
45
|
+
Admin:
|
|
46
|
+
- admin@x.io
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
/** The folder Ana may download, with the files the rules below decide over. */
|
|
50
|
+
const FILES: Record<string, string> = {
|
|
51
|
+
'Shared/access.md': '---\n---\ndownload:\n - Ana <ana@x.io>\n',
|
|
52
|
+
'Shared/Open.md': '# open\n',
|
|
53
|
+
'Shared/Node-Deny-Download.md': '---\ndownload:\n - deny Ana <ana@x.io>\n---\n# no save\n',
|
|
54
|
+
'Shared/Node-Deny-Read.md': '---\nread:\n - deny Ana <ana@x.io>\n---\n# hidden\n',
|
|
55
|
+
'Shared/Inner/access.md': '---\n---\nread:\n - deny Ana <ana@x.io>\ndownload:\n - deny Ana <ana@x.io>\n',
|
|
56
|
+
'Shared/Inner/Plan.md': '# hidden plan\n',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
interface Harness {
|
|
60
|
+
server: Server;
|
|
61
|
+
baseUrl: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe('GET /folder/zip judges every file it packs (real resolver, real zip)', () => {
|
|
65
|
+
let root: string;
|
|
66
|
+
let h: Harness | null = null;
|
|
67
|
+
|
|
68
|
+
beforeEach(async () => {
|
|
69
|
+
root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-zip-access-'));
|
|
70
|
+
caller = { id: 'user-1', email: 'ana@x.io', name: 'Ana' };
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(async () => {
|
|
74
|
+
if (h) {
|
|
75
|
+
await new Promise<void>((resolve, reject) => h!.server.close((e) => (e ? reject(e) : resolve())));
|
|
76
|
+
h = null;
|
|
77
|
+
}
|
|
78
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
async function makeHarness(files: Record<string, string>): Promise<Harness> {
|
|
82
|
+
const workspaceDir = path.join(root, WORKSPACE_ID);
|
|
83
|
+
const repo = path.join(workspaceDir, KB);
|
|
84
|
+
// A `.git` folder is what makes the service adopt the clone without cloning.
|
|
85
|
+
await fs.mkdir(path.join(repo, '.git'), { recursive: true });
|
|
86
|
+
for (const [rel, contents] of Object.entries({ 'roles.yaml': ROLES_YAML, ...files })) {
|
|
87
|
+
const abs = path.join(repo, rel);
|
|
88
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
89
|
+
await fs.writeFile(abs, contents);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const workspaceService = new WorkspaceService(
|
|
93
|
+
root,
|
|
94
|
+
'https://example.invalid/kb.git',
|
|
95
|
+
testKbContext({ kbDirName: KB }),
|
|
96
|
+
new NodeFs(),
|
|
97
|
+
);
|
|
98
|
+
const accessControl = new AccessControlService(workspaceService, KB, new NodeFs());
|
|
99
|
+
const authService = { getUserById: async () => caller } as unknown as AuthService;
|
|
100
|
+
|
|
101
|
+
const app = express();
|
|
102
|
+
app.use(express.json());
|
|
103
|
+
app.use('/api', (req, _res, next) => {
|
|
104
|
+
(req as unknown as { userId: string }).userId = caller.id;
|
|
105
|
+
next();
|
|
106
|
+
});
|
|
107
|
+
app.use(
|
|
108
|
+
'/api',
|
|
109
|
+
createWorkspaceRoutes(
|
|
110
|
+
workspaceService,
|
|
111
|
+
authService,
|
|
112
|
+
{} as unknown as IWorkflowService,
|
|
113
|
+
{} as unknown as WorkflowEventBus,
|
|
114
|
+
accessControl,
|
|
115
|
+
testKbContext({ kbDirName: KB }),
|
|
116
|
+
stubCreatorAccess,
|
|
117
|
+
{ isAdmin: async () => false } as unknown as IAdminAccessService,
|
|
118
|
+
new NodeFs(),
|
|
119
|
+
),
|
|
120
|
+
);
|
|
121
|
+
const server = await new Promise<Server>((resolve) => {
|
|
122
|
+
const s = app.listen(0, () => resolve(s));
|
|
123
|
+
});
|
|
124
|
+
const addr = server.address() as AddressInfo;
|
|
125
|
+
return { server, baseUrl: `http://127.0.0.1:${addr.port}` };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const zipOf = (base: string, folder: string) =>
|
|
129
|
+
fetch(`${base}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent(folder)}&download=1`);
|
|
130
|
+
|
|
131
|
+
const entriesOf = async (res: Response) =>
|
|
132
|
+
new AdmZip(Buffer.from(await res.arrayBuffer()))
|
|
133
|
+
.getEntries()
|
|
134
|
+
.map((e) => e.entryName)
|
|
135
|
+
.sort();
|
|
136
|
+
|
|
137
|
+
it('packs the file the caller may download, and none of the ones their own rules withhold', async () => {
|
|
138
|
+
h = await makeHarness(FILES);
|
|
139
|
+
|
|
140
|
+
const res = await zipOf(h.baseUrl, `${KB}/Shared`);
|
|
141
|
+
|
|
142
|
+
expect(res.status).toBe(200);
|
|
143
|
+
expect(res.headers.get('content-type')).toBe('application/zip');
|
|
144
|
+
// Ana's archive, with Ana's verdicts: never served to anyone else from a cache.
|
|
145
|
+
expect(res.headers.get('cache-control')).toBe('private, no-store');
|
|
146
|
+
expect(await entriesOf(res)).toEqual(['Shared/Open.md', 'Shared/access.md']);
|
|
147
|
+
// One file Ana can see but not save is counted; the two she cannot see
|
|
148
|
+
// and the closed folder are not — the count says nothing the tree does not.
|
|
149
|
+
expect(res.headers.get('x-withheld-files')).toBe('1');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('the per-file route agrees on every one of them', async () => {
|
|
153
|
+
h = await makeHarness(FILES);
|
|
154
|
+
const raw = (p: string) =>
|
|
155
|
+
fetch(`${h!.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent(`${KB}/${p}`)}&download=1`);
|
|
156
|
+
|
|
157
|
+
expect((await raw('Shared/Open.md')).status).toBe(200);
|
|
158
|
+
expect((await raw('Shared/Node-Deny-Download.md')).status).toBe(403);
|
|
159
|
+
expect((await raw('Shared/Node-Deny-Read.md')).status).toBe(403);
|
|
160
|
+
expect((await raw('Shared/Inner/Plan.md')).status).toBe(403);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('still refuses the folder itself to someone with no download grant', async () => {
|
|
164
|
+
h = await makeHarness(FILES);
|
|
165
|
+
caller = { id: 'user-2', email: 'mallory@x.io', name: 'Mallory' };
|
|
166
|
+
|
|
167
|
+
const res = await zipOf(h.baseUrl, `${KB}/Shared`);
|
|
168
|
+
|
|
169
|
+
expect(res.status).toBe(403);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// `access.md` itself stays in HERE: this fixture's declares nothing for
|
|
173
|
+
// itself (its frontmatter is empty), so the folder's rules — which admit
|
|
174
|
+
// the caller — admit the file too. That is not a guarantee: an `access.md`
|
|
175
|
+
// is judged like any file, by its own frontmatter and the folder chain, and
|
|
176
|
+
// one whose frontmatter denies the caller its read is left out of the
|
|
177
|
+
// archive exactly as the single-file route would refuse it.
|
|
178
|
+
it('answers an archive holding only the grant file, not an error, when every content file is withheld', async () => {
|
|
179
|
+
h = await makeHarness({
|
|
180
|
+
'Shared/access.md': '---\n---\ndownload:\n - Ana <ana@x.io>\n',
|
|
181
|
+
'Shared/Only.md': '---\ndownload:\n - deny Ana <ana@x.io>\n---\n# no save\n',
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const res = await zipOf(h.baseUrl, `${KB}/Shared`);
|
|
185
|
+
|
|
186
|
+
expect(res.status).toBe(200);
|
|
187
|
+
expect(await entriesOf(res)).toEqual(['Shared/access.md']);
|
|
188
|
+
expect(res.headers.get('x-withheld-files')).toBe('1');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -127,6 +127,13 @@ describe('a bootstrap in flight when the working copy is taken away', () => {
|
|
|
127
127
|
const svc = service(runner);
|
|
128
128
|
// Nothing on disk: this is a first clone, which is what takes seconds.
|
|
129
129
|
const opening = svc.getOrCreateForBranch(BRANCH);
|
|
130
|
+
// Told, rather than handed a cached path to a working copy of the
|
|
131
|
+
// repository that was replaced — which every later read would ENOENT on,
|
|
132
|
+
// with nothing left to re-clone it. The expectation is attached NOW: the
|
|
133
|
+
// refusal lands somewhere inside the timer turns below, and a rejection
|
|
134
|
+
// with no handler on it across a turn is what Vitest reports as unhandled
|
|
135
|
+
// — a flake that failed the suite on CI with every test green.
|
|
136
|
+
const refused = expect(opening).rejects.toThrow(/replaced while the "main" working copy was being created/);
|
|
130
137
|
await vi.waitFor(() => expect(calls.length).toBeGreaterThan(0));
|
|
131
138
|
|
|
132
139
|
// The replacement lands while the clone runs.
|
|
@@ -139,9 +146,6 @@ describe('a bootstrap in flight when the working copy is taken away', () => {
|
|
|
139
146
|
await new Promise((r) => setTimeout(r, 0));
|
|
140
147
|
}
|
|
141
148
|
|
|
142
|
-
|
|
143
|
-
// repository that was replaced — which every later read would ENOENT on,
|
|
144
|
-
// with nothing left to re-clone it.
|
|
145
|
-
await expect(opening).rejects.toThrow(/replaced while the "main" working copy was being created/);
|
|
149
|
+
await refused;
|
|
146
150
|
});
|
|
147
151
|
});
|
|
@@ -698,13 +698,55 @@ describe('WorkspaceService.createFolderZip', () => {
|
|
|
698
698
|
}));
|
|
699
699
|
}
|
|
700
700
|
|
|
701
|
+
/** The filter a caller with every permission passes: pack everything found. */
|
|
702
|
+
const ALL = async (paths: string[]) => new Set(paths);
|
|
703
|
+
|
|
704
|
+
it('packs only what the filter allows, and asks it once with every file as a repository path', async () => {
|
|
705
|
+
const dir = path.join(repoDir, 'judged');
|
|
706
|
+
await fs.mkdir(path.join(dir, 'inner'), { recursive: true });
|
|
707
|
+
await fs.writeFile(path.join(dir, 'open.md'), 'open');
|
|
708
|
+
await fs.writeFile(path.join(dir, 'withheld.md'), 'withheld');
|
|
709
|
+
await fs.writeFile(path.join(dir, 'inner', 'deep.md'), 'deep');
|
|
710
|
+
const asked: string[][] = [];
|
|
711
|
+
const include = async (paths: string[]) => {
|
|
712
|
+
asked.push([...paths].sort());
|
|
713
|
+
return new Set(paths.filter((p) => p !== 'judged/withheld.md'));
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
const buf = await svc.createFolderZip(workspaceId, 'judged', include);
|
|
717
|
+
|
|
718
|
+
// Asked once, with every file the walk found, keyed the way the access
|
|
719
|
+
// rules are: relative to the repository, not to the folder or the workspace.
|
|
720
|
+
expect(asked).toEqual([['judged/inner/deep.md', 'judged/open.md', 'judged/withheld.md']]);
|
|
721
|
+
const names = (await unzipEntries(buf)).map((e) => e.name).sort();
|
|
722
|
+
expect(names).toEqual(['judged/inner/deep.md', 'judged/open.md']);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('keys the files the same way whether the folder is written with a trailing slash or not', async () => {
|
|
726
|
+
const dir = path.join(repoDir, 'slashed');
|
|
727
|
+
await fs.mkdir(dir, { recursive: true });
|
|
728
|
+
await fs.writeFile(path.join(dir, 'a.md'), 'a');
|
|
729
|
+
const asked: string[][] = [];
|
|
730
|
+
const include = async (paths: string[]) => {
|
|
731
|
+
asked.push(paths);
|
|
732
|
+
return new Set(paths);
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
await svc.createFolderZip(workspaceId, 'slashed/', include);
|
|
736
|
+
await svc.createFolderZip(workspaceId, `${'knowledge-base'}/slashed//`, include);
|
|
737
|
+
|
|
738
|
+
// A `slashed//a.md` key would match no verdict, and the filter would
|
|
739
|
+
// withhold the whole folder without a word.
|
|
740
|
+
expect(asked).toEqual([['slashed/a.md'], ['slashed/a.md']]);
|
|
741
|
+
});
|
|
742
|
+
|
|
701
743
|
it('zips a folder prefixing entries with the folder name', async () => {
|
|
702
744
|
const dir = path.join(repoDir, 'docs');
|
|
703
745
|
await fs.mkdir(dir, { recursive: true });
|
|
704
746
|
await fs.writeFile(path.join(dir, 'a.md'), 'alpha');
|
|
705
747
|
await fs.writeFile(path.join(dir, 'b.md'), 'beta');
|
|
706
748
|
|
|
707
|
-
const buf = await svc.createFolderZip(workspaceId, 'docs');
|
|
749
|
+
const buf = await svc.createFolderZip(workspaceId, 'docs', ALL);
|
|
708
750
|
const entries = await unzipEntries(buf);
|
|
709
751
|
const names = entries.map((e) => e.name).sort();
|
|
710
752
|
expect(names).toEqual(['docs/a.md', 'docs/b.md']);
|
|
@@ -719,7 +761,7 @@ describe('WorkspaceService.createFolderZip', () => {
|
|
|
719
761
|
await fs.writeFile(path.join(dir, 'nested', 'mid.md'), 'mid');
|
|
720
762
|
await fs.writeFile(path.join(dir, 'nested', 'deep', 'leaf.md'), 'leaf');
|
|
721
763
|
|
|
722
|
-
const buf = await svc.createFolderZip(workspaceId, 'tree');
|
|
764
|
+
const buf = await svc.createFolderZip(workspaceId, 'tree', ALL);
|
|
723
765
|
const names = (await unzipEntries(buf)).map((e) => e.name).sort();
|
|
724
766
|
expect(names).toEqual([
|
|
725
767
|
'tree/nested/deep/leaf.md',
|
|
@@ -736,7 +778,7 @@ describe('WorkspaceService.createFolderZip', () => {
|
|
|
736
778
|
await fs.writeFile(path.join(dir, '.gitkeep'), '');
|
|
737
779
|
await fs.writeFile(path.join(dir, 'real.md'), 'real');
|
|
738
780
|
|
|
739
|
-
const buf = await svc.createFolderZip(workspaceId, 'mixed');
|
|
781
|
+
const buf = await svc.createFolderZip(workspaceId, 'mixed', ALL);
|
|
740
782
|
const names = (await unzipEntries(buf)).map((e) => e.name).sort();
|
|
741
783
|
expect(names).toEqual(['mixed/real.md']);
|
|
742
784
|
});
|
|
@@ -748,7 +790,7 @@ describe('WorkspaceService.createFolderZip', () => {
|
|
|
748
790
|
await fs.writeFile(path.join(dir, 'public.md'), 'pub');
|
|
749
791
|
await fs.writeFile(path.join(dir, 'secret.md'), 'shh');
|
|
750
792
|
|
|
751
|
-
const buf = await svc.createFolderZip(workspaceId, 'with-ignore');
|
|
793
|
+
const buf = await svc.createFolderZip(workspaceId, 'with-ignore', ALL);
|
|
752
794
|
const names = (await unzipEntries(buf)).map((e) => e.name).sort();
|
|
753
795
|
expect(names).toContain('with-ignore/public.md');
|
|
754
796
|
expect(names).not.toContain('with-ignore/secret.md');
|
|
@@ -756,17 +798,17 @@ describe('WorkspaceService.createFolderZip', () => {
|
|
|
756
798
|
|
|
757
799
|
it('refuses to zip a file (not a directory)', async () => {
|
|
758
800
|
await fs.writeFile(path.join(repoDir, 'lone.md'), 'one');
|
|
759
|
-
await expect(svc.createFolderZip(workspaceId, 'lone.md')).rejects.toThrow('Not a directory');
|
|
801
|
+
await expect(svc.createFolderZip(workspaceId, 'lone.md', ALL)).rejects.toThrow('Not a directory');
|
|
760
802
|
});
|
|
761
803
|
|
|
762
804
|
it('refuses a traversing path, and reads an unprefixed one as the repository folder', async () => {
|
|
763
|
-
await expect(svc.createFolderZip(workspaceId, '../escape')).rejects.toThrow(
|
|
805
|
+
await expect(svc.createFolderZip(workspaceId, '../escape', ALL)).rejects.toThrow(
|
|
764
806
|
'is outside the knowledge base repository',
|
|
765
807
|
);
|
|
766
808
|
// The unprefixed spelling is the repository's own folder, so it zips.
|
|
767
809
|
await fs.mkdir(path.join(repoDir, 'unprefixed'), { recursive: true });
|
|
768
810
|
await fs.writeFile(path.join(repoDir, 'unprefixed', 'a.md'), 'a');
|
|
769
|
-
const buf = await svc.createFolderZip(workspaceId, 'unprefixed');
|
|
811
|
+
const buf = await svc.createFolderZip(workspaceId, 'unprefixed', ALL);
|
|
770
812
|
expect((await unzipEntries(buf)).map((e) => e.name)).toEqual(['unprefixed/a.md']);
|
|
771
813
|
});
|
|
772
814
|
|
|
@@ -796,7 +838,7 @@ describe('WorkspaceService.createFolderZip', () => {
|
|
|
796
838
|
return result;
|
|
797
839
|
});
|
|
798
840
|
try {
|
|
799
|
-
await expect(svc.createFolderZip(workspaceId, 'too-big'))
|
|
841
|
+
await expect(svc.createFolderZip(workspaceId, 'too-big', ALL))
|
|
800
842
|
.rejects.toBeInstanceOf(FolderTooLargeError);
|
|
801
843
|
} finally {
|
|
802
844
|
statSpy.mockRestore();
|
|
@@ -9,6 +9,7 @@ import { afterEach, describe, expect, it } from 'vitest';
|
|
|
9
9
|
import '@utcp/http'; // registers the http CallTemplate serializer + protocol
|
|
10
10
|
import { UtcpManualSerializer } from '@utcp/sdk';
|
|
11
11
|
import { HttpCallTemplateSerializer } from '@utcp/http';
|
|
12
|
+
import { exampleArguments } from '@bevel-software/platform-mcp-core';
|
|
12
13
|
import { CodeModeUtcpClient } from '../../code-mode/index.js';
|
|
13
14
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
14
15
|
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
@@ -167,7 +168,12 @@ describe('workspace tools — a branch that cannot be opened', () => {
|
|
|
167
168
|
} as ToolAuth;
|
|
168
169
|
next();
|
|
169
170
|
},
|
|
170
|
-
|
|
171
|
+
// Wired as the composition root wires it, so a missing branch is
|
|
172
|
+
// answered before the tool runs, as it is in production.
|
|
173
|
+
createToolHandlerFactory(resolve, undefined, {
|
|
174
|
+
defaultBranch: () => 'target-company-state',
|
|
175
|
+
isMissing: (branch) => workspaces!.isBranchMissing(branch),
|
|
176
|
+
}),
|
|
171
177
|
new SpillStore(spillRoot),
|
|
172
178
|
new DocExtractService(docCacheDir),
|
|
173
179
|
allowAll,
|
|
@@ -272,6 +278,24 @@ describe('workspace tools — a branch that cannot be opened', () => {
|
|
|
272
278
|
});
|
|
273
279
|
}
|
|
274
280
|
|
|
281
|
+
/**
|
|
282
|
+
* The platform's file tools keep the refusal they had: `read_file` is
|
|
283
|
+
* still `required`, answered the same sentence by the tool handler that
|
|
284
|
+
* now answers it for every tool that takes a branch.
|
|
285
|
+
*/
|
|
286
|
+
it('read_file refuses a call without a branch as before', async () => {
|
|
287
|
+
const base = await start();
|
|
288
|
+
|
|
289
|
+
const res = await callTool(base, 'read_file', { path: 'knowledge-base/a.md' });
|
|
290
|
+
|
|
291
|
+
expect(res.status).toBe(400);
|
|
292
|
+
expect(res.body).toEqual({
|
|
293
|
+
kind: 'branch-required',
|
|
294
|
+
error: '`branch` is required: pass the branch (draft) you are working on.',
|
|
295
|
+
});
|
|
296
|
+
expect(await workspaceDirs()).toEqual([]);
|
|
297
|
+
});
|
|
298
|
+
|
|
275
299
|
it('never writes `undefined` or `null` as a branch name into the answer', async () => {
|
|
276
300
|
const base = await start();
|
|
277
301
|
|
|
@@ -407,6 +431,25 @@ describe('workspace tools — a branch that cannot be opened', () => {
|
|
|
407
431
|
expect(await workspaceDirs()).toEqual([]);
|
|
408
432
|
});
|
|
409
433
|
|
|
434
|
+
it('404s a branch that does not exist on every mounted tool that takes one, before the tool runs', async () => {
|
|
435
|
+
const base = await start({ source: 'internal', scope: 'write' });
|
|
436
|
+
const takesBranch = internalTools
|
|
437
|
+
.map((t) => ({ name: t.name, body: (t.inputs as { properties?: { body?: { required?: string[] } } }).properties?.body }))
|
|
438
|
+
.filter(({ body }) => (body?.required ?? []).includes('branch'))
|
|
439
|
+
// Resolves its own branch, and validates it itself first.
|
|
440
|
+
.filter(({ name }) => name !== 'execute_command');
|
|
441
|
+
|
|
442
|
+
for (const { name: tool, body } of takesBranch) {
|
|
443
|
+
// Every other argument the tool requires, so the call matches the tool
|
|
444
|
+
// and it is the branch, not a missing argument, that is refused.
|
|
445
|
+
const args = { ...exampleArguments(body), branch: 'nobody/never-made-this' };
|
|
446
|
+
const res = await callTool(base, tool, args);
|
|
447
|
+
expect(res.status, `${tool} must 404 a branch that does not exist`).toBe(404);
|
|
448
|
+
expect(res.body, tool).toMatchObject({ kind: 'branch-not-found', branch: 'nobody/never-made-this' });
|
|
449
|
+
}
|
|
450
|
+
expect(await workspaceDirs()).toEqual([]);
|
|
451
|
+
});
|
|
452
|
+
|
|
410
453
|
it('410s a branch a listing showed us and origin no longer has', async () => {
|
|
411
454
|
const base = await start();
|
|
412
455
|
workspaces!.noteBranchesListed(['target-company-state', 'alice/draft']);
|