@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
|
@@ -16,6 +16,7 @@ import { createAccessRoutes } from '../access.routes.js';
|
|
|
16
16
|
import { usersDbDouble } from './users-db-double.js';
|
|
17
17
|
import { AccessMutationError, AccessMutationService } from '../access-mutation.service.js';
|
|
18
18
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
19
|
+
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* A file that cannot carry frontmatter is never written by an access mutation.
|
|
@@ -161,6 +162,7 @@ describe('access mutations on a file that cannot carry frontmatter', () => {
|
|
|
161
162
|
{ emit: vi.fn() } as unknown as WorkflowEventBus,
|
|
162
163
|
usersDbDouble(),
|
|
163
164
|
testKbContext({ kbDirName: KB }),
|
|
165
|
+
new NodeFs(),
|
|
164
166
|
),
|
|
165
167
|
);
|
|
166
168
|
server = await new Promise<Server>((resolve) => {
|
|
@@ -12,6 +12,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
|
12
12
|
import { createAccessRoutes } from '../access.routes.js';
|
|
13
13
|
import { usersDbDouble } from './users-db-double.js';
|
|
14
14
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
15
|
+
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* HTTP-level contract tests for GROUP and ROLE principals on the share
|
|
@@ -99,7 +100,7 @@ async function makeHarness(opts: {
|
|
|
99
100
|
(req as unknown as { userId: string }).userId = USER.id;
|
|
100
101
|
next();
|
|
101
102
|
});
|
|
102
|
-
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB })));
|
|
103
|
+
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB }), new NodeFs()));
|
|
103
104
|
|
|
104
105
|
const server = await new Promise<Server>((resolve) => {
|
|
105
106
|
const s = app.listen(0, () => resolve(s));
|
|
@@ -126,7 +127,7 @@ describe('group principals on the share surface', () => {
|
|
|
126
127
|
body: JSON.stringify(body),
|
|
127
128
|
});
|
|
128
129
|
|
|
129
|
-
it('grants read to a known group — spliced as the
|
|
130
|
+
it('grants read to a known group — spliced as the explicit group/<Name> token', async () => {
|
|
130
131
|
h = await makeHarness({ groups: ['GTM Team', 'Product'] });
|
|
131
132
|
const res = await post('grant', {
|
|
132
133
|
path: `${KB}/Sales`,
|
|
@@ -136,8 +137,8 @@ describe('group principals on the share surface', () => {
|
|
|
136
137
|
});
|
|
137
138
|
expect(res.status).toBe(200);
|
|
138
139
|
const md = h.files.get(`${KB}/Sales/access.md`)!;
|
|
139
|
-
expect(md).toContain('- GTM Team');
|
|
140
|
-
expect(md).not.toContain('role/');
|
|
140
|
+
expect(md.split('\n')).toContain(' - group/GTM Team'); // never the bare name
|
|
141
|
+
expect(md).not.toContain('role/');
|
|
141
142
|
});
|
|
142
143
|
|
|
143
144
|
it('grants a ROLE as the explicit role/<Name> token', async () => {
|
|
@@ -190,7 +191,7 @@ describe('group principals on the share surface', () => {
|
|
|
190
191
|
expect(await res.json()).toMatchObject({ kind: 'unknown-role', role: 'Ghost Role' });
|
|
191
192
|
});
|
|
192
193
|
|
|
193
|
-
it('a group sharing a role name is grantable —
|
|
194
|
+
it('a group sharing a role name is grantable — each kind is written with its prefix (no refusal)', async () => {
|
|
194
195
|
h = await makeHarness({ roles: ['Everyone', 'Product'], groups: ['Product'] });
|
|
195
196
|
const asGroup = await post('grant', {
|
|
196
197
|
path: `${KB}/Sales`,
|
|
@@ -199,7 +200,7 @@ describe('group principals on the share surface', () => {
|
|
|
199
200
|
principal: { kind: 'group', group: 'Product' },
|
|
200
201
|
});
|
|
201
202
|
expect(asGroup.status).toBe(200);
|
|
202
|
-
expect(h.files.get(`${KB}/Sales/access.md`)).toContain('- Product');
|
|
203
|
+
expect(h.files.get(`${KB}/Sales/access.md`)).toContain('- group/Product');
|
|
203
204
|
// The ROLE of the same name grants as the explicit token, side by side.
|
|
204
205
|
const asRole = await post('grant', {
|
|
205
206
|
path: `${KB}/Sales`,
|
|
@@ -249,7 +250,19 @@ describe('group principals on the share surface', () => {
|
|
|
249
250
|
principal: { kind: 'group', group: 'Product' },
|
|
250
251
|
});
|
|
251
252
|
expect(res.status).toBe(200);
|
|
252
|
-
expect(h.files.get(`${KB}/Sales/access.md`)).toContain('- Product');
|
|
253
|
+
expect(h.files.get(`${KB}/Sales/access.md`)).toContain('- group/Product');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('a group named with its group/ prefix is written once-prefixed', async () => {
|
|
257
|
+
h = await makeHarness({ groups: ['GTM Team'] });
|
|
258
|
+
const res = await post('grant', {
|
|
259
|
+
path: `${KB}/Sales`,
|
|
260
|
+
kind: 'folder',
|
|
261
|
+
verb: 'read',
|
|
262
|
+
principal: { kind: 'group', group: 'group/GTM Team' },
|
|
263
|
+
});
|
|
264
|
+
expect(res.status).toBe(200);
|
|
265
|
+
expect(h.files.get(`${KB}/Sales/access.md`)!.split('\n')).toContain(' - group/GTM Team');
|
|
253
266
|
});
|
|
254
267
|
|
|
255
268
|
it('a non-admin granting to a group they are not in is refused exactly like an unknown group', async () => {
|
|
@@ -281,9 +294,11 @@ describe('group principals on the share surface', () => {
|
|
|
281
294
|
|
|
282
295
|
it('revokes with a group principal (name-based, no existence check)', async () => {
|
|
283
296
|
h = await makeHarness({
|
|
297
|
+
roles: ['Everyone'],
|
|
284
298
|
groups: ['GTM Team'],
|
|
285
299
|
files: {
|
|
286
|
-
|
|
300
|
+
// Both spellings: the legacy bare one and the explicit one.
|
|
301
|
+
[`${KB}/Sales/access.md`]: '---\nread:\n - Vanished Team\n - group/Vanished Team\n---\n',
|
|
287
302
|
},
|
|
288
303
|
});
|
|
289
304
|
const res = await post('revoke', {
|
|
@@ -369,11 +384,11 @@ describe('token-kind family at the route boundary', () => {
|
|
|
369
384
|
expect(md).toContain('- Admin');
|
|
370
385
|
});
|
|
371
386
|
|
|
372
|
-
it('granting a GROUP that
|
|
387
|
+
it('granting a GROUP that shares an already-granted role name is NOT swallowed as idempotent', async () => {
|
|
373
388
|
h = await makeHarness({
|
|
374
389
|
roles: ['Everyone', 'Product'],
|
|
375
390
|
groups: ['Product'],
|
|
376
|
-
files: { [`${KB}/Sales/access.md`]: '---\nread:\n - role/Product\n---\n' },
|
|
391
|
+
files: { [`${KB}/Sales/access.md`]: '---\nread:\n - role/Product\n - Product\n---\n' },
|
|
377
392
|
});
|
|
378
393
|
const res = await post('grant', {
|
|
379
394
|
path: `${KB}/Sales`,
|
|
@@ -384,15 +399,15 @@ describe('token-kind family at the route boundary', () => {
|
|
|
384
399
|
expect(res.status).toBe(200);
|
|
385
400
|
const md = h.files.get(`${KB}/Sales/access.md`)!;
|
|
386
401
|
expect(md).toContain('- role/Product');
|
|
387
|
-
expect(md.split('\n')).toContain(' - Product'); // the group's
|
|
402
|
+
expect(md.split('\n')).toContain(' - group/Product'); // the group's token landed
|
|
388
403
|
});
|
|
389
404
|
|
|
390
|
-
it(
|
|
405
|
+
it("revoking the ROLE while a same-named GROUP is granted leaves the group's token", async () => {
|
|
391
406
|
h = await makeHarness({
|
|
392
407
|
roles: ['Everyone', 'Product'],
|
|
393
408
|
groups: ['Product'],
|
|
394
409
|
files: {
|
|
395
|
-
[`${KB}/Sales/access.md`]: '---\nwrite:\n - Product\n - role/Product\n---\n',
|
|
410
|
+
[`${KB}/Sales/access.md`]: '---\nwrite:\n - Product\n - role/Product\n - group/Product\n---\n',
|
|
396
411
|
},
|
|
397
412
|
});
|
|
398
413
|
const res = await post('revoke', {
|
|
@@ -403,6 +418,27 @@ describe('token-kind family at the route boundary', () => {
|
|
|
403
418
|
expect(res.status).toBe(200);
|
|
404
419
|
const md = h.files.get(`${KB}/Sales/access.md`)!;
|
|
405
420
|
expect(md).not.toContain('role/Product');
|
|
406
|
-
expect(md.split('\n')).toContain(' - Product'); // the
|
|
421
|
+
expect(md.split('\n')).not.toContain(' - Product'); // the bare name was the role's
|
|
422
|
+
expect(md.split('\n')).toContain(' - group/Product'); // the GROUP's grant survives
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("revoking the GROUP while a same-named ROLE is granted leaves the role's spellings", async () => {
|
|
426
|
+
h = await makeHarness({
|
|
427
|
+
roles: ['Everyone', 'Product'],
|
|
428
|
+
groups: ['Product'],
|
|
429
|
+
files: {
|
|
430
|
+
[`${KB}/Sales/access.md`]: '---\nwrite:\n - Product\n - role/Product\n - group/Product\n---\n',
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
const res = await post('revoke', {
|
|
434
|
+
path: `${KB}/Sales`,
|
|
435
|
+
kind: 'folder',
|
|
436
|
+
principal: { kind: 'group', group: 'Product' },
|
|
437
|
+
});
|
|
438
|
+
expect(res.status).toBe(200);
|
|
439
|
+
const md = h.files.get(`${KB}/Sales/access.md`)!;
|
|
440
|
+
expect(md).not.toContain('group/Product');
|
|
441
|
+
expect(md.split('\n')).toContain(' - Product');
|
|
442
|
+
expect(md).toContain('- role/Product');
|
|
407
443
|
});
|
|
408
444
|
});
|
|
@@ -12,6 +12,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
|
12
12
|
import { createAccessRoutes } from '../access.routes.js';
|
|
13
13
|
import { usersDbDouble } from './users-db-double.js';
|
|
14
14
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
15
|
+
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* HTTP contract for `GET /access/overrides`. The scan itself is covered in
|
|
@@ -98,7 +99,7 @@ async function makeHarness(opts: {
|
|
|
98
99
|
});
|
|
99
100
|
app.use(
|
|
100
101
|
'/api',
|
|
101
|
-
createAccessRoutes(accessControl, workspaceService, authService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB })),
|
|
102
|
+
createAccessRoutes(accessControl, workspaceService, authService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB }), new NodeFs()),
|
|
102
103
|
);
|
|
103
104
|
|
|
104
105
|
const server = await new Promise<Server>((resolve) => {
|
|
@@ -139,6 +139,7 @@ describe('access routes on a proposal branch (real git)', () => {
|
|
|
139
139
|
{ emit: () => {} } as unknown as WorkflowEventBus,
|
|
140
140
|
usersDbDouble(),
|
|
141
141
|
testKbContext({ kbDirName: KB }),
|
|
142
|
+
new NodeFs(),
|
|
142
143
|
),
|
|
143
144
|
);
|
|
144
145
|
server = await new Promise<Server>((resolve) => {
|
|
@@ -11,6 +11,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
|
11
11
|
import type { Database } from '../../database/connection.js';
|
|
12
12
|
import { createAccessRoutes } from '../access.routes.js';
|
|
13
13
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
14
|
+
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* HTTP contract for `GET /access/prospective` — what the move confirmation
|
|
@@ -69,6 +70,7 @@ async function makeHarness(opts: { canRead?: boolean } = {}): Promise<Harness> {
|
|
|
69
70
|
{ emit: vi.fn() } as unknown as WorkflowEventBus,
|
|
70
71
|
{} as unknown as Database,
|
|
71
72
|
testKbContext({ kbDirName: KB }),
|
|
73
|
+
new NodeFs(),
|
|
72
74
|
),
|
|
73
75
|
);
|
|
74
76
|
|
|
@@ -11,6 +11,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
|
11
11
|
import { createAccessRoutes } from '../access.routes.js';
|
|
12
12
|
import { usersDbDouble } from './users-db-double.js';
|
|
13
13
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
14
|
+
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* HTTP-level contract tests for the revoke route's CLASSIFICATION + MODE logic —
|
|
@@ -56,6 +57,8 @@ async function makeHarness(opts: {
|
|
|
56
57
|
effectiveEmails?: Record<string, boolean>;
|
|
57
58
|
/** Override the (otherwise-empty) eligibleWriters list so a principal shows as a row in resolvedView. */
|
|
58
59
|
eligibleWriters?: { roles: string[]; users: { name: string; email: string }[] };
|
|
60
|
+
/** kbPrincipals' roles — empty (the default) reads as a model that failed to load. */
|
|
61
|
+
roles?: string[];
|
|
59
62
|
}): Promise<Harness> {
|
|
60
63
|
const files = new Map<string, string>(Object.entries(opts.files ?? {}));
|
|
61
64
|
|
|
@@ -79,8 +82,8 @@ async function makeHarness(opts: {
|
|
|
79
82
|
invalidate: vi.fn(),
|
|
80
83
|
// The mutation service consults the merged principal index to decide
|
|
81
84
|
// exact-vs-name token matching on revoke (group shadowing). No groups in
|
|
82
|
-
// these fixtures
|
|
83
|
-
kbPrincipals: vi.fn(async () => ({ roles: [], groups: [], people: [] })),
|
|
85
|
+
// these fixtures; no roles (the default) reads as a failed load → exact.
|
|
86
|
+
kbPrincipals: vi.fn(async () => ({ roles: opts.roles ?? [], groups: [], people: [] })),
|
|
84
87
|
// resolvedView() fans out to these after a successful mutation; harmless stubs.
|
|
85
88
|
// canRead/canDownload/canOwner are ALSO consumed by denyHere's assert.
|
|
86
89
|
canRead: vi.fn(async (_w: string, email: string) => effEmail(email, true)),
|
|
@@ -134,7 +137,7 @@ async function makeHarness(opts: {
|
|
|
134
137
|
(req as unknown as { userId: string }).userId = USER.id;
|
|
135
138
|
next();
|
|
136
139
|
});
|
|
137
|
-
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB })));
|
|
140
|
+
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB }), new NodeFs()));
|
|
138
141
|
|
|
139
142
|
const server = await new Promise<Server>((resolve) => {
|
|
140
143
|
const s = app.listen(0, () => resolve(s));
|
|
@@ -285,16 +288,16 @@ describe('POST /access/revoke — route classification + modes', () => {
|
|
|
285
288
|
expect(h.stubs.files.get(`${KB}/Sales/access.md`)).toContain('Admin'); // untouched
|
|
286
289
|
});
|
|
287
290
|
|
|
288
|
-
it('a GROUP revoke
|
|
289
|
-
// The file grants
|
|
290
|
-
//
|
|
291
|
-
// vanished from the active source
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
291
|
+
it('a GROUP revoke strips group/<Name> — a VANISHED group never strips the role spellings', async () => {
|
|
292
|
+
// The file grants every spelling of one name: the bare token and
|
|
293
|
+
// role/Product (the role's) and group/Product (the group's). The group
|
|
294
|
+
// has since vanished from the active source and the principal index
|
|
295
|
+
// says nothing about roles either (kbPrincipals reports none), so
|
|
296
|
+
// nothing vouches that the bare name is the group's: only the explicit
|
|
297
|
+
// group token goes.
|
|
295
298
|
h = await makeHarness({
|
|
296
299
|
files: {
|
|
297
|
-
[`${KB}/Sales/access.md`]: '---\nwrite:\n - Product\n - role/Product\n---\n',
|
|
300
|
+
[`${KB}/Sales/access.md`]: '---\nwrite:\n - Product\n - role/Product\n - group/Product\n---\n',
|
|
298
301
|
},
|
|
299
302
|
grantSources: () => ({}),
|
|
300
303
|
});
|
|
@@ -305,9 +308,10 @@ describe('POST /access/revoke — route classification + modes', () => {
|
|
|
305
308
|
});
|
|
306
309
|
expect(res.status).toBe(200);
|
|
307
310
|
const text = h.stubs.files.get(`${KB}/Sales/access.md`)!;
|
|
308
|
-
// The group's
|
|
311
|
+
// The group's grant is gone; the role's spellings survive.
|
|
312
|
+
expect(text).not.toContain('group/Product');
|
|
309
313
|
expect(text).toContain('- role/Product');
|
|
310
|
-
expect(text.split('\n')).
|
|
314
|
+
expect(text.split('\n')).toContain(' - Product');
|
|
311
315
|
});
|
|
312
316
|
|
|
313
317
|
it('a ROLE revoke still sweeps legacy bare spellings when nothing shadows the name', async () => {
|
|
@@ -318,6 +322,7 @@ describe('POST /access/revoke — route classification + modes', () => {
|
|
|
318
322
|
[`${KB}/Sales/access.md`]: '---\nwrite:\n - Product\n - role/Product\n---\n',
|
|
319
323
|
},
|
|
320
324
|
grantSources: () => ({}),
|
|
325
|
+
roles: ['Everyone', 'Product'],
|
|
321
326
|
});
|
|
322
327
|
const res = await revoke({
|
|
323
328
|
path: `${KB}/Sales`,
|
|
@@ -12,6 +12,7 @@ import { createAccessRoutes } from '../access.routes.js';
|
|
|
12
12
|
import type { Database } from '../../database/connection.js';
|
|
13
13
|
import { usersDbDouble } from './users-db-double.js';
|
|
14
14
|
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
15
|
+
import { NodeFs } from '../../kb-fs/node-fs.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* A grant to an email with NO ACCOUNT says so, and stays allowed.
|
|
@@ -112,6 +113,7 @@ async function makeHarness(opts: {
|
|
|
112
113
|
{ emit: vi.fn() } as unknown as WorkflowEventBus,
|
|
113
114
|
opts.db ?? usersDbDouble(opts.accounts ?? []),
|
|
114
115
|
testKbContext({ kbDirName: KB }),
|
|
116
|
+
new NodeFs(),
|
|
115
117
|
),
|
|
116
118
|
);
|
|
117
119
|
|
|
@@ -204,7 +204,7 @@ describe('AccessControlService.grantSources', () => {
|
|
|
204
204
|
});
|
|
205
205
|
});
|
|
206
206
|
|
|
207
|
-
describe('grantSources — principal kind
|
|
207
|
+
describe('grantSources — principal kind when a group shares a role name', () => {
|
|
208
208
|
let root: string;
|
|
209
209
|
let repo: string;
|
|
210
210
|
let svc: AccessControlService;
|
|
@@ -216,8 +216,8 @@ describe('grantSources — principal kind under group shadowing', () => {
|
|
|
216
216
|
repo = path.join(workspaceDir, PROCESS_MAP_DIR);
|
|
217
217
|
await fs.mkdir(repo, { recursive: true });
|
|
218
218
|
await writeFile(repo, 'roles.yaml', ROLES_YAML);
|
|
219
|
-
// A GROUP named Engineer
|
|
220
|
-
//
|
|
219
|
+
// A GROUP named Engineer shares the Engineer ROLE's name: the bare token
|
|
220
|
+
// and `role/engineer` are the role's, `group/engineer` is the group's.
|
|
221
221
|
await writeFile(repo, 'groups.yaml', 'groups:\n Engineer:\n - pat@x.io\n');
|
|
222
222
|
svc = new AccessControlService(stubWorkspaceService(workspaceId, workspaceDir), PROCESS_MAP_DIR, new NodeFs());
|
|
223
223
|
});
|
|
@@ -226,13 +226,13 @@ describe('grantSources — principal kind under group shadowing', () => {
|
|
|
226
226
|
await fs.rm(root, { recursive: true, force: true });
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
-
it("a
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
229
|
+
it("a group/ grant belongs to the GROUP — it must not hide the role's ancestor grant", async () => {
|
|
230
|
+
// group/ grant on the target, role/ grant on the ancestor. Attributing the
|
|
231
|
+
// group's token to the role would report the role as `direct` here —
|
|
232
|
+
// hiding the real role/<Name> ancestor grant and making its revoke a
|
|
233
|
+
// false no-op.
|
|
234
234
|
await writeFile(repo, 'access.md', '---\nwrite:\n - role/Engineer\n---\n');
|
|
235
|
-
await writeFile(repo, 'Knowledge/access.md', '---\nwrite:\n - Engineer\n---\n');
|
|
235
|
+
await writeFile(repo, 'Knowledge/access.md', '---\nwrite:\n - group/Engineer\n---\n');
|
|
236
236
|
|
|
237
237
|
const roleSrc = await svc.grantSources(workspaceId, 'folder', 'Knowledge', {
|
|
238
238
|
kind: 'role',
|
|
@@ -243,23 +243,28 @@ describe('grantSources — principal kind under group shadowing', () => {
|
|
|
243
243
|
|
|
244
244
|
const groupSrc = await svc.grantSources(workspaceId, 'folder', 'Knowledge', {
|
|
245
245
|
kind: 'role',
|
|
246
|
-
role: 'Engineer',
|
|
246
|
+
role: 'group/Engineer',
|
|
247
247
|
});
|
|
248
|
-
// The GROUP's only entry is the direct
|
|
248
|
+
// The GROUP's only entry is the direct group/ grant.
|
|
249
249
|
expect(groupSrc.write).toEqual([{ kind: 'direct' }]);
|
|
250
250
|
});
|
|
251
251
|
|
|
252
|
-
it(
|
|
252
|
+
it('a BARE grant of a shared name is the ROLE\'s, never attributed to the GROUP principal', async () => {
|
|
253
253
|
await writeFile(repo, 'access.md', '---\nwrite:\n - Admin\n---\n');
|
|
254
|
-
await writeFile(repo, 'Knowledge/access.md', '---\nwrite:\n -
|
|
254
|
+
await writeFile(repo, 'Knowledge/access.md', '---\nwrite:\n - Engineer\n---\n');
|
|
255
255
|
const groupSrc = await svc.grantSources(workspaceId, 'folder', 'Knowledge', {
|
|
256
256
|
kind: 'role',
|
|
257
|
-
role: 'Engineer',
|
|
257
|
+
role: 'group/Engineer',
|
|
258
258
|
});
|
|
259
259
|
expect(groupSrc).toEqual({}); // the group holds nothing here
|
|
260
|
+
const roleSrc = await svc.grantSources(workspaceId, 'folder', 'Knowledge', {
|
|
261
|
+
kind: 'role',
|
|
262
|
+
role: 'role/Engineer',
|
|
263
|
+
});
|
|
264
|
+
expect(roleSrc.write).toEqual([{ kind: 'direct' }]);
|
|
260
265
|
});
|
|
261
266
|
|
|
262
|
-
it('
|
|
267
|
+
it('with no group of the name, both spellings count as the role (legacy bare grants stay removable)', async () => {
|
|
263
268
|
await fs.rm(path.join(repo, 'groups.yaml'));
|
|
264
269
|
await writeFile(repo, 'access.md', '---\nwrite:\n - role/Engineer\n---\n');
|
|
265
270
|
await writeFile(repo, 'Knowledge/access.md', '---\nwrite:\n - Engineer\n---\n');
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Server } from 'node:http';
|
|
2
|
+
import type { AddressInfo } from 'node:net';
|
|
3
|
+
import express from 'express';
|
|
4
|
+
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import type { IAdminAccessService } from '../../admin/index.js';
|
|
7
|
+
import type { GroupsAdminService } from '../groups-admin.service.js';
|
|
8
|
+
import { createGroupsAdminRoutes } from '../groups-admin.routes.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* GET /admin/groups is a read: the roster is filtered for the email the admin
|
|
12
|
+
* gate checked, and a failing user lookup (needed only to attribute commits)
|
|
13
|
+
* never fails it.
|
|
14
|
+
*/
|
|
15
|
+
describe('GET /admin/groups', () => {
|
|
16
|
+
let server: Server | null = null;
|
|
17
|
+
|
|
18
|
+
afterEach(async () => {
|
|
19
|
+
await new Promise<void>((resolve) => (server ? server.close(() => resolve()) : resolve()));
|
|
20
|
+
server = null;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
async function start(email: string, isAdmin: boolean) {
|
|
24
|
+
const getRoster = vi.fn(async (viewer: string) => ({ viewer, groups: [] }));
|
|
25
|
+
const getUserById = vi.fn(async () => {
|
|
26
|
+
throw new Error('users table unavailable');
|
|
27
|
+
});
|
|
28
|
+
const app = express();
|
|
29
|
+
app.use((req, _res, next) => {
|
|
30
|
+
req.userId = 'u-1';
|
|
31
|
+
req.userEmail = email;
|
|
32
|
+
next();
|
|
33
|
+
});
|
|
34
|
+
app.use(
|
|
35
|
+
createGroupsAdminRoutes({
|
|
36
|
+
groupsAdmin: { getRoster } as unknown as GroupsAdminService,
|
|
37
|
+
adminAccess: { isAdmin: async () => isAdmin } as unknown as IAdminAccessService,
|
|
38
|
+
getUserById,
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
server = app.listen(0);
|
|
42
|
+
await new Promise<void>((resolve) => server!.once('listening', () => resolve()));
|
|
43
|
+
const { port } = server.address() as AddressInfo;
|
|
44
|
+
return { url: `http://127.0.0.1:${port}/admin/groups`, getRoster, getUserById };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
it('reads the roster for the gated email without a user lookup', async () => {
|
|
48
|
+
const { url, getRoster, getUserById } = await start('admin@x.io', true);
|
|
49
|
+
const res = await fetch(url);
|
|
50
|
+
expect(res.status).toBe(200);
|
|
51
|
+
expect(await res.json()).toEqual({ viewer: 'admin@x.io', groups: [] });
|
|
52
|
+
expect(getRoster).toHaveBeenCalledWith('admin@x.io');
|
|
53
|
+
expect(getUserById).not.toHaveBeenCalled();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('still refuses a non-admin', async () => {
|
|
57
|
+
const { url, getRoster } = await start('pat@x.io', false);
|
|
58
|
+
expect((await fetch(url)).status).toBe(403);
|
|
59
|
+
expect(getRoster).not.toHaveBeenCalled();
|
|
60
|
+
});
|
|
61
|
+
});
|