@bevel-software/platform-core-backend 0.33.0 → 0.34.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 +19 -8
- package/agent-guide/html-views.md +231 -5
- package/agent-guide/tool-manuals.md +1 -1
- package/dist/core/catalog-revision.d.ts +12 -1
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +23 -3
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/core-ports.d.ts +41 -0
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +14 -7
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +16 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +37 -12
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +16 -0
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +12 -2
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +17 -9
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +12 -2
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +164 -75
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +22 -24
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +48 -51
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-view.d.ts.map +1 -1
- package/dist/modules/access/access-view.js +12 -11
- package/dist/modules/access/access-view.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +3 -0
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +35 -29
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/groups-admin.routes.d.ts.map +1 -1
- package/dist/modules/access/groups-admin.routes.js +4 -2
- package/dist/modules/access/groups-admin.routes.js.map +1 -1
- package/dist/modules/access/groups-admin.service.d.ts +44 -13
- package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
- package/dist/modules/access/groups-admin.service.js +80 -38
- package/dist/modules/access/groups-admin.service.js.map +1 -1
- package/dist/modules/access/groups-edit.d.ts +1 -1
- package/dist/modules/access/groups-edit.d.ts.map +1 -1
- package/dist/modules/access/groups-edit.js +1 -1
- package/dist/modules/access/groups-edit.js.map +1 -1
- package/dist/modules/access/reference-scan.d.ts +116 -24
- package/dist/modules/access/reference-scan.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.js +305 -44
- package/dist/modules/access/reference-scan.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +17 -8
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +32 -23
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +35 -10
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +60 -20
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/access-splice.d.ts +22 -18
- package/dist/modules/access-model/access-splice.d.ts.map +1 -1
- package/dist/modules/access-model/access-splice.js +20 -14
- package/dist/modules/access-model/access-splice.js.map +1 -1
- package/dist/modules/access-model/group-files.d.ts +1 -0
- package/dist/modules/access-model/group-files.d.ts.map +1 -1
- package/dist/modules/access-model/group-files.js +5 -1
- package/dist/modules/access-model/group-files.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.tools.js +13 -3
- package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +3 -1
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +3 -1
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +5 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +16 -1
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/agent-scope/agent-scope.contract.d.ts +67 -0
- package/dist/modules/agent-scope/agent-scope.contract.d.ts.map +1 -0
- package/dist/modules/agent-scope/agent-scope.contract.js +27 -0
- package/dist/modules/agent-scope/agent-scope.contract.js.map +1 -0
- package/dist/modules/agent-scope/connection-scope.d.ts +111 -0
- package/dist/modules/agent-scope/connection-scope.d.ts.map +1 -0
- package/dist/modules/agent-scope/connection-scope.js +165 -0
- package/dist/modules/agent-scope/connection-scope.js.map +1 -0
- package/dist/modules/agent-scope/index.d.ts +3 -0
- package/dist/modules/agent-scope/index.d.ts.map +1 -0
- package/dist/modules/agent-scope/index.js +3 -0
- package/dist/modules/agent-scope/index.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +2 -0
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/audit.contract.d.ts +6 -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/auth/account-admission.d.ts +12 -0
- package/dist/modules/auth/account-admission.d.ts.map +1 -1
- package/dist/modules/auth/account-admission.js +15 -0
- package/dist/modules/auth/account-admission.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +27 -1
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +42 -4
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts +13 -9
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +35 -29
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +55 -6
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +128 -34
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/index.d.ts +1 -1
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/auth/index.js +1 -1
- package/dist/modules/auth/index.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +2 -9
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +68 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +33 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts +8 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.js +13 -2
- package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
- package/dist/modules/embed/embed.tools.d.ts.map +1 -1
- package/dist/modules/embed/embed.tools.js +5 -0
- package/dist/modules/embed/embed.tools.js.map +1 -1
- package/dist/modules/kb-fs/index.d.ts +2 -0
- package/dist/modules/kb-fs/index.d.ts.map +1 -1
- package/dist/modules/kb-fs/index.js +1 -0
- package/dist/modules/kb-fs/index.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +7 -1
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +26 -6
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +22 -7
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-fs/tmp-ref.d.ts +36 -0
- package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -0
- package/dist/modules/kb-fs/tmp-ref.js +76 -0
- package/dist/modules/kb-fs/tmp-ref.js.map +1 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +7 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +14 -1
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +7 -1
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +39 -4
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +11 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +68 -13
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +45 -10
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +9 -0
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +34 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- 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.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +18 -2
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +5 -1
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +32 -5
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +9 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +15 -3
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/skills/index.d.ts +3 -3
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +0 -2
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +16 -0
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +6 -1
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +24 -12
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +11 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +1 -0
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +4 -1
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/index.d.ts +1 -1
- package/dist/modules/tool-auth/index.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +9 -0
- package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.js +1 -0
- package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +27 -3
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +32 -6
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts +7 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +3 -0
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +15 -0
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +19 -0
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +10 -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-helpers/validate-token.d.ts +3 -1
- package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -1
- package/dist/modules/tool-helpers/validate-token.js +1 -1
- package/dist/modules/tool-helpers/validate-token.js.map +1 -1
- package/dist/modules/tool-manuals/index.d.ts +3 -3
- package/dist/modules/tool-manuals/index.d.ts.map +1 -1
- package/dist/modules/tool-manuals/index.js +0 -2
- package/dist/modules/tool-manuals/index.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +19 -6
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +7 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +41 -4
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +13 -6
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +23 -15
- package/dist/modules/tool-manuals/tool-manuals.service.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 +25 -10
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/manual.routes.d.ts +8 -1
- package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -1
- package/dist/modules/tool-registry/manual.routes.js +27 -7
- package/dist/modules/tool-registry/manual.routes.js.map +1 -1
- package/dist/modules/tool-registry/tool.contract.d.ts +7 -0
- package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-registry/tool.contract.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +14 -9
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +2 -2
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +20 -35
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +2 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +3 -0
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +28 -4
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js +61 -40
- package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
- package/dist/modules/workspace/index.d.ts +8 -0
- package/dist/modules/workspace/index.d.ts.map +1 -1
- package/dist/modules/workspace/index.js +8 -0
- package/dist/modules/workspace/index.js.map +1 -1
- package/dist/modules/workspace/nul-scan.d.ts +19 -0
- package/dist/modules/workspace/nul-scan.d.ts.map +1 -0
- package/dist/modules/workspace/nul-scan.js +55 -0
- package/dist/modules/workspace/nul-scan.js.map +1 -0
- package/dist/modules/workspace/pattern-matcher.contract.d.ts +110 -27
- package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -1
- package/dist/modules/workspace/pattern-matcher.contract.js +30 -13
- package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -1
- package/dist/modules/workspace/ripgrep-search.d.ts +54 -0
- package/dist/modules/workspace/ripgrep-search.d.ts.map +1 -0
- package/dist/modules/workspace/ripgrep-search.js +657 -0
- package/dist/modules/workspace/ripgrep-search.js.map +1 -0
- package/dist/modules/workspace/ripgrep-version.d.ts +15 -0
- package/dist/modules/workspace/ripgrep-version.d.ts.map +1 -0
- package/dist/modules/workspace/ripgrep-version.js +31 -0
- package/dist/modules/workspace/ripgrep-version.js.map +1 -0
- package/dist/modules/workspace/startup/beside-checkout.d.ts +8 -5
- package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -1
- package/dist/modules/workspace/startup/beside-checkout.js +14 -5
- package/dist/modules/workspace/startup/beside-checkout.js.map +1 -1
- package/dist/modules/workspace/worker-pool.d.ts +3 -4
- package/dist/modules/workspace/worker-pool.d.ts.map +1 -1
- package/dist/modules/workspace/worker-pool.js.map +1 -1
- package/dist/modules/workspace/workspace-tmp.d.ts +146 -0
- package/dist/modules/workspace/workspace-tmp.d.ts.map +1 -0
- package/dist/modules/workspace/workspace-tmp.js +435 -0
- package/dist/modules/workspace/workspace-tmp.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +5 -26
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +47 -6
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +86 -6
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +460 -116
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/path-order.d.ts +28 -8
- package/dist/shared/path-order.d.ts.map +1 -1
- package/dist/shared/path-order.js +37 -8
- package/dist/shared/path-order.js.map +1 -1
- package/migrations/0022_session_version.sql +6 -0
- package/migrations/0023_agent_path.sql +3 -0
- package/migrations/meta/0022_snapshot.json +2360 -0
- package/migrations/meta/0023_snapshot.json +2378 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +3 -3
- package/src/__tests__/pg-scratch.test.ts +69 -0
- package/src/__tests__/pg-scratch.ts +31 -0
- package/src/__tests__/slow-ripgrep.ts +65 -0
- package/src/core/__tests__/boot-without-ripgrep.test.ts +34 -0
- package/src/core/__tests__/catalog-revision.test.ts +89 -0
- package/src/core/__tests__/damaged-document.tools.test.ts +2 -2
- package/src/core/__tests__/gated-boot.test.ts +8 -1
- package/src/core/__tests__/lifecycle.test.ts +46 -13
- package/src/core/catalog-revision.ts +24 -2
- package/src/core/core-ports.ts +42 -0
- package/src/core/create-core-server.ts +13 -0
- package/src/core/create-core-services.ts +49 -12
- package/src/core/lifecycle.ts +26 -2
- package/src/index.ts +1 -1
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +12 -0
- package/src/modules/access/__tests__/access-control.colliding-name-cases.test.ts +113 -0
- package/src/modules/access/__tests__/access-control.frontmatter-head.test.ts +117 -0
- package/src/modules/access/__tests__/access-control.group-named-admin.test.ts +122 -0
- package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +50 -0
- package/src/modules/access/__tests__/access-declarations.test.ts +19 -0
- package/src/modules/access/__tests__/access-groups.test.ts +55 -22
- package/src/modules/access/__tests__/access-mutation.service.test.ts +85 -59
- package/src/modules/access/__tests__/access-restrict-here.test.ts +7 -5
- package/src/modules/access/__tests__/access.routes.admins.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -0
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +50 -14
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +18 -13
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
- package/src/modules/access/__tests__/grant-sources.test.ts +20 -15
- package/src/modules/access/__tests__/groups-admin.routes.test.ts +61 -0
- package/src/modules/access/__tests__/groups-admin.service.test.ts +211 -18
- package/src/modules/access/__tests__/reference-scan.test.ts +366 -14
- package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
- package/src/modules/access/__tests__/roles-admin.service.test.ts +43 -22
- package/src/modules/access/__tests__/roles-capabilities.test.ts +6 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +1 -1
- package/src/modules/access/access-control.interface.ts +17 -10
- package/src/modules/access/access-control.service.ts +158 -72
- package/src/modules/access/access-mutation.service.ts +48 -53
- package/src/modules/access/access-view.ts +12 -10
- package/src/modules/access/access.routes.ts +43 -33
- package/src/modules/access/groups-admin.routes.ts +4 -2
- package/src/modules/access/groups-admin.service.ts +114 -44
- package/src/modules/access/groups-edit.ts +1 -1
- package/src/modules/access/reference-scan.ts +319 -46
- package/src/modules/access/roles-admin.service.ts +43 -26
- package/src/modules/access/synced-groups-writer.ts +1 -1
- package/src/modules/access-model/__tests__/access-grammar.test.ts +38 -1
- package/src/modules/access-model/__tests__/access-splice.test.ts +50 -8
- package/src/modules/access-model/access-grammar.ts +65 -20
- package/src/modules/access-model/access-splice.ts +31 -23
- package/src/modules/access-model/group-files.ts +5 -0
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +31 -0
- package/src/modules/agent-guide/agent-guide.tools.ts +13 -2
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +1 -0
- package/src/modules/agent-instructions/compose.ts +3 -1
- package/src/modules/agent-instructions/shared-file-rules.ts +17 -1
- package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +421 -0
- package/src/modules/agent-scope/__tests__/connection-scope.test.ts +46 -0
- package/src/modules/agent-scope/__tests__/in-memory-kb.ts +148 -0
- package/src/modules/agent-scope/agent-scope.contract.ts +83 -0
- package/src/modules/agent-scope/connection-scope.ts +199 -0
- package/src/modules/agent-scope/index.ts +15 -0
- package/src/modules/audit/agent-audit.service.ts +2 -0
- package/src/modules/audit/audit.contract.ts +6 -0
- package/src/modules/auth/__tests__/auth.middleware.test.ts +30 -2
- package/src/modules/auth/__tests__/auth.routes.test.ts +71 -5
- package/src/modules/auth/__tests__/auth.service.test.ts +90 -9
- package/src/modules/auth/__tests__/password-change-sessions.pg.test.ts +269 -0
- package/src/modules/auth/account-admission.ts +19 -0
- package/src/modules/auth/auth.middleware.ts +45 -4
- package/src/modules/auth/auth.routes.ts +46 -29
- package/src/modules/auth/auth.service.ts +135 -34
- package/src/modules/auth/index.ts +2 -0
- package/src/modules/auth/oidc-auth-provider.ts +2 -9
- package/src/modules/database/__tests__/session-version-migration.pg.test.ts +133 -0
- package/src/modules/database/core-schema.ts +33 -0
- package/src/modules/declared-variables/__tests__/declared-variables.route.test.ts +44 -1
- package/src/modules/declared-variables/declared-variables.routes.ts +12 -1
- package/src/modules/embed/__tests__/embed.tools.test.ts +12 -0
- package/src/modules/embed/embed.tools.ts +5 -0
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +25 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +53 -8
- package/src/modules/kb-fs/index.ts +2 -0
- package/src/modules/kb-fs/locking-filesystem.ts +7 -1
- package/src/modules/kb-fs/repo-path.ts +33 -7
- package/src/modules/kb-fs/tmp-ref.ts +81 -0
- package/src/modules/mcp/__tests__/connect-as.pg.test.ts +137 -0
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +29 -5
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +8 -0
- package/src/modules/mcp/__tests__/mcp.connect-as.test.ts +195 -0
- package/src/modules/mcp/__tests__/mcp.routes.connect-as.test.ts +192 -0
- package/src/modules/mcp/__tests__/oauth-consent.connect-as.test.ts +146 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +19 -2
- package/src/modules/mcp/mcp.routes.ts +39 -3
- package/src/modules/mcp/mcp.service.ts +107 -17
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +46 -8
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +41 -1
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +38 -2
- package/src/modules/plugins/index.ts +1 -1
- package/src/modules/plugins/plugin-links.ts +19 -2
- package/src/modules/plugins/plugins.routes.ts +35 -4
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +113 -0
- package/src/modules/secrets-vault/secrets-vault.routes.ts +23 -3
- package/src/modules/skills/__tests__/skills.service.test.ts +34 -0
- package/src/modules/skills/index.ts +7 -2
- package/src/modules/skills/skills.contract.ts +17 -0
- package/src/modules/skills/skills.service.ts +5 -1
- package/src/modules/skills/skills.tools.ts +44 -13
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +4 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +11 -1
- package/src/modules/tool-auth/external-api-key.service.ts +5 -2
- package/src/modules/tool-auth/index.ts +1 -1
- package/src/modules/tool-auth/internal-token.service.ts +10 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +45 -3
- package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -1
- package/src/modules/tool-helpers/tool-context.ts +10 -0
- package/src/modules/tool-helpers/tool-handler.ts +34 -0
- package/src/modules/tool-helpers/tool.contract.ts +10 -0
- package/src/modules/tool-helpers/validate-token.ts +4 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +80 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +42 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +3 -3
- package/src/modules/tool-manuals/index.ts +6 -2
- package/src/modules/tool-manuals/tool-manuals.contract.ts +18 -6
- package/src/modules/tool-manuals/tool-manuals.routes.ts +40 -3
- package/src/modules/tool-manuals/tool-manuals.service.ts +31 -15
- package/src/modules/tool-manuals/tool-manuals.tools.ts +28 -11
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +2 -2
- package/src/modules/tool-registry/manual.routes.ts +26 -5
- package/src/modules/tool-registry/tool.contract.ts +7 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +1 -1
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +54 -3
- package/src/modules/workflow/agent-tools/workflow.tools.ts +14 -9
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +130 -3
- package/src/modules/workflow/git/git.service.ts +29 -48
- package/src/modules/workflow/workflow.service.ts +5 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -2
- package/src/modules/workspace/__tests__/holds-nul.test.ts +66 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
- package/src/modules/workspace/__tests__/ripgrep-search.test.ts +570 -0
- package/src/modules/workspace/__tests__/ripgrep-version.test.ts +29 -0
- package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +928 -0
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +17 -37
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +20 -8
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +22 -10
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +393 -113
- package/src/modules/workspace/file-readers/__tests__/doc-extract.service.test.ts +10 -0
- package/src/modules/workspace/file-readers/doc-extract.service.ts +71 -38
- package/src/modules/workspace/index.ts +20 -0
- package/src/modules/workspace/nul-scan.ts +50 -0
- package/src/modules/workspace/pattern-matcher.contract.ts +123 -32
- package/src/modules/workspace/ripgrep-search.ts +715 -0
- package/src/modules/workspace/ripgrep-version.ts +35 -0
- package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +11 -0
- package/src/modules/workspace/startup/beside-checkout.ts +13 -5
- package/src/modules/workspace/worker-pool.ts +3 -4
- package/src/modules/workspace/workspace-tmp.ts +457 -0
- package/src/modules/workspace/workspace.routes.ts +6 -27
- package/src/modules/workspace/workspace.service.ts +92 -15
- package/src/modules/workspace/workspace.tools.ts +500 -155
- package/src/shared/path-order.ts +37 -8
- package/starter-packs/engineering/Plugins/engineering-starter/access.md +1 -1
- package/starter-packs/general/Plugins/general-starter/access.md +1 -1
- package/starter-packs/operations/Plugins/operations-starter/access.md +1 -1
- package/starter-packs/sales/Plugins/sales-starter/access.md +1 -1
- package/starter-packs/support/Plugins/support-starter/access.md +1 -1
- package/dist/modules/workspace/bounded-matcher.d.ts +0 -73
- package/dist/modules/workspace/bounded-matcher.d.ts.map +0 -1
- package/dist/modules/workspace/bounded-matcher.js +0 -200
- package/dist/modules/workspace/bounded-matcher.js.map +0 -1
- package/src/modules/workspace/__tests__/bounded-matcher.test.ts +0 -213
- package/src/modules/workspace/bounded-matcher.ts +0 -271
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { PathNotFoundError, WorkflowDomainError, WorkflowValidationError } from '../../shared/domain-errors.js';
|
|
4
|
+
import { assertWithinDirectory } from '../../shared/path-containment.js';
|
|
5
|
+
import { sanitizedPath } from '../../shared/printable.js';
|
|
6
|
+
import { hasControlCharacter, TMP_REF_PREFIX } from '../kb-fs/index.js';
|
|
7
|
+
import { isAbsence } from '../../shared/fs.contract.js';
|
|
8
|
+
import { logger } from '../../shared/logging.js';
|
|
9
|
+
import { lstatOrNull } from '../../shared/rename-no-replace.js';
|
|
10
|
+
const log = logger('workspace-tmp');
|
|
11
|
+
/**
|
|
12
|
+
* A workspace's `tmp/` folder, reached through a reserved ref.
|
|
13
|
+
*
|
|
14
|
+
* Every workspace directory holds its checkout and, beside it, a `tmp/`
|
|
15
|
+
* folder: the chat stages attachments there, the SharePoint reader stages
|
|
16
|
+
* documents there, and nothing in it is ever committed. The file tools place
|
|
17
|
+
* every path under the checkout (see `kb-fs/repo-path.ts`), so the folder is
|
|
18
|
+
* named by a REF instead — `__tmp__/<path>` — which `read_file`, `file_stat`,
|
|
19
|
+
* `grep` and `copy_file` (as its source) accept beside a repository path, and
|
|
20
|
+
* every other tool refuses (`kb-fs/tmp-ref.ts`, the one admission gate every
|
|
21
|
+
* tool's path inputs pass). A plain `tmp/…` path is not that ref and never
|
|
22
|
+
* will be: `tmp` is a legal folder name inside a repository, and the
|
|
23
|
+
* normaliser's rule is what keeps every other path in it.
|
|
24
|
+
*
|
|
25
|
+
* The ref is scoped twice. To the WORKSPACE: it is resolved against the
|
|
26
|
+
* workspace of the branch the call names, exactly like a repository path. And
|
|
27
|
+
* to the CALLER: what a person stages lands under their own folder,
|
|
28
|
+
* `tmp/<owner id>/` ({@link stageTmpRef}, the one way in), and a ref resolves
|
|
29
|
+
* inside the caller's own folder and nowhere else ({@link resolveTmpRef}). A
|
|
30
|
+
* file somebody else staged is therefore not there — the plain not-found,
|
|
31
|
+
* the same answer as for a file that never existed — rather than something an
|
|
32
|
+
* access rule has to refuse: the folder is outside the repository, where no
|
|
33
|
+
* `access.md` governs anything, and the only person a staged attachment is
|
|
34
|
+
* for is the one who attached it. The ref names ONE file — no folder, no
|
|
35
|
+
* listing — and nothing outside the caller's folder is reachable through it,
|
|
36
|
+
* links included. Every resolution goes through {@link resolveTmpRef}; there
|
|
37
|
+
* is no second reader of the folder.
|
|
38
|
+
*
|
|
39
|
+
* Entries live for {@link TMP_MAX_AGE_MS} after their last modification, then
|
|
40
|
+
* the platform's periodic sweep removes them ({@link sweepTmpFolder},
|
|
41
|
+
* scheduled by {@link TmpSweeper}) — or sooner, when a distribution clears a
|
|
42
|
+
* workspace's whole `tmp/` after a run of its own (`clearTmp`): "up to seven
|
|
43
|
+
* days" is the promise, not "seven days".
|
|
44
|
+
*/
|
|
45
|
+
/** The folder the ref reaches, beside the checkout in every workspace directory. */
|
|
46
|
+
export const WORKSPACE_TMP_DIR = 'tmp';
|
|
47
|
+
/** How long an entry of a `tmp/` folder lives after its last modification: seven days. */
|
|
48
|
+
export const TMP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
49
|
+
/** How often the platform sweeps every workspace's `tmp/` folder: hourly. */
|
|
50
|
+
export const TMP_SWEEP_INTERVAL_MS = 60 * 60 * 1000;
|
|
51
|
+
/** A ref that breaks one of the rules, with the rule named. */
|
|
52
|
+
function invalidRef(ref, rule) {
|
|
53
|
+
const shown = sanitizedPath(ref);
|
|
54
|
+
return new WorkflowValidationError(`"${shown}" is not a valid tmp ref: ${rule}`, { kind: 'tmp-ref-invalid', path: shown });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The folder of one owner inside a workspace's `tmp/`: `tmp/<owner id>/`.
|
|
58
|
+
* The id is an account id, which the platform mints and which is one path
|
|
59
|
+
* segment by construction; one that is not — empty, a separator in it, a
|
|
60
|
+
* `.`/`..`, a control character — is a programming error, never a file to
|
|
61
|
+
* look for, and is refused as one.
|
|
62
|
+
*/
|
|
63
|
+
function ownerFolder(workspaceDir, ownerId) {
|
|
64
|
+
if (ownerId === '' ||
|
|
65
|
+
ownerId === '.' ||
|
|
66
|
+
ownerId === '..' ||
|
|
67
|
+
ownerId.includes('/') ||
|
|
68
|
+
ownerId.includes('\\') ||
|
|
69
|
+
hasControlCharacter(ownerId) ||
|
|
70
|
+
ownerId.length > 255) {
|
|
71
|
+
throw new Error(`Invalid tmp owner id "${sanitizedPath(ownerId)}": an owner's tmp folder is one path segment.`);
|
|
72
|
+
}
|
|
73
|
+
return path.join(workspaceDir, WORKSPACE_TMP_DIR, ownerId);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* What a failed look at the folder answers, naming the REF and never the path
|
|
77
|
+
* on disk — the raw error (which carries that path) is the server log's, not
|
|
78
|
+
* the caller's: nothing there is the ordinary not-found; a loop of links is
|
|
79
|
+
* refused by the link rule; anything else (a permission, an I/O error) is a
|
|
80
|
+
* 500 that says the ref could not be read.
|
|
81
|
+
*/
|
|
82
|
+
function tmpRefFailure(ref, err) {
|
|
83
|
+
if (isAbsence(err))
|
|
84
|
+
return new PathNotFoundError(ref);
|
|
85
|
+
const shown = sanitizedPath(ref);
|
|
86
|
+
const code = err?.code;
|
|
87
|
+
if (code === 'ELOOP') {
|
|
88
|
+
return invalidRef(ref, 'it goes through a link that cannot be followed (a link that leads back to itself).');
|
|
89
|
+
}
|
|
90
|
+
log.warn('could not read a tmp ref:', { err, ref: shown });
|
|
91
|
+
return new WorkflowDomainError(`"${shown}" could not be read from the workspace's tmp folder.`, 500, {
|
|
92
|
+
kind: 'tmp-ref-unreadable',
|
|
93
|
+
path: shown,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The ref's path inside the `tmp/` folder, as segments — or the refusal that
|
|
98
|
+
* names the rule it breaks. Lexical only: the folder and link rules need the
|
|
99
|
+
* disk and are {@link resolveTmpRef}'s.
|
|
100
|
+
*/
|
|
101
|
+
function tmpRefSegments(ref) {
|
|
102
|
+
const rest = ref.startsWith(TMP_REF_PREFIX) ? ref.slice(TMP_REF_PREFIX.length) : '';
|
|
103
|
+
// First, because the refusal below would otherwise quote the character
|
|
104
|
+
// that forges a second line — and a line break is a separator to git and
|
|
105
|
+
// to every line-oriented log this ref may reach.
|
|
106
|
+
if (hasControlCharacter(ref)) {
|
|
107
|
+
throw invalidRef(ref, 'it contains a control character (a line break, a NUL or another invisible byte), which is part of no file name.');
|
|
108
|
+
}
|
|
109
|
+
if (rest === '') {
|
|
110
|
+
throw invalidRef(ref, `it names the tmp folder itself. A tmp ref names one file: \`${TMP_REF_PREFIX}<path to the file>\`.`);
|
|
111
|
+
}
|
|
112
|
+
if (rest.startsWith('/') || /^[A-Za-z]:/.test(rest)) {
|
|
113
|
+
throw invalidRef(ref, 'the path after the prefix is absolute. It is a path inside the workspace\'s tmp folder, written without a leading slash.');
|
|
114
|
+
}
|
|
115
|
+
if (rest.includes('\\')) {
|
|
116
|
+
throw invalidRef(ref, 'it contains a backslash. Separate folders with `/`.');
|
|
117
|
+
}
|
|
118
|
+
if (rest.endsWith('/')) {
|
|
119
|
+
throw invalidRef(ref, 'it ends with `/`, which names a folder. A tmp ref names one file; the tmp folder is not listed.');
|
|
120
|
+
}
|
|
121
|
+
const segments = rest.split('/');
|
|
122
|
+
if (segments.some((segment) => segment === '' || segment === '.' || segment === '..')) {
|
|
123
|
+
throw invalidRef(ref, 'it contains a `.` or `..` segment (or an empty one). A tmp ref names one file inside the workspace\'s tmp folder and cannot climb out of it.');
|
|
124
|
+
}
|
|
125
|
+
return segments;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* `folder` is absent, or a real directory — never a link, never a file. The
|
|
129
|
+
* `tmp/` folder and an owner's folder are judged by this before anything
|
|
130
|
+
* resolves or lands under them: a link standing in either place would carry
|
|
131
|
+
* the whole folder somewhere else, and a real-path containment check judged
|
|
132
|
+
* against the link's target would not notice.
|
|
133
|
+
*/
|
|
134
|
+
async function assertOwnFolder(folder, ref, failed) {
|
|
135
|
+
const stat = await lstatOrNull(folder).catch(failed);
|
|
136
|
+
if (stat !== null && !stat.isDirectory()) {
|
|
137
|
+
throw invalidRef(ref, "its tmp folder is not a folder of this workspace's own (a link or a file stands where it should be).");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* THE resolution of a ref, against one owner's folder in one workspace
|
|
142
|
+
* directory: the file's absolute path and its stat, or a refusal. Every read
|
|
143
|
+
* of the folder starts here.
|
|
144
|
+
*
|
|
145
|
+
* - The lexical rules first ({@link tmpRefSegments}), before the disk is
|
|
146
|
+
* asked anything.
|
|
147
|
+
* - Nothing there — the owner's folder included, which a person who has
|
|
148
|
+
* staged nothing does not have — is a {@link PathNotFoundError} naming the
|
|
149
|
+
* REF, never the path on disk: the answer every file tool turns into its
|
|
150
|
+
* ordinary 404. What another person staged is "nothing there" too. Every
|
|
151
|
+
* other failure names the ref alone as well ({@link tmpRefFailure}).
|
|
152
|
+
* - The file's REAL path must lie inside the owner's folder's real path, so a
|
|
153
|
+
* link — anywhere along the ref — that leads out of it (into another
|
|
154
|
+
* owner's folder included) is refused rather than followed. A link that
|
|
155
|
+
* stays inside is the file it points at.
|
|
156
|
+
* - A folder is refused by the rule, never listed.
|
|
157
|
+
*/
|
|
158
|
+
export async function resolveTmpRef(workspaceDir, ownerId, ref) {
|
|
159
|
+
const segments = tmpRefSegments(ref);
|
|
160
|
+
const ownerDir = ownerFolder(workspaceDir, ownerId);
|
|
161
|
+
const absent = (err) => {
|
|
162
|
+
throw tmpRefFailure(ref, err);
|
|
163
|
+
};
|
|
164
|
+
// `tmp/` and the owner's folder must BE folders of this workspace, not
|
|
165
|
+
// links to ones somewhere else: the containment below is judged against
|
|
166
|
+
// the owner's folder's real path, which a link would move along with it.
|
|
167
|
+
await assertOwnFolder(path.join(workspaceDir, WORKSPACE_TMP_DIR), ref, absent);
|
|
168
|
+
await assertOwnFolder(ownerDir, ref, absent);
|
|
169
|
+
if ((await lstatOrNull(ownerDir).catch(absent)) === null)
|
|
170
|
+
throw new PathNotFoundError(ref);
|
|
171
|
+
const realOwnerDir = await fs.realpath(ownerDir).catch(absent);
|
|
172
|
+
const real = await fs.realpath(path.join(ownerDir, ...segments)).catch(absent);
|
|
173
|
+
// THE containment check (shared/path-containment.ts), on the real paths;
|
|
174
|
+
// its refusal is answered as the link rule, naming the ref. The folder
|
|
175
|
+
// itself passes it, and is refused below as a folder.
|
|
176
|
+
try {
|
|
177
|
+
assertWithinDirectory(real, realOwnerDir);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
throw invalidRef(ref, 'it goes through a link that leads out of your tmp folder, and links out of it are never followed.');
|
|
181
|
+
}
|
|
182
|
+
const stat = await fs.stat(real).catch(absent);
|
|
183
|
+
if (stat.isDirectory()) {
|
|
184
|
+
throw invalidRef(ref, 'it names a folder. A tmp ref names one file; the tmp folder is not listed.');
|
|
185
|
+
}
|
|
186
|
+
if (!stat.isFile()) {
|
|
187
|
+
throw invalidRef(ref, 'it names something that is not a file.');
|
|
188
|
+
}
|
|
189
|
+
return { absolutePath: real, stat };
|
|
190
|
+
}
|
|
191
|
+
/** The bytes of the one file a ref names in `ownerId`'s folder of `workspaceDir`'s tmp (see {@link resolveTmpRef}). */
|
|
192
|
+
export async function readTmpRef(workspaceDir, ownerId, ref) {
|
|
193
|
+
const { absolutePath } = await resolveTmpRef(workspaceDir, ownerId, ref);
|
|
194
|
+
return fs.readFile(absolutePath).catch((err) => {
|
|
195
|
+
// Swept or replaced between the resolve and the read: the same answer a
|
|
196
|
+
// moment later would give, still naming the ref.
|
|
197
|
+
throw tmpRefFailure(ref, err);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/** The stat of the one file a ref names (see {@link resolveTmpRef}). */
|
|
201
|
+
export async function statTmpRef(workspaceDir, ownerId, ref) {
|
|
202
|
+
const { stat } = await resolveTmpRef(workspaceDir, ownerId, ref);
|
|
203
|
+
return { name: ref.slice(ref.lastIndexOf('/') + 1), size: stat.size, createdAt: stat.birthtime, modifiedAt: stat.mtime };
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Stage `bytes` at `relativePath` in `ownerId`'s folder of `workspaceDir`'s
|
|
207
|
+
* tmp, and answer the ref the owner reads it back with: the ONE way a file
|
|
208
|
+
* gets into the folder the ref reaches, so the convention — one folder per
|
|
209
|
+
* owner, the file's path inside it — is this function's and no stager's.
|
|
210
|
+
* `relativePath` obeys a ref's own rules (one file, no `..`, no absolute
|
|
211
|
+
* path, no backslash, no control character); the refusal names the rule.
|
|
212
|
+
* Replaces the FILE at the path: a staged file is the stager's own scratch,
|
|
213
|
+
* and the chat names uploads by their content. Never writes through a link:
|
|
214
|
+
* not one standing where `tmp/` or the owner's folder should be, not one on
|
|
215
|
+
* the way, and not one at the file's own name (judged by `lstat`, which is
|
|
216
|
+
* what every platform has; the check and the write are two steps, as they
|
|
217
|
+
* are everywhere else in this file).
|
|
218
|
+
*/
|
|
219
|
+
export async function stageTmpRef(workspaceDir, ownerId, relativePath, bytes) {
|
|
220
|
+
const ref = TMP_REF_PREFIX + relativePath;
|
|
221
|
+
const segments = tmpRefSegments(ref);
|
|
222
|
+
const ownerDir = ownerFolder(workspaceDir, ownerId);
|
|
223
|
+
const rethrow = (err) => {
|
|
224
|
+
throw err;
|
|
225
|
+
};
|
|
226
|
+
await assertOwnFolder(path.join(workspaceDir, WORKSPACE_TMP_DIR), ref, rethrow);
|
|
227
|
+
await assertOwnFolder(ownerDir, ref, rethrow);
|
|
228
|
+
// The workspace directory itself may not exist yet: a staged file belongs
|
|
229
|
+
// beside the checkout whether or not the branch is cloned, and the
|
|
230
|
+
// directory's path is the root's own (`workspaceDirWithinRoot`), nothing
|
|
231
|
+
// the ref names — so it is simply made.
|
|
232
|
+
await fs.mkdir(workspaceDir, { recursive: true });
|
|
233
|
+
// The folders on the way — `tmp/` and the owner's folder included, when a
|
|
234
|
+
// first staging has yet to make them — made one at a time and each judged
|
|
235
|
+
// before the next is entered: never through a link, which a recursive
|
|
236
|
+
// mkdir would follow, creating folders wherever the link leads before
|
|
237
|
+
// anything could refuse it. The one rule for the whole path, the folders
|
|
238
|
+
// already there included.
|
|
239
|
+
await makeFoldersWithin(workspaceDir, [WORKSPACE_TMP_DIR, ownerId, ...segments.slice(0, -1)], ref);
|
|
240
|
+
const target = path.join(ownerDir, ...segments);
|
|
241
|
+
// The name itself: a file to replace, nothing yet, or something that is
|
|
242
|
+
// neither — a link (which a write would follow) or a folder — refused.
|
|
243
|
+
const standing = await lstatOrNull(target);
|
|
244
|
+
if (standing !== null && !standing.isFile()) {
|
|
245
|
+
throw invalidRef(ref, 'it names something that is not a file.');
|
|
246
|
+
}
|
|
247
|
+
await fs.writeFile(target, bytes);
|
|
248
|
+
return ref;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* The folders of `segments` under `base`, each one entered only once it is
|
|
252
|
+
* known to be a real directory of the owner's folder: a missing one is made
|
|
253
|
+
* (and judged again, since another stage may have made it meanwhile), and a
|
|
254
|
+
* link or a file standing in its place is refused as the link rule — before
|
|
255
|
+
* any folder is made beyond it, so a link on the way creates nothing where it
|
|
256
|
+
* leads. `lstat`, as everywhere in this file, so a link is seen as a link.
|
|
257
|
+
*/
|
|
258
|
+
async function makeFoldersWithin(base, segments, ref) {
|
|
259
|
+
let at = base;
|
|
260
|
+
for (const segment of segments) {
|
|
261
|
+
at = path.join(at, segment);
|
|
262
|
+
let standing = await lstatOrNull(at);
|
|
263
|
+
if (standing === null) {
|
|
264
|
+
await fs.mkdir(at).catch((err) => {
|
|
265
|
+
if (err.code !== 'EEXIST')
|
|
266
|
+
throw err;
|
|
267
|
+
});
|
|
268
|
+
standing = await lstatOrNull(at);
|
|
269
|
+
}
|
|
270
|
+
if (standing === null || !standing.isDirectory()) {
|
|
271
|
+
throw invalidRef(ref, 'it goes through a link that leads out of your tmp folder, and links out of it are never followed.');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Remove every file of one `tmp/` folder, at any depth, whose last
|
|
277
|
+
* modification is older than `maxAgeMs`, and every folder in it that the
|
|
278
|
+
* sweep leaves empty and that was itself older than that before the sweep
|
|
279
|
+
* touched it. Judged per FILE, because the stagers keep a long-lived folder
|
|
280
|
+
* each (`chat-uploads/`, …) whose own mtime moves with every new upload: a
|
|
281
|
+
* folder judged as a whole would keep an old file for as long as anyone kept
|
|
282
|
+
* uploading beside it. Everything is judged by its own `lstat`, and a link is
|
|
283
|
+
* removed as the link, never followed. The `tmp/` folder itself stays. A
|
|
284
|
+
* folder that is not there is nothing to sweep. Returns the paths removed,
|
|
285
|
+
* relative to `tmp/`.
|
|
286
|
+
*
|
|
287
|
+
* The tree is read by THE walk (`walker`, see `shared/fs.contract.ts`), not a
|
|
288
|
+
* loop of its own: its files, its other entries (a link, a socket — never
|
|
289
|
+
* entered) and its folders are collected, each folder's age read as it is
|
|
290
|
+
* listed, so before anything in it goes. A folder that cannot be listed is
|
|
291
|
+
* the walk's hole: logged, and walked around. The removals follow the walk —
|
|
292
|
+
* the entries first, then the folders deepest first, so a folder emptied by
|
|
293
|
+
* the sweep is judged after its contents.
|
|
294
|
+
*
|
|
295
|
+
* `signal` ends the sweep early: once it is aborted the walk stops listing
|
|
296
|
+
* and nothing more is removed — it is asked again right before each removal,
|
|
297
|
+
* after every awaited look — and what is already gone is returned.
|
|
298
|
+
*/
|
|
299
|
+
export async function sweepTmpFolder(walker, workspaceDir, maxAgeMs, now, signal) {
|
|
300
|
+
const tmpDir = path.join(workspaceDir, WORKSPACE_TMP_DIR);
|
|
301
|
+
// `lstat` the folder itself: a `tmp` that is a link is not a folder of
|
|
302
|
+
// this workspace's to sweep (the walk would follow it). Not there is
|
|
303
|
+
// nothing to sweep.
|
|
304
|
+
if ((await lstatOrNull(tmpDir))?.isDirectory() !== true)
|
|
305
|
+
return [];
|
|
306
|
+
const join = (dir, name) => (dir === '' ? name : `${dir}/${name}`);
|
|
307
|
+
const entries = [];
|
|
308
|
+
const oldDirs = [];
|
|
309
|
+
const stopped = () => signal?.aborted === true;
|
|
310
|
+
// The entry's own `lstat` (shared/rename-no-replace.ts), or null when it is
|
|
311
|
+
// gone; any other failure spoils that one entry: logged, and left alone.
|
|
312
|
+
const entryStat = async (rel) => {
|
|
313
|
+
try {
|
|
314
|
+
return await lstatOrNull(path.join(tmpDir, rel));
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
log.warn('could not stat a tmp entry:', { err, entry: sanitizedPath(rel) });
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
await walker.walk(tmpDir, { until: stopped }, [
|
|
322
|
+
{
|
|
323
|
+
async onDir(rel) {
|
|
324
|
+
if (rel === '')
|
|
325
|
+
return;
|
|
326
|
+
// Its age is read BEFORE its contents go: removing them moves it.
|
|
327
|
+
const stat = await entryStat(rel);
|
|
328
|
+
if (stat && now - stat.mtimeMs > maxAgeMs)
|
|
329
|
+
oldDirs.push(rel);
|
|
330
|
+
},
|
|
331
|
+
onFile(dir, name) {
|
|
332
|
+
entries.push(join(dir, name));
|
|
333
|
+
},
|
|
334
|
+
onOther(dir, entry) {
|
|
335
|
+
entries.push(join(dir, entry.name));
|
|
336
|
+
},
|
|
337
|
+
onHole(rel, err) {
|
|
338
|
+
log.warn('could not list a tmp folder:', { err, folder: sanitizedPath(rel) });
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
]);
|
|
342
|
+
const removed = [];
|
|
343
|
+
const attempt = async (rel, remove) => {
|
|
344
|
+
try {
|
|
345
|
+
await remove(path.join(tmpDir, rel));
|
|
346
|
+
removed.push(rel);
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
// Gone mid-sweep is what the sweep wanted, and a folder something is
|
|
350
|
+
// still in stays; anything else spoils one entry, not the sweep.
|
|
351
|
+
const code = err?.code;
|
|
352
|
+
if (!isAbsence(err) && code !== 'ENOTEMPTY' && code !== 'EEXIST') {
|
|
353
|
+
log.warn('could not sweep a tmp entry:', { err, entry: sanitizedPath(rel) });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
for (const rel of entries) {
|
|
358
|
+
if (stopped())
|
|
359
|
+
return removed;
|
|
360
|
+
const stat = await entryStat(rel);
|
|
361
|
+
if (!stat || now - stat.mtimeMs <= maxAgeMs)
|
|
362
|
+
continue;
|
|
363
|
+
// Asked again AFTER the awaited look: a stop that came while it ran
|
|
364
|
+
// removes nothing more, this entry included.
|
|
365
|
+
if (stopped())
|
|
366
|
+
return removed;
|
|
367
|
+
// A file, or a link removed as the link.
|
|
368
|
+
await attempt(rel, (abs) => fs.unlink(abs));
|
|
369
|
+
}
|
|
370
|
+
// Walk order lists a folder before what is in it, so the reverse is deepest first.
|
|
371
|
+
for (const rel of oldDirs.reverse()) {
|
|
372
|
+
if (stopped())
|
|
373
|
+
return removed;
|
|
374
|
+
await attempt(rel, (abs) => fs.rmdir(abs));
|
|
375
|
+
}
|
|
376
|
+
return removed;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* The periodic sweep of every workspace's `tmp/` folder, on a timer.
|
|
380
|
+
*
|
|
381
|
+
* Started with the graph (`startCore`) and stopped with it (`stopCore`):
|
|
382
|
+
* stopping clears the timer and CANCELS the sweep in flight — its signal is
|
|
383
|
+
* aborted, so it removes nothing more and ends at the next entry it would
|
|
384
|
+
* have looked at — and `drainSweep` waits for it to end. `stopCore` bounds
|
|
385
|
+
* that wait by its own deadline, like every other drain there: a sweep stuck
|
|
386
|
+
* inside one filesystem call (a hung mount) can outlive the wait, but it
|
|
387
|
+
* removes nothing once that call returns. The first sweep runs at start, not
|
|
388
|
+
* one interval later, so what a previous process left past its age goes
|
|
389
|
+
* without waiting. The timer is `unref`'d: nothing here is worth keeping a
|
|
390
|
+
* process alive for.
|
|
391
|
+
*/
|
|
392
|
+
export class TmpSweeper {
|
|
393
|
+
sweep;
|
|
394
|
+
timer = null;
|
|
395
|
+
sweeping = null;
|
|
396
|
+
stop = null;
|
|
397
|
+
constructor(sweep) {
|
|
398
|
+
this.sweep = sweep;
|
|
399
|
+
}
|
|
400
|
+
/** Sweep now, and every `intervalMs` after. Idempotent. */
|
|
401
|
+
startSweeping(intervalMs = TMP_SWEEP_INTERVAL_MS) {
|
|
402
|
+
if (this.timer)
|
|
403
|
+
return;
|
|
404
|
+
this.stop = new AbortController();
|
|
405
|
+
this.tick();
|
|
406
|
+
this.timer = setInterval(() => this.tick(), intervalMs);
|
|
407
|
+
this.timer.unref?.();
|
|
408
|
+
}
|
|
409
|
+
/** Clear the timer and cancel the sweep in flight; {@link drainSweep} waits for it to end. */
|
|
410
|
+
stopSweeping() {
|
|
411
|
+
if (this.timer)
|
|
412
|
+
clearInterval(this.timer);
|
|
413
|
+
this.timer = null;
|
|
414
|
+
this.stop?.abort();
|
|
415
|
+
this.stop = null;
|
|
416
|
+
}
|
|
417
|
+
/** Wait for the sweep in flight, if any, to end. */
|
|
418
|
+
async drainSweep() {
|
|
419
|
+
await this.sweeping;
|
|
420
|
+
}
|
|
421
|
+
/** One sweep, unless one is still running; its failure is logged. */
|
|
422
|
+
tick() {
|
|
423
|
+
if (this.sweeping || !this.stop)
|
|
424
|
+
return;
|
|
425
|
+
const running = this.sweep(this.stop.signal)
|
|
426
|
+
.then(() => undefined)
|
|
427
|
+
.catch((err) => log.warn('could not sweep the workspaces\' tmp folders:', { err }))
|
|
428
|
+
.finally(() => {
|
|
429
|
+
if (this.sweeping === running)
|
|
430
|
+
this.sweeping = null;
|
|
431
|
+
});
|
|
432
|
+
this.sweeping = running;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
//# sourceMappingURL=workspace-tmp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-tmp.js","sourceRoot":"","sources":["../../../src/modules/workspace/workspace-tmp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAc,MAAM,SAAS,CAAC;AACrD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAChH,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,SAAS,EAAoB,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAEhE,MAAM,GAAG,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,oFAAoF;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEvC,0FAA0F;AAC1F,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtD,6EAA6E;AAC7E,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpD,+DAA+D;AAC/D,SAAS,UAAU,CAAC,GAAW,EAAE,IAAY;IAC3C,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,OAAO,IAAI,uBAAuB,CAAC,IAAI,KAAK,6BAA6B,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC7H,CAAC;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,YAAoB,EAAE,OAAe;IACxD,IACE,OAAO,KAAK,EAAE;QACd,OAAO,KAAK,GAAG;QACf,OAAO,KAAK,IAAI;QAChB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QACtB,mBAAmB,CAAC,OAAO,CAAC;QAC5B,OAAO,CAAC,MAAM,GAAG,GAAG,EACpB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,yBAAyB,aAAa,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC;IAClH,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,GAAW,EAAE,GAAY;IAC9C,IAAI,SAAS,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,IAAI,GAAI,GAAiC,EAAE,IAAI,CAAC;IACtD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,UAAU,CAAC,GAAG,EAAE,oFAAoF,CAAC,CAAC;IAC/G,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,OAAO,IAAI,mBAAmB,CAAC,IAAI,KAAK,sDAAsD,EAAE,GAAG,EAAE;QACnG,IAAI,EAAE,oBAAoB;QAC1B,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,uEAAuE;IACvE,yEAAyE;IACzE,iDAAiD;IACjD,IAAI,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,UAAU,CAAC,GAAG,EAAE,iHAAiH,CAAC,CAAC;IAC3I,CAAC;IACD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,UAAU,CAAC,GAAG,EAAE,+DAA+D,cAAc,uBAAuB,CAAC,CAAC;IAC9H,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,UAAU,CAAC,GAAG,EAAE,0HAA0H,CAAC,CAAC;IACpJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,UAAU,CAAC,GAAG,EAAE,qDAAqD,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,UAAU,CAAC,GAAG,EAAE,iGAAiG,CAAC,CAAC;IAC3H,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;QACtF,MAAM,UAAU,CAAC,GAAG,EAAE,8IAA8I,CAAC,CAAC;IACxK,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,GAAW,EAAE,MAA+B;IACzF,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACzC,MAAM,UAAU,CAAC,GAAG,EAAE,sGAAsG,CAAC,CAAC;IAChI,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,YAAoB,EACpB,OAAe,EACf,GAAW;IAEX,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,CAAC,GAAY,EAAS,EAAE;QACrC,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC,CAAC;IACF,uEAAuE;IACvE,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,iBAAiB,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC/E,MAAM,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI;QAAE,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAC3F,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/E,yEAAyE;IACzE,uEAAuE;IACvE,sDAAsD;IACtD,IAAI,CAAC;QACH,qBAAqB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,UAAU,CAAC,GAAG,EAAE,mGAAmG,CAAC,CAAC;IAC7H,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACvB,MAAM,UAAU,CAAC,GAAG,EAAE,4EAA4E,CAAC,CAAC;IACtG,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACnB,MAAM,UAAU,CAAC,GAAG,EAAE,wCAAwC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACtC,CAAC;AAED,uHAAuH;AACvH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,YAAoB,EAAE,OAAe,EAAE,GAAW;IACjF,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACzE,OAAO,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACtD,wEAAwE;QACxE,iDAAiD;QACjD,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAWD,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,YAAoB,EAAE,OAAe,EAAE,GAAW;IACjF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACjE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;AAC3H,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,YAAoB,EAAE,OAAe,EAAE,YAAoB,EAAE,KAAiB;IAC9G,MAAM,GAAG,GAAG,cAAc,GAAG,YAAY,CAAC;IAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,CAAC,GAAY,EAAS,EAAE;QACtC,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC;IACF,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,iBAAiB,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAChF,MAAM,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9C,0EAA0E;IAC1E,mEAAmE;IACnE,yEAAyE;IACzE,wCAAwC;IACxC,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,0EAA0E;IAC1E,0EAA0E;IAC1E,sEAAsE;IACtE,sEAAsE;IACtE,yEAAyE;IACzE,0BAA0B;IAC1B,MAAM,iBAAiB,CAAC,YAAY,EAAE,CAAC,iBAAiB,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnG,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC;IAChD,wEAAwE;IACxE,uEAAuE;IACvE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5C,MAAM,UAAU,CAAC,GAAG,EAAE,wCAAwC,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClC,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,QAA2B,EAAE,GAAW;IACrF,IAAI,EAAE,GAAG,IAAI,CAAC;IACd,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5B,IAAI,QAAQ,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACxC,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,GAAG,CAAC;YAClE,CAAC,CAAC,CAAC;YACH,QAAQ,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;YACjD,MAAM,UAAU,CAAC,GAAG,EAAE,mGAAmG,CAAC,CAAC;QAC7H,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAmB,EACnB,YAAoB,EACpB,QAAgB,EAChB,GAAW,EACX,MAAoB;IAEpB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;IAC1D,uEAAuE;IACvE,qEAAqE;IACrE,oBAAoB;IACpB,IAAI,CAAC,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACnE,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,IAAY,EAAU,EAAE,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3F,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,GAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,yEAAyE;IACzE,MAAM,SAAS,GAAG,KAAK,EAAE,GAAW,EAAyB,EAAE;QAC7D,IAAI,CAAC;YACH,OAAO,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC5E,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IACF,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5C;YACE,KAAK,CAAC,KAAK,CAAC,GAAG;gBACb,IAAI,GAAG,KAAK,EAAE;oBAAE,OAAO;gBACvB,kEAAkE;gBAClE,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;gBAClC,IAAI,IAAI,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,QAAQ;oBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/D,CAAC;YACD,MAAM,CAAC,GAAG,EAAE,IAAI;gBACd,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YAChC,CAAC;YACD,OAAO,CAAC,GAAG,EAAE,KAAK;gBAChB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACtC,CAAC;YACD,MAAM,CAAC,GAAG,EAAE,GAAG;gBACb,GAAG,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChF,CAAC;SACF;KACF,CAAC,CAAC;IACH,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK,EAAE,GAAW,EAAE,MAAsC,EAAiB,EAAE;QAC3F,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,iEAAiE;YACjE,MAAM,IAAI,GAAI,GAAiC,EAAE,IAAI,CAAC;YACtD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACjE,GAAG,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,OAAO,EAAE;YAAE,OAAO,OAAO,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ;YAAE,SAAS;QACtD,oEAAoE;QACpE,6CAA6C;QAC7C,IAAI,OAAO,EAAE;YAAE,OAAO,OAAO,CAAC;QAC9B,yCAAyC;QACzC,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,mFAAmF;IACnF,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACpC,IAAI,OAAO,EAAE;YAAE,OAAO,OAAO,CAAC;QAC9B,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,UAAU;IAKQ;IAJrB,KAAK,GAA0C,IAAI,CAAC;IACpD,QAAQ,GAAyB,IAAI,CAAC;IACtC,IAAI,GAA2B,IAAI,CAAC;IAE5C,YAA6B,KAAgD;QAAhD,UAAK,GAAL,KAAK,CAA2C;IAAG,CAAC;IAEjF,2DAA2D;IAC3D,aAAa,CAAC,aAAqB,qBAAqB;QACtD,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IACvB,CAAC;IAED,8FAA8F;IAC9F,YAAY;QACV,IAAI,IAAI,CAAC,KAAK;YAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,oDAAoD;IACpD,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED,qEAAqE;IAC7D,IAAI;QACV,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;aACzC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aACrB,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,+CAA+C,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;aAC3F,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO;gBAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACtD,CAAC,CAAC,CAAC;QACL,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/workspace.routes.ts"],"names":[],"mappings":"AAKA,OAAO,
|
|
1
|
+
{"version":3,"file":"workspace.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/workspace.routes.ts"],"names":[],"mappings":"AAKA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAelF,OAAO,EAA8D,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE3H,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAQ7D,OAAO,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAwB3D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,gBAAgB,EAAE,gBAAgB,EAClC,WAAW,EAAE,WAAW,EACxB,eAAe,EAAE,gBAAgB,EACjC,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,EAAE,cAAc,EAC7B,EAAE,EAAE,SAAS,EACb,aAAa,EAAE,cAAc,EAC7B,WAAW,EAAE,mBAAmB,EAChC,IAAI,EAAE,WAAW;AACjB,4GAA4G;AAC5G,cAAc,CAAC,EAAE,cAAc;AAC/B;;;;;GAKG;AACH,UAAU,CAAC,EAAE,eAAe,GAC3B,OAAO,CAAC,MAAM,CAm/ChB"}
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import { logger } from '../../shared/logging.js';
|
|
3
3
|
const log = logger('workspace.routes');
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import {
|
|
5
|
+
import { isAbsence } from '../../shared/fs.contract.js';
|
|
6
6
|
import { printable } from '../../shared/printable.js';
|
|
7
7
|
import express from 'express';
|
|
8
8
|
import { KNOWLEDGE_DIR, canonicalRelativePath, folderPlaceholderPath, isPlatformFile, isPlatformFolder, isPlatformRestoreShape, isRepositoryOwnFile, repositoryOwnFileDeleteRefusal, platformFileCreationRefusal, platformFileRefusal, platformFolderRefusal, reservedRootDirNames, } from '@bevel-software/platform-shared';
|
|
@@ -585,31 +585,10 @@ changeGate) {
|
|
|
585
585
|
if (rel !== null && structuralRoots.has(rel))
|
|
586
586
|
verdict.set(wp, true);
|
|
587
587
|
}
|
|
588
|
-
// `.bevelignore` is
|
|
589
|
-
//
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
// already hidden from every reader by the shipped ignore rules. Somebody
|
|
593
|
-
// who cannot act on it gains a puzzle; somebody who can needs to reach it.
|
|
594
|
-
//
|
|
595
|
-
// Read permission is deliberately NOT the lever. Everyone can read it —
|
|
596
|
-
// it has to be readable to be applied — so hiding it is a listing
|
|
597
|
-
// decision, made here, rather than an ACL fiction maintained in a file.
|
|
598
|
-
//
|
|
599
|
-
// Resolved through `AdminAccessService` rather than by asking whether
|
|
600
|
-
// this caller can write `roles.yaml` in THIS workspace: admin is settled
|
|
601
|
-
// on the default branch precisely so that editing `roles.yaml` on your
|
|
602
|
-
// own branch cannot promote you, and the same reasoning applies to
|
|
603
|
-
// anything gated on being an admin.
|
|
604
|
-
//
|
|
605
|
-
// `'unlisted'`, not `false`: hiding it withholds no content, so a
|
|
606
|
-
// non-admin in an empty knowledge base (every one ships this file) is
|
|
607
|
-
// told it is empty, not that something is being kept from them.
|
|
608
|
-
const ignoreFiles = wsRelPaths.filter((wp) => path.basename(wp) === IGNORE_FILENAME);
|
|
609
|
-
if (ignoreFiles.length > 0 && !(await adminAccess.isAdmin(userEmail))) {
|
|
610
|
-
for (const wp of ignoreFiles)
|
|
611
|
-
verdict.set(wp, 'unlisted');
|
|
612
|
-
}
|
|
588
|
+
// `.bevelignore` is listed like any other file: whoever may read it sees
|
|
589
|
+
// it. It used to be admins only (a listing decision, since everyone can
|
|
590
|
+
// read it) — Razvan reversed that on 2026-10-23: a reader who can open
|
|
591
|
+
// the file may find it in the tree.
|
|
613
592
|
return verdict;
|
|
614
593
|
};
|
|
615
594
|
}
|