@bevel-software/platform-core-backend 0.1.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/LICENSE +202 -0
- package/dist/assets.d.ts +5 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +23 -0
- package/dist/assets.js.map +1 -0
- package/dist/core/core-ports.d.ts +83 -0
- package/dist/core/core-ports.d.ts.map +1 -0
- package/dist/core/core-ports.js +12 -0
- package/dist/core/core-ports.js.map +1 -0
- package/dist/core/create-core-server.d.ts +62 -0
- package/dist/core/create-core-server.d.ts.map +1 -0
- package/dist/core/create-core-server.js +204 -0
- package/dist/core/create-core-server.js.map +1 -0
- package/dist/core/create-core-services.d.ts +95 -0
- package/dist/core/create-core-services.d.ts.map +1 -0
- package/dist/core/create-core-services.js +352 -0
- package/dist/core/create-core-services.js.map +1 -0
- package/dist/core-config.d.ts +119 -0
- package/dist/core-config.d.ts.map +1 -0
- package/dist/core-config.js +240 -0
- package/dist/core-config.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/access/access-control.interface.d.ts +348 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -0
- package/dist/modules/access/access-control.interface.js +2 -0
- package/dist/modules/access/access-control.interface.js.map +1 -0
- package/dist/modules/access/access-control.service.d.ts +323 -0
- package/dist/modules/access/access-control.service.d.ts.map +1 -0
- package/dist/modules/access/access-control.service.js +1431 -0
- package/dist/modules/access/access-control.service.js.map +1 -0
- package/dist/modules/access/access-errors.d.ts +34 -0
- package/dist/modules/access/access-errors.d.ts.map +1 -0
- package/dist/modules/access/access-errors.js +40 -0
- package/dist/modules/access/access-errors.js.map +1 -0
- package/dist/modules/access/access-mutation.service.d.ts +176 -0
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -0
- package/dist/modules/access/access-mutation.service.js +307 -0
- package/dist/modules/access/access-mutation.service.js.map +1 -0
- package/dist/modules/access/access-splice.d.ts +98 -0
- package/dist/modules/access/access-splice.d.ts.map +1 -0
- package/dist/modules/access/access-splice.js +346 -0
- package/dist/modules/access/access-splice.js.map +1 -0
- package/dist/modules/access/access.routes.d.ts +10 -0
- package/dist/modules/access/access.routes.d.ts.map +1 -0
- package/dist/modules/access/access.routes.js +757 -0
- package/dist/modules/access/access.routes.js.map +1 -0
- package/dist/modules/access/creator-access.d.ts +113 -0
- package/dist/modules/access/creator-access.d.ts.map +1 -0
- package/dist/modules/access/creator-access.js +212 -0
- package/dist/modules/access/creator-access.js.map +1 -0
- package/dist/modules/access/kb-read-filter.d.ts +41 -0
- package/dist/modules/access/kb-read-filter.d.ts.map +1 -0
- package/dist/modules/access/kb-read-filter.js +60 -0
- package/dist/modules/access/kb-read-filter.js.map +1 -0
- package/dist/modules/access/roles-admin.service.d.ts +240 -0
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -0
- package/dist/modules/access/roles-admin.service.js +630 -0
- package/dist/modules/access/roles-admin.service.js.map +1 -0
- package/dist/modules/access/roles-edit.d.ts +91 -0
- package/dist/modules/access/roles-edit.d.ts.map +1 -0
- package/dist/modules/access/roles-edit.js +232 -0
- package/dist/modules/access/roles-edit.js.map +1 -0
- package/dist/modules/access/roles-yaml-guard.d.ts +56 -0
- package/dist/modules/access/roles-yaml-guard.d.ts.map +1 -0
- package/dist/modules/access/roles-yaml-guard.js +79 -0
- package/dist/modules/access/roles-yaml-guard.js.map +1 -0
- package/dist/modules/admin/admin-access.routes.d.ts +18 -0
- package/dist/modules/admin/admin-access.routes.d.ts.map +1 -0
- package/dist/modules/admin/admin-access.routes.js +26 -0
- package/dist/modules/admin/admin-access.routes.js.map +1 -0
- package/dist/modules/admin/admin-access.service.d.ts +24 -0
- package/dist/modules/admin/admin-access.service.d.ts.map +1 -0
- package/dist/modules/admin/admin-access.service.js +41 -0
- package/dist/modules/admin/admin-access.service.js.map +1 -0
- package/dist/modules/admin/admin.interface.d.ts +11 -0
- package/dist/modules/admin/admin.interface.d.ts.map +1 -0
- package/dist/modules/admin/admin.interface.js +2 -0
- package/dist/modules/admin/admin.interface.js.map +1 -0
- package/dist/modules/auth/account-erasure.service.d.ts +80 -0
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -0
- package/dist/modules/auth/account-erasure.service.js +91 -0
- package/dist/modules/auth/account-erasure.service.js.map +1 -0
- package/dist/modules/auth/auth.middleware.d.ts +23 -0
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -0
- package/dist/modules/auth/auth.middleware.js +68 -0
- package/dist/modules/auth/auth.middleware.js.map +1 -0
- package/dist/modules/auth/auth.routes.d.ts +25 -0
- package/dist/modules/auth/auth.routes.d.ts.map +1 -0
- package/dist/modules/auth/auth.routes.js +72 -0
- package/dist/modules/auth/auth.routes.js.map +1 -0
- package/dist/modules/auth/auth.service.d.ts +76 -0
- package/dist/modules/auth/auth.service.d.ts.map +1 -0
- package/dist/modules/auth/auth.service.js +168 -0
- package/dist/modules/auth/auth.service.js.map +1 -0
- package/dist/modules/code-mode/code-mode-names.d.ts +16 -0
- package/dist/modules/code-mode/code-mode-names.d.ts.map +1 -0
- package/dist/modules/code-mode/code-mode-names.js +30 -0
- package/dist/modules/code-mode/code-mode-names.js.map +1 -0
- package/dist/modules/code-mode/code-mode.tool.d.ts +13 -0
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -0
- package/dist/modules/code-mode/code-mode.tool.js +112 -0
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -0
- package/dist/modules/code-mode/index.d.ts +3 -0
- package/dist/modules/code-mode/index.d.ts.map +1 -0
- package/dist/modules/code-mode/index.js +3 -0
- package/dist/modules/code-mode/index.js.map +1 -0
- package/dist/modules/database/connection.d.ts +6 -0
- package/dist/modules/database/connection.d.ts.map +1 -0
- package/dist/modules/database/connection.js +12 -0
- package/dist/modules/database/connection.js.map +1 -0
- package/dist/modules/database/core-schema.d.ts +2273 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -0
- package/dist/modules/database/core-schema.js +413 -0
- package/dist/modules/database/core-schema.js.map +1 -0
- package/dist/modules/database/migrate.d.ts +10 -0
- package/dist/modules/database/migrate.d.ts.map +1 -0
- package/dist/modules/database/migrate.js +40 -0
- package/dist/modules/database/migrate.js.map +1 -0
- package/dist/modules/database/schema.d.ts +12 -0
- package/dist/modules/database/schema.d.ts.map +1 -0
- package/dist/modules/database/schema.js +12 -0
- package/dist/modules/database/schema.js.map +1 -0
- package/dist/modules/diff/diff-paths.d.ts +2 -0
- package/dist/modules/diff/diff-paths.d.ts.map +1 -0
- package/dist/modules/diff/diff-paths.js +9 -0
- package/dist/modules/diff/diff-paths.js.map +1 -0
- package/dist/modules/diff/diff.config.d.ts +8 -0
- package/dist/modules/diff/diff.config.d.ts.map +1 -0
- package/dist/modules/diff/diff.config.js +13 -0
- package/dist/modules/diff/diff.config.js.map +1 -0
- package/dist/modules/diff/diff.interface.d.ts +73 -0
- package/dist/modules/diff/diff.interface.d.ts.map +1 -0
- package/dist/modules/diff/diff.interface.js +2 -0
- package/dist/modules/diff/diff.interface.js.map +1 -0
- package/dist/modules/diff/diff.routes.d.ts +35 -0
- package/dist/modules/diff/diff.routes.d.ts.map +1 -0
- package/dist/modules/diff/diff.routes.js +228 -0
- package/dist/modules/diff/diff.routes.js.map +1 -0
- package/dist/modules/diff/diff.service.d.ts +51 -0
- package/dist/modules/diff/diff.service.d.ts.map +1 -0
- package/dist/modules/diff/diff.service.js +489 -0
- package/dist/modules/diff/diff.service.js.map +1 -0
- package/dist/modules/diff/line-diff.d.ts +16 -0
- package/dist/modules/diff/line-diff.d.ts.map +1 -0
- package/dist/modules/diff/line-diff.js +67 -0
- package/dist/modules/diff/line-diff.js.map +1 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +27 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -0
- package/dist/modules/mcp/mcp-auth.middleware.js +116 -0
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -0
- package/dist/modules/mcp/mcp-session-store.d.ts +74 -0
- package/dist/modules/mcp/mcp-session-store.d.ts.map +1 -0
- package/dist/modules/mcp/mcp-session-store.js +131 -0
- package/dist/modules/mcp/mcp-session-store.js.map +1 -0
- package/dist/modules/mcp/mcp.routes.d.ts +24 -0
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -0
- package/dist/modules/mcp/mcp.routes.js +229 -0
- package/dist/modules/mcp/mcp.routes.js.map +1 -0
- package/dist/modules/mcp/mcp.service.d.ts +198 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -0
- package/dist/modules/mcp/mcp.service.js +840 -0
- package/dist/modules/mcp/mcp.service.js.map +1 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +76 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +291 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -0
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +18 -0
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -0
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +52 -0
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -0
- package/dist/modules/mcp/oauth/oauth-state.d.ts +32 -0
- package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -0
- package/dist/modules/mcp/oauth/oauth-state.js +34 -0
- package/dist/modules/mcp/oauth/oauth-state.js.map +1 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +92 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +728 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -0
- package/dist/modules/secrets-vault/index.d.ts +7 -0
- package/dist/modules/secrets-vault/index.d.ts.map +1 -0
- package/dist/modules/secrets-vault/index.js +6 -0
- package/dist/modules/secrets-vault/index.js.map +1 -0
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts +63 -0
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -0
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +269 -0
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -0
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +46 -0
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -0
- package/dist/modules/secrets-vault/secrets-variable-loader.js +87 -0
- package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +210 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js +90 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +30 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.js +490 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -0
- package/dist/modules/skills/index.d.ts +5 -0
- package/dist/modules/skills/index.d.ts.map +1 -0
- package/dist/modules/skills/index.js +4 -0
- package/dist/modules/skills/index.js.map +1 -0
- package/dist/modules/skills/skills.contract.d.ts +58 -0
- package/dist/modules/skills/skills.contract.d.ts.map +1 -0
- package/dist/modules/skills/skills.contract.js +10 -0
- package/dist/modules/skills/skills.contract.js.map +1 -0
- package/dist/modules/skills/skills.routes.d.ts +15 -0
- package/dist/modules/skills/skills.routes.d.ts.map +1 -0
- package/dist/modules/skills/skills.routes.js +34 -0
- package/dist/modules/skills/skills.routes.js.map +1 -0
- package/dist/modules/skills/skills.service.d.ts +21 -0
- package/dist/modules/skills/skills.service.d.ts.map +1 -0
- package/dist/modules/skills/skills.service.js +202 -0
- package/dist/modules/skills/skills.service.js.map +1 -0
- package/dist/modules/skills/skills.tools.d.ts +15 -0
- package/dist/modules/skills/skills.tools.d.ts.map +1 -0
- package/dist/modules/skills/skills.tools.js +96 -0
- package/dist/modules/skills/skills.tools.js.map +1 -0
- package/dist/modules/tool-auth/external-api-key.errors.d.ts +14 -0
- package/dist/modules/tool-auth/external-api-key.errors.d.ts.map +1 -0
- package/dist/modules/tool-auth/external-api-key.errors.js +23 -0
- package/dist/modules/tool-auth/external-api-key.errors.js.map +1 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +90 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -0
- package/dist/modules/tool-auth/external-api-key.interface.js +2 -0
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -0
- package/dist/modules/tool-auth/external-api-key.service.d.ts +25 -0
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -0
- package/dist/modules/tool-auth/external-api-key.service.js +164 -0
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -0
- package/dist/modules/tool-auth/internal-token.service.d.ts +62 -0
- package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -0
- package/dist/modules/tool-auth/internal-token.service.js +85 -0
- package/dist/modules/tool-auth/internal-token.service.js.map +1 -0
- package/dist/modules/tool-auth/llm-usage-meter.d.ts +16 -0
- package/dist/modules/tool-auth/llm-usage-meter.d.ts.map +1 -0
- package/dist/modules/tool-auth/llm-usage-meter.js +6 -0
- package/dist/modules/tool-auth/llm-usage-meter.js.map +1 -0
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +101 -0
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -0
- package/dist/modules/tool-auth/tool-auth.middleware.js +166 -0
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -0
- package/dist/modules/tool-helpers/index.d.ts +19 -0
- package/dist/modules/tool-helpers/index.d.ts.map +1 -0
- package/dist/modules/tool-helpers/index.js +18 -0
- package/dist/modules/tool-helpers/index.js.map +1 -0
- package/dist/modules/tool-helpers/tool-context.d.ts +35 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -0
- package/dist/modules/tool-helpers/tool-context.js +66 -0
- package/dist/modules/tool-helpers/tool-context.js.map +1 -0
- package/dist/modules/tool-helpers/tool-def.d.ts +33 -0
- package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -0
- package/dist/modules/tool-helpers/tool-def.js +57 -0
- package/dist/modules/tool-helpers/tool-def.js.map +1 -0
- package/dist/modules/tool-helpers/tool-handler.d.ts +19 -0
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -0
- package/dist/modules/tool-helpers/tool-handler.js +84 -0
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -0
- package/dist/modules/tool-helpers/tool.contract.d.ts +64 -0
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -0
- package/dist/modules/tool-helpers/tool.contract.js +24 -0
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -0
- package/dist/modules/tool-helpers/validate-token.d.ts +31 -0
- package/dist/modules/tool-helpers/validate-token.d.ts.map +1 -0
- package/dist/modules/tool-helpers/validate-token.js +20 -0
- package/dist/modules/tool-helpers/validate-token.js.map +1 -0
- package/dist/modules/tool-manuals/index.d.ts +5 -0
- package/dist/modules/tool-manuals/index.d.ts.map +1 -0
- package/dist/modules/tool-manuals/index.js +5 -0
- package/dist/modules/tool-manuals/index.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +182 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.js +19 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +24 -0
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.routes.js +102 -0
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +108 -0
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.service.js +710 -0
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +35 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.js +157 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -0
- package/dist/modules/tool-registry/manual.routes.d.ts +6 -0
- package/dist/modules/tool-registry/manual.routes.d.ts.map +1 -0
- package/dist/modules/tool-registry/manual.routes.js +76 -0
- package/dist/modules/tool-registry/manual.routes.js.map +1 -0
- package/dist/modules/tool-registry/tool-registry.d.ts +23 -0
- package/dist/modules/tool-registry/tool-registry.d.ts.map +1 -0
- package/dist/modules/tool-registry/tool-registry.js +46 -0
- package/dist/modules/tool-registry/tool-registry.js.map +1 -0
- package/dist/modules/tool-registry/tool.contract.d.ts +48 -0
- package/dist/modules/tool-registry/tool.contract.d.ts.map +1 -0
- package/dist/modules/tool-registry/tool.contract.js +2 -0
- package/dist/modules/tool-registry/tool.contract.js.map +1 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +14 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.js +437 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -0
- package/dist/modules/workflow/event-bus.d.ts +107 -0
- package/dist/modules/workflow/event-bus.d.ts.map +1 -0
- package/dist/modules/workflow/event-bus.js +224 -0
- package/dist/modules/workflow/event-bus.js.map +1 -0
- package/dist/modules/workflow/events.routes.d.ts +80 -0
- package/dist/modules/workflow/events.routes.d.ts.map +1 -0
- package/dist/modules/workflow/events.routes.js +216 -0
- package/dist/modules/workflow/events.routes.js.map +1 -0
- package/dist/modules/workflow/file-change-notifier.d.ts +38 -0
- package/dist/modules/workflow/file-change-notifier.d.ts.map +1 -0
- package/dist/modules/workflow/file-change-notifier.js +22 -0
- package/dist/modules/workflow/file-change-notifier.js.map +1 -0
- package/dist/modules/workflow/file-lock.service.d.ts +57 -0
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -0
- package/dist/modules/workflow/file-lock.service.js +202 -0
- package/dist/modules/workflow/file-lock.service.js.map +1 -0
- package/dist/modules/workflow/git/branch-name.d.ts +10 -0
- package/dist/modules/workflow/git/branch-name.d.ts.map +1 -0
- package/dist/modules/workflow/git/branch-name.js +76 -0
- package/dist/modules/workflow/git/branch-name.js.map +1 -0
- package/dist/modules/workflow/git/clone-config.d.ts +61 -0
- package/dist/modules/workflow/git/clone-config.d.ts.map +1 -0
- package/dist/modules/workflow/git/clone-config.js +69 -0
- package/dist/modules/workflow/git/clone-config.js.map +1 -0
- package/dist/modules/workflow/git/git-version.d.ts +34 -0
- package/dist/modules/workflow/git/git-version.d.ts.map +1 -0
- package/dist/modules/workflow/git/git-version.js +63 -0
- package/dist/modules/workflow/git/git-version.js.map +1 -0
- package/dist/modules/workflow/git/git.service.d.ts +427 -0
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -0
- package/dist/modules/workflow/git/git.service.js +1987 -0
- package/dist/modules/workflow/git/git.service.js.map +1 -0
- package/dist/modules/workflow/git/mutex.d.ts +11 -0
- package/dist/modules/workflow/git/mutex.d.ts.map +1 -0
- package/dist/modules/workflow/git/mutex.js +23 -0
- package/dist/modules/workflow/git/mutex.js.map +1 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts +124 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -0
- package/dist/modules/workflow/git/pull-request.service.js +328 -0
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -0
- package/dist/modules/workflow/locking-filesystem.d.ts +133 -0
- package/dist/modules/workflow/locking-filesystem.d.ts.map +1 -0
- package/dist/modules/workflow/locking-filesystem.js +400 -0
- package/dist/modules/workflow/locking-filesystem.js.map +1 -0
- package/dist/modules/workflow/pending-commits.service.d.ts +221 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -0
- package/dist/modules/workflow/pending-commits.service.js +364 -0
- package/dist/modules/workflow/pending-commits.service.js.map +1 -0
- package/dist/modules/workflow/pending-commits.worker.d.ts +148 -0
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -0
- package/dist/modules/workflow/pending-commits.worker.js +245 -0
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -0
- package/dist/modules/workflow/read-only-filesystem.d.ts +24 -0
- package/dist/modules/workflow/read-only-filesystem.d.ts.map +1 -0
- package/dist/modules/workflow/read-only-filesystem.js +39 -0
- package/dist/modules/workflow/read-only-filesystem.js.map +1 -0
- package/dist/modules/workflow/recovery-bot.d.ts +31 -0
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -0
- package/dist/modules/workflow/recovery-bot.js +64 -0
- package/dist/modules/workflow/recovery-bot.js.map +1 -0
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +137 -0
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -0
- package/dist/modules/workflow/review-workflow/review-workflow.interface.js +2 -0
- package/dist/modules/workflow/review-workflow/review-workflow.interface.js.map +1 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +26 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +636 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -0
- package/dist/modules/workflow/sanitize-error.d.ts +29 -0
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -0
- package/dist/modules/workflow/sanitize-error.js +60 -0
- package/dist/modules/workflow/sanitize-error.js.map +1 -0
- package/dist/modules/workflow/session-ontology.policy.d.ts +52 -0
- package/dist/modules/workflow/session-ontology.policy.d.ts.map +1 -0
- package/dist/modules/workflow/session-ontology.policy.js +62 -0
- package/dist/modules/workflow/session-ontology.policy.js.map +1 -0
- package/dist/modules/workflow/session-ontology.service.d.ts +100 -0
- package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -0
- package/dist/modules/workflow/session-ontology.service.js +143 -0
- package/dist/modules/workflow/session-ontology.service.js.map +1 -0
- package/dist/modules/workflow/workflow-hooks.d.ts +87 -0
- package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -0
- package/dist/modules/workflow/workflow-hooks.js +35 -0
- package/dist/modules/workflow/workflow-hooks.js.map +1 -0
- package/dist/modules/workflow/workflow.errors.d.ts +171 -0
- package/dist/modules/workflow/workflow.errors.d.ts.map +1 -0
- package/dist/modules/workflow/workflow.errors.js +260 -0
- package/dist/modules/workflow/workflow.errors.js.map +1 -0
- package/dist/modules/workflow/workflow.routes.d.ts +26 -0
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -0
- package/dist/modules/workflow/workflow.routes.js +804 -0
- package/dist/modules/workflow/workflow.routes.js.map +1 -0
- package/dist/modules/workflow/workflow.service.d.ts +385 -0
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -0
- package/dist/modules/workflow/workflow.service.js +1155 -0
- package/dist/modules/workflow/workflow.service.js.map +1 -0
- package/dist/modules/workspace/bevel-ignore.d.ts +25 -0
- package/dist/modules/workspace/bevel-ignore.d.ts.map +1 -0
- package/dist/modules/workspace/bevel-ignore.js +57 -0
- package/dist/modules/workspace/bevel-ignore.js.map +1 -0
- package/dist/modules/workspace/kb-seed.interface.d.ts +36 -0
- package/dist/modules/workspace/kb-seed.interface.d.ts.map +1 -0
- package/dist/modules/workspace/kb-seed.interface.js +2 -0
- package/dist/modules/workspace/kb-seed.interface.js.map +1 -0
- package/dist/modules/workspace/kb-seed.service.d.ts +70 -0
- package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -0
- package/dist/modules/workspace/kb-seed.service.js +325 -0
- package/dist/modules/workspace/kb-seed.service.js.map +1 -0
- package/dist/modules/workspace/routine-write-policy.d.ts +49 -0
- package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -0
- package/dist/modules/workspace/routine-write-policy.js +46 -0
- package/dist/modules/workspace/routine-write-policy.js.map +1 -0
- package/dist/modules/workspace/session-ontology.gate.d.ts +112 -0
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -0
- package/dist/modules/workspace/session-ontology.gate.js +161 -0
- package/dist/modules/workspace/session-ontology.gate.js.map +1 -0
- package/dist/modules/workspace/session-sink.d.ts +25 -0
- package/dist/modules/workspace/session-sink.d.ts.map +1 -0
- package/dist/modules/workspace/session-sink.js +8 -0
- package/dist/modules/workspace/session-sink.js.map +1 -0
- package/dist/modules/workspace/spill-store.d.ts +24 -0
- package/dist/modules/workspace/spill-store.d.ts.map +1 -0
- package/dist/modules/workspace/spill-store.js +87 -0
- package/dist/modules/workspace/spill-store.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts +25 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -0
- package/dist/modules/workspace/workspace.routes.js +1046 -0
- package/dist/modules/workspace/workspace.routes.js.map +1 -0
- package/dist/modules/workspace/workspace.service.d.ts +339 -0
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -0
- package/dist/modules/workspace/workspace.service.js +1221 -0
- package/dist/modules/workspace/workspace.service.js.map +1 -0
- package/dist/modules/workspace/workspace.tools.d.ts +19 -0
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -0
- package/dist/modules/workspace/workspace.tools.js +767 -0
- package/dist/modules/workspace/workspace.tools.js.map +1 -0
- package/dist/shared/frontmatter-id.d.ts +35 -0
- package/dist/shared/frontmatter-id.d.ts.map +1 -0
- package/dist/shared/frontmatter-id.js +68 -0
- package/dist/shared/frontmatter-id.js.map +1 -0
- package/dist/shared/fs-walk.d.ts +9 -0
- package/dist/shared/fs-walk.d.ts.map +1 -0
- package/dist/shared/fs-walk.js +34 -0
- package/dist/shared/fs-walk.js.map +1 -0
- package/dist/shared/hash-email.d.ts +11 -0
- package/dist/shared/hash-email.d.ts.map +1 -0
- package/dist/shared/hash-email.js +14 -0
- package/dist/shared/hash-email.js.map +1 -0
- package/dist/shared/kb-layout.d.ts +38 -0
- package/dist/shared/kb-layout.d.ts.map +1 -0
- package/dist/shared/kb-layout.js +102 -0
- package/dist/shared/kb-layout.js.map +1 -0
- package/dist/shared/kb-layout.test.d.ts +2 -0
- package/dist/shared/kb-layout.test.d.ts.map +1 -0
- package/dist/shared/kb-layout.test.js +67 -0
- package/dist/shared/kb-layout.test.js.map +1 -0
- package/dist/shared/ssrf.d.ts +20 -0
- package/dist/shared/ssrf.d.ts.map +1 -0
- package/dist/shared/ssrf.js +89 -0
- package/dist/shared/ssrf.js.map +1 -0
- package/dist/shared/token-crypto.d.ts +16 -0
- package/dist/shared/token-crypto.d.ts.map +1 -0
- package/dist/shared/token-crypto.js +49 -0
- package/dist/shared/token-crypto.js.map +1 -0
- package/dist/shared/ttl-cache.d.ts +16 -0
- package/dist/shared/ttl-cache.d.ts.map +1 -0
- package/dist/shared/ttl-cache.js +27 -0
- package/dist/shared/ttl-cache.js.map +1 -0
- package/dist/shared/utcp-namespace.d.ts +46 -0
- package/dist/shared/utcp-namespace.d.ts.map +1 -0
- package/dist/shared/utcp-namespace.js +82 -0
- package/dist/shared/utcp-namespace.js.map +1 -0
- package/dist/version.d.ts +17 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +19 -0
- package/dist/version.js.map +1 -0
- package/kb-template/.bevelignore +21 -0
- package/kb-template/Agents/.gitkeep +0 -0
- package/kb-template/Agents/README.md +38 -0
- package/kb-template/CLAUDE.md +419 -0
- package/kb-template/Data/.gitkeep +0 -0
- package/kb-template/Data/README.md +39 -0
- package/kb-template/KnowledgeBase/.gitkeep +0 -0
- package/kb-template/KnowledgeBase/README.md +37 -0
- package/kb-template/Pipelines/.gitkeep +0 -0
- package/kb-template/Pipelines/README.md +38 -0
- package/kb-template/Skills/.gitkeep +0 -0
- package/kb-template/Skills/README.md +32 -0
- package/kb-template/Tools/.gitkeep +0 -0
- package/kb-template/Tools/README.md +42 -0
- package/kb-template/access.md +36 -0
- package/migrations/0000_core_init.sql +243 -0
- package/migrations/meta/0000_snapshot.json +1480 -0
- package/migrations/meta/_journal.json +13 -0
- package/package.json +80 -0
- package/src/assets.ts +25 -0
- package/src/core/core-ports.ts +91 -0
- package/src/core/create-core-server.ts +338 -0
- package/src/core/create-core-services.ts +501 -0
- package/src/core-config.ts +257 -0
- package/src/index.ts +69 -0
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +98 -0
- package/src/modules/access/__tests__/access-control.read-batch.test.ts +132 -0
- package/src/modules/access/__tests__/access-control.service.test.ts +1011 -0
- package/src/modules/access/__tests__/access-mutation.service.test.ts +439 -0
- package/src/modules/access/__tests__/access-splice.test.ts +247 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +407 -0
- package/src/modules/access/__tests__/creator-access.test.ts +217 -0
- package/src/modules/access/__tests__/grant-sources.test.ts +204 -0
- package/src/modules/access/__tests__/kb-read-filter.test.ts +70 -0
- package/src/modules/access/__tests__/roles-admin.service.test.ts +469 -0
- package/src/modules/access/__tests__/roles-edit.test.ts +138 -0
- package/src/modules/access/__tests__/roles-yaml-guard.test.ts +70 -0
- package/src/modules/access/__tests__/roles.routes.test.ts +234 -0
- package/src/modules/access/access-control.interface.ts +383 -0
- package/src/modules/access/access-control.service.ts +1794 -0
- package/src/modules/access/access-errors.ts +60 -0
- package/src/modules/access/access-mutation.service.ts +369 -0
- package/src/modules/access/access-splice.ts +413 -0
- package/src/modules/access/access.routes.ts +894 -0
- package/src/modules/access/creator-access.ts +270 -0
- package/src/modules/access/kb-read-filter.ts +76 -0
- package/src/modules/access/roles-admin.service.ts +741 -0
- package/src/modules/access/roles-edit.ts +248 -0
- package/src/modules/access/roles-yaml-guard.ts +89 -0
- package/src/modules/admin/admin-access.routes.ts +29 -0
- package/src/modules/admin/admin-access.service.ts +38 -0
- package/src/modules/admin/admin.interface.ts +10 -0
- package/src/modules/auth/account-erasure.service.ts +183 -0
- package/src/modules/auth/auth.middleware.ts +78 -0
- package/src/modules/auth/auth.routes.ts +97 -0
- package/src/modules/auth/auth.service.ts +199 -0
- package/src/modules/code-mode/code-mode-names.ts +38 -0
- package/src/modules/code-mode/code-mode.tool.ts +117 -0
- package/src/modules/code-mode/index.ts +6 -0
- package/src/modules/database/connection.ts +15 -0
- package/src/modules/database/core-schema.ts +431 -0
- package/src/modules/database/migrate.ts +43 -0
- package/src/modules/database/schema.ts +11 -0
- package/src/modules/diff/__tests__/diff.routes.read-gate.test.ts +87 -0
- package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +150 -0
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +184 -0
- package/src/modules/diff/__tests__/diff.service.test.ts +202 -0
- package/src/modules/diff/diff-paths.ts +9 -0
- package/src/modules/diff/diff.config.ts +12 -0
- package/src/modules/diff/diff.interface.ts +78 -0
- package/src/modules/diff/diff.routes.ts +277 -0
- package/src/modules/diff/diff.service.ts +508 -0
- package/src/modules/diff/line-diff.ts +79 -0
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +287 -0
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +249 -0
- package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +253 -0
- package/src/modules/mcp/__tests__/mcp-session-store.test.ts +151 -0
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +150 -0
- package/src/modules/mcp/__tests__/mcp.routes.delete.test.ts +92 -0
- package/src/modules/mcp/__tests__/mcp.service.test.ts +414 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +126 -0
- package/src/modules/mcp/mcp-session-store.ts +162 -0
- package/src/modules/mcp/mcp.routes.ts +263 -0
- package/src/modules/mcp/mcp.service.ts +973 -0
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +401 -0
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +60 -0
- package/src/modules/mcp/oauth/oauth-state.ts +61 -0
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +277 -0
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +206 -0
- package/src/modules/secrets-vault/__tests__/mcp-oauth-discovery.service.test.ts +178 -0
- package/src/modules/secrets-vault/__tests__/scopes-covered.test.ts +101 -0
- package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +54 -0
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +143 -0
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +792 -0
- package/src/modules/secrets-vault/index.ts +25 -0
- package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +289 -0
- package/src/modules/secrets-vault/secrets-variable-loader.ts +104 -0
- package/src/modules/secrets-vault/secrets-vault.contract.ts +270 -0
- package/src/modules/secrets-vault/secrets-vault.routes.ts +526 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +176 -0
- package/src/modules/skills/index.ts +10 -0
- package/src/modules/skills/skills.contract.ts +54 -0
- package/src/modules/skills/skills.routes.ts +38 -0
- package/src/modules/skills/skills.service.ts +245 -0
- package/src/modules/skills/skills.tools.ts +121 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +358 -0
- package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +79 -0
- package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +88 -0
- package/src/modules/tool-auth/external-api-key.errors.ts +25 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +99 -0
- package/src/modules/tool-auth/external-api-key.service.ts +209 -0
- package/src/modules/tool-auth/internal-token.service.ts +124 -0
- package/src/modules/tool-auth/llm-usage-meter.ts +19 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +220 -0
- package/src/modules/tool-helpers/__tests__/phase0-loopback-spike.test.ts +129 -0
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +95 -0
- package/src/modules/tool-helpers/__tests__/tools-surface.test.ts +146 -0
- package/src/modules/tool-helpers/__tests__/validate-token.test.ts +75 -0
- package/src/modules/tool-helpers/index.ts +18 -0
- package/src/modules/tool-helpers/tool-context.ts +98 -0
- package/src/modules/tool-helpers/tool-def.ts +73 -0
- package/src/modules/tool-helpers/tool-handler.ts +91 -0
- package/src/modules/tool-helpers/tool.contract.ts +85 -0
- package/src/modules/tool-helpers/validate-token.ts +50 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +200 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +499 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +156 -0
- package/src/modules/tool-manuals/index.ts +12 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +183 -0
- package/src/modules/tool-manuals/tool-manuals.routes.ts +110 -0
- package/src/modules/tool-manuals/tool-manuals.service.ts +781 -0
- package/src/modules/tool-manuals/tool-manuals.tools.ts +202 -0
- package/src/modules/tool-registry/__tests__/tool-registry.test.ts +64 -0
- package/src/modules/tool-registry/manual.routes.ts +87 -0
- package/src/modules/tool-registry/tool-registry.ts +55 -0
- package/src/modules/tool-registry/tool.contract.ts +52 -0
- package/src/modules/workflow/__tests__/event-bus.test.ts +322 -0
- package/src/modules/workflow/__tests__/file-change-notifier.test.ts +51 -0
- package/src/modules/workflow/__tests__/format-affected-owners.test.ts +67 -0
- package/src/modules/workflow/__tests__/locking-filesystem.test.ts +445 -0
- package/src/modules/workflow/__tests__/pending-commits.canonical.test.ts +36 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +243 -0
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +308 -0
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +67 -0
- package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +62 -0
- package/src/modules/workflow/__tests__/session-ontology.service.test.ts +200 -0
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +232 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +380 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +340 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +192 -0
- package/src/modules/workflow/agent-tools/workflow.tools.ts +502 -0
- package/src/modules/workflow/event-bus.ts +262 -0
- package/src/modules/workflow/events.routes.ts +266 -0
- package/src/modules/workflow/file-change-notifier.ts +54 -0
- package/src/modules/workflow/file-lock.service.ts +269 -0
- package/src/modules/workflow/git/__tests__/branch-name.test.ts +86 -0
- package/src/modules/workflow/git/__tests__/branchAuthor.test.ts +80 -0
- package/src/modules/workflow/git/__tests__/clone-config.test.ts +15 -0
- package/src/modules/workflow/git/__tests__/git-test-helpers.ts +47 -0
- package/src/modules/workflow/git/__tests__/git-version.failure.test.ts +24 -0
- package/src/modules/workflow/git/__tests__/git-version.test.ts +49 -0
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +389 -0
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +124 -0
- package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +105 -0
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +211 -0
- package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +154 -0
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +320 -0
- package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +202 -0
- package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +453 -0
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +198 -0
- package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +125 -0
- package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +219 -0
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +145 -0
- package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +240 -0
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +272 -0
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +232 -0
- package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +101 -0
- package/src/modules/workflow/git/__tests__/git.service.status.test.ts +127 -0
- package/src/modules/workflow/git/__tests__/mutex.test.ts +78 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +225 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +101 -0
- package/src/modules/workflow/git/branch-name.ts +70 -0
- package/src/modules/workflow/git/clone-config.ts +71 -0
- package/src/modules/workflow/git/git-version.ts +70 -0
- package/src/modules/workflow/git/git.service.ts +2248 -0
- package/src/modules/workflow/git/mutex.ts +22 -0
- package/src/modules/workflow/git/pull-request.service.ts +447 -0
- package/src/modules/workflow/locking-filesystem.ts +489 -0
- package/src/modules/workflow/pending-commits.service.ts +444 -0
- package/src/modules/workflow/pending-commits.worker.ts +361 -0
- package/src/modules/workflow/read-only-filesystem.ts +54 -0
- package/src/modules/workflow/recovery-bot.ts +73 -0
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +560 -0
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +176 -0
- package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +258 -0
- package/src/modules/workflow/review-workflow/review-workflow.interface.ts +199 -0
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +806 -0
- package/src/modules/workflow/sanitize-error.ts +62 -0
- package/src/modules/workflow/session-ontology.policy.ts +70 -0
- package/src/modules/workflow/session-ontology.service.ts +178 -0
- package/src/modules/workflow/workflow-hooks.ts +101 -0
- package/src/modules/workflow/workflow.errors.ts +285 -0
- package/src/modules/workflow/workflow.routes.ts +858 -0
- package/src/modules/workflow/workflow.service.ts +1555 -0
- package/src/modules/workspace/__tests__/kb-seed.service.test.ts +213 -0
- package/src/modules/workspace/__tests__/routine-write-policy.test.ts +68 -0
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +237 -0
- package/src/modules/workspace/__tests__/spill-store.test.ts +43 -0
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +233 -0
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +176 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +303 -0
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +276 -0
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +120 -0
- package/src/modules/workspace/__tests__/workspace.service.test.ts +565 -0
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +504 -0
- package/src/modules/workspace/bevel-ignore.ts +62 -0
- package/src/modules/workspace/kb-seed.interface.ts +36 -0
- package/src/modules/workspace/kb-seed.service.ts +361 -0
- package/src/modules/workspace/routine-write-policy.ts +79 -0
- package/src/modules/workspace/session-ontology.gate.ts +189 -0
- package/src/modules/workspace/session-sink.ts +25 -0
- package/src/modules/workspace/spill-store.ts +87 -0
- package/src/modules/workspace/workspace.routes.ts +1114 -0
- package/src/modules/workspace/workspace.service.ts +1343 -0
- package/src/modules/workspace/workspace.tools.ts +885 -0
- package/src/shared/__tests__/frontmatter-id.test.ts +88 -0
- package/src/shared/__tests__/fs-walk.test.ts +42 -0
- package/src/shared/__tests__/ssrf.test.ts +53 -0
- package/src/shared/__tests__/utcp-namespace.test.ts +19 -0
- package/src/shared/frontmatter-id.ts +72 -0
- package/src/shared/fs-walk.ts +30 -0
- package/src/shared/hash-email.ts +14 -0
- package/src/shared/kb-layout.test.ts +88 -0
- package/src/shared/kb-layout.ts +101 -0
- package/src/shared/ssrf.ts +78 -0
- package/src/shared/token-crypto.ts +54 -0
- package/src/shared/ttl-cache.ts +27 -0
- package/src/shared/utcp-namespace.ts +88 -0
- package/src/version.ts +19 -0
|
@@ -0,0 +1,303 @@
|
|
|
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
|
+
import type { IWorkflowService } from '@bevel-software/platform-shared';
|
|
6
|
+
import type { IAccessControl } from '../../access/access-control.interface.js';
|
|
7
|
+
import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
8
|
+
import type { AuthService } from '../../auth/auth.service.js';
|
|
9
|
+
import { AccessConfigError } from '../../access/access-errors.js';
|
|
10
|
+
import { createWorkspaceRoutes } from '../workspace.routes.js';
|
|
11
|
+
import type { ICreatorAccess } from '../../access/creator-access.js';
|
|
12
|
+
import { FolderTooLargeError, type WorkspaceService } from '../workspace.service.js';
|
|
13
|
+
|
|
14
|
+
const stubCreatorAccess: ICreatorAccess = {
|
|
15
|
+
planForCreate: async () => null,
|
|
16
|
+
grantInExtractedFile: async () => null,
|
|
17
|
+
noteAccessFileWritten: () => {},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Contract test for the `?download=1` flavours of the workspace routes:
|
|
22
|
+
*
|
|
23
|
+
* - GET /workspace/:id/file/raw?download=1 (single-file save)
|
|
24
|
+
* - GET /workspace/:id/folder/zip?download=1 (folder → zip save)
|
|
25
|
+
*
|
|
26
|
+
* Both are gated on the dedicated `Download` role in roles.yaml. Admin
|
|
27
|
+
* status is fully orthogonal — being in the `Admin` role does NOT
|
|
28
|
+
* implicitly grant Download; users must be listed under the `Download`
|
|
29
|
+
* role to receive bytes. The inline branch of /file/raw (no flag) stays
|
|
30
|
+
* open to every authenticated user so the PdfRenderer and image renderers
|
|
31
|
+
* keep working for everyone.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const USER_ID = 'user-1';
|
|
35
|
+
const USER = { id: USER_ID, email: 'alice@example.com', name: 'Alice' };
|
|
36
|
+
const WORKSPACE_ID = 'target-company-state';
|
|
37
|
+
const FILE_BYTES = Buffer.from('hello world');
|
|
38
|
+
const ZIP_BYTES = Buffer.from('PK\x03\x04 fake-zip-bytes');
|
|
39
|
+
|
|
40
|
+
interface Harness {
|
|
41
|
+
server: Server;
|
|
42
|
+
baseUrl: string;
|
|
43
|
+
canDownload: ReturnType<typeof vi.fn>;
|
|
44
|
+
canWrite: ReturnType<typeof vi.fn>;
|
|
45
|
+
createFolderZip: ReturnType<typeof vi.fn>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function makeHarness(opts: {
|
|
49
|
+
canDownload: boolean;
|
|
50
|
+
folderZip?: () => Promise<Buffer> | Buffer;
|
|
51
|
+
/** When set, canDownload rejects with this error instead of returning a bool. */
|
|
52
|
+
canDownloadError?: Error;
|
|
53
|
+
}): Promise<Harness> {
|
|
54
|
+
const canDownload = vi.fn(async () => {
|
|
55
|
+
if (opts.canDownloadError) throw opts.canDownloadError;
|
|
56
|
+
return opts.canDownload;
|
|
57
|
+
});
|
|
58
|
+
const canWrite = vi.fn();
|
|
59
|
+
const accessControl: IAccessControl = {
|
|
60
|
+
canWrite,
|
|
61
|
+
canWriteBatch: vi.fn(),
|
|
62
|
+
// The /file/raw route now read-gates before the download gate; these tests
|
|
63
|
+
// exercise the download verb, so reads pass.
|
|
64
|
+
canRead: vi.fn(async () => true),
|
|
65
|
+
canReadBatch: vi.fn(async (_w: string, _e: string, paths: string[]) => new Map(paths.map((p) => [p, true]))),
|
|
66
|
+
canDownload,
|
|
67
|
+
eligibleWriters: vi.fn(),
|
|
68
|
+
eligibleWriterEmails: vi.fn(),
|
|
69
|
+
invalidate: vi.fn(),
|
|
70
|
+
findEmailByHash: vi.fn(),
|
|
71
|
+
canWriteAtRef: vi.fn(),
|
|
72
|
+
canWriteBatchAtRef: vi.fn(),
|
|
73
|
+
eligibleWritersAtRef: vi.fn(),
|
|
74
|
+
eligibleWritersForPathsAtRef: vi.fn(),
|
|
75
|
+
} as unknown as IAccessControl;
|
|
76
|
+
|
|
77
|
+
const createFolderZip = vi.fn(async () => {
|
|
78
|
+
if (opts.folderZip) return opts.folderZip();
|
|
79
|
+
return ZIP_BYTES;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Partial<WorkspaceService> + final cast keeps the contract typed at the
|
|
83
|
+
// call site (createWorkspaceRoutes wants a full WorkspaceService) while
|
|
84
|
+
// letting us stub only the two methods these tests exercise.
|
|
85
|
+
const workspaceServiceMock: Partial<WorkspaceService> = {
|
|
86
|
+
readFileBinary: vi.fn(async () => FILE_BYTES),
|
|
87
|
+
createFolderZip,
|
|
88
|
+
};
|
|
89
|
+
const workspaceService = workspaceServiceMock as WorkspaceService;
|
|
90
|
+
|
|
91
|
+
const authServiceMock: Partial<AuthService> = {
|
|
92
|
+
getUserById: vi.fn(async () => USER),
|
|
93
|
+
};
|
|
94
|
+
const authService = authServiceMock as AuthService;
|
|
95
|
+
|
|
96
|
+
const workflowService = {} as unknown as IWorkflowService;
|
|
97
|
+
const eventBus = {} as unknown as WorkflowEventBus;
|
|
98
|
+
|
|
99
|
+
const app = express();
|
|
100
|
+
app.use(express.json());
|
|
101
|
+
app.use('/api', (req, _res, next) => {
|
|
102
|
+
(req as any).userId = USER_ID;
|
|
103
|
+
next();
|
|
104
|
+
});
|
|
105
|
+
app.use('/api', createWorkspaceRoutes(
|
|
106
|
+
workspaceService,
|
|
107
|
+
authService,
|
|
108
|
+
workflowService,
|
|
109
|
+
eventBus,
|
|
110
|
+
accessControl,
|
|
111
|
+
'knowledge-base',
|
|
112
|
+
stubCreatorAccess,
|
|
113
|
+
));
|
|
114
|
+
|
|
115
|
+
const server = await new Promise<Server>((resolve) => {
|
|
116
|
+
const s = app.listen(0, () => resolve(s));
|
|
117
|
+
});
|
|
118
|
+
// `server.address()` returns `AddressInfo | string | null`. The string
|
|
119
|
+
// branch is for Unix-domain sockets and the null branch for an
|
|
120
|
+
// unbound listener — neither can happen here (we awaited `listen(0,
|
|
121
|
+
// ...)` on a TCP port), so a narrow + throw is sufficient and avoids
|
|
122
|
+
// the `as any` lint.
|
|
123
|
+
const addr = server.address();
|
|
124
|
+
if (addr === null || typeof addr === 'string') {
|
|
125
|
+
throw new Error(`Unexpected server.address() shape: ${JSON.stringify(addr)}`);
|
|
126
|
+
}
|
|
127
|
+
const port = (addr as AddressInfo).port;
|
|
128
|
+
return { server, baseUrl: `http://127.0.0.1:${port}`, canDownload, canWrite, createFolderZip };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function closeServer(server: Server): Promise<void> {
|
|
132
|
+
await new Promise<void>((resolve, reject) =>
|
|
133
|
+
server.close((err) => (err ? reject(err) : resolve())),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
describe('GET /workspace/:id/file/raw — ?download=1 gated on Download role', () => {
|
|
138
|
+
let h: Harness | null = null;
|
|
139
|
+
afterEach(async () => { if (h) await closeServer(h.server); h = null; });
|
|
140
|
+
|
|
141
|
+
it('user with Download role gets 200 + attachment disposition + bytes', async () => {
|
|
142
|
+
h = await makeHarness({ canDownload: true });
|
|
143
|
+
const res = await fetch(
|
|
144
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
|
|
145
|
+
);
|
|
146
|
+
expect(res.status).toBe(200);
|
|
147
|
+
const dispo = res.headers.get('content-disposition');
|
|
148
|
+
expect(dispo).toBeTruthy();
|
|
149
|
+
expect(dispo).toContain('attachment');
|
|
150
|
+
expect(dispo).toContain(`filename*=UTF-8''${encodeURIComponent('Foo.md')}`);
|
|
151
|
+
expect(res.headers.get('x-content-type-options')).toBe('nosniff');
|
|
152
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
153
|
+
expect(buf.equals(FILE_BYTES)).toBe(true);
|
|
154
|
+
// canDownload is path-scoped now; the route strips the kbDirName prefix
|
|
155
|
+
// before consulting the access service (mirrors useFileAccess on the
|
|
156
|
+
// frontend), so the resolver sees the repo-relative path.
|
|
157
|
+
expect(h.canDownload).toHaveBeenCalledWith(WORKSPACE_ID, USER.email, 'Knowledge/Foo.md');
|
|
158
|
+
// canWrite must NOT be consulted by the download gate — the two verbs
|
|
159
|
+
// are independent.
|
|
160
|
+
expect(h.canWrite).not.toHaveBeenCalled();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('user without Download role gets 403 with no body bytes', async () => {
|
|
164
|
+
h = await makeHarness({ canDownload: false });
|
|
165
|
+
const res = await fetch(
|
|
166
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
|
|
167
|
+
);
|
|
168
|
+
expect(res.status).toBe(403);
|
|
169
|
+
const body = (await res.json()) as { error: string };
|
|
170
|
+
expect(body.error).toBe('Download permission required');
|
|
171
|
+
expect(res.headers.get('content-disposition')).toBeNull();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('inline view (no ?download flag) returns 200 for any authed user without consulting access-control', async () => {
|
|
175
|
+
// Even a user with no download permission must be able to view PDFs /
|
|
176
|
+
// images inline — that's the PdfRenderer happy path. The route must not
|
|
177
|
+
// call canDownload at all on this branch.
|
|
178
|
+
h = await makeHarness({ canDownload: false });
|
|
179
|
+
const res = await fetch(
|
|
180
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}`,
|
|
181
|
+
);
|
|
182
|
+
expect(res.status).toBe(200);
|
|
183
|
+
expect(res.headers.get('content-disposition')).toBeNull();
|
|
184
|
+
expect(h.canDownload).not.toHaveBeenCalled();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('returns 500 with AccessConfigError payload when roles.yaml / access.md fails to load', async () => {
|
|
188
|
+
// Regression guard: if `canDownload` throws inside requireDownloadPermission
|
|
189
|
+
// (e.g. roles.yaml is missing or the access tree is unrecoverably
|
|
190
|
+
// malformed) and we don't catch it, the async rejection bubbles past
|
|
191
|
+
// Express and the request hangs (or 500s without the helpful
|
|
192
|
+
// config-error payload). The route should surface the
|
|
193
|
+
// WorkflowDomainError shape — same JSON the per-path /access endpoint
|
|
194
|
+
// returns when it hits the same exception — so the operator can see
|
|
195
|
+
// which access.md file is broken.
|
|
196
|
+
const configErr = new AccessConfigError([
|
|
197
|
+
"Knowledge/Sales/access.md: unknown role 'Product Manager'",
|
|
198
|
+
]);
|
|
199
|
+
h = await makeHarness({ canDownload: true, canDownloadError: configErr });
|
|
200
|
+
const res = await fetch(
|
|
201
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
|
|
202
|
+
);
|
|
203
|
+
expect(res.status).toBe(500);
|
|
204
|
+
const body = (await res.json()) as { error: string; accessConfigErrors?: string[] };
|
|
205
|
+
expect(body.error).toContain('Access-control config is invalid');
|
|
206
|
+
expect(body.accessConfigErrors?.[0]).toContain("unknown role 'Product Manager'");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('forces SVG to octet-stream when downloading (anti-XSS), preserves image/svg+xml inline', async () => {
|
|
210
|
+
h = await makeHarness({ canDownload: true });
|
|
211
|
+
const dl = await fetch(
|
|
212
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('icon.svg')}&download=1`,
|
|
213
|
+
);
|
|
214
|
+
expect(dl.headers.get('content-type')).toBe('application/octet-stream');
|
|
215
|
+
|
|
216
|
+
await closeServer(h.server);
|
|
217
|
+
h = await makeHarness({ canDownload: false });
|
|
218
|
+
const inline = await fetch(
|
|
219
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/file/raw?path=${encodeURIComponent('icon.svg')}`,
|
|
220
|
+
);
|
|
221
|
+
expect(inline.headers.get('content-type')).toBe('image/svg+xml');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('GET /workspace/:id/folder/zip — gated on Download role', () => {
|
|
226
|
+
let h: Harness | null = null;
|
|
227
|
+
afterEach(async () => { if (h) await closeServer(h.server); h = null; });
|
|
228
|
+
|
|
229
|
+
it('user with Download role gets a 200 zip with attachment disposition', async () => {
|
|
230
|
+
h = await makeHarness({ canDownload: true });
|
|
231
|
+
const res = await fetch(
|
|
232
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Sales')}&download=1`,
|
|
233
|
+
);
|
|
234
|
+
expect(res.status).toBe(200);
|
|
235
|
+
expect(res.headers.get('content-type')).toBe('application/zip');
|
|
236
|
+
expect(res.headers.get('x-content-type-options')).toBe('nosniff');
|
|
237
|
+
const dispo = res.headers.get('content-disposition');
|
|
238
|
+
expect(dispo).toBeTruthy();
|
|
239
|
+
expect(dispo).toContain('attachment');
|
|
240
|
+
expect(dispo).toContain(`filename*=UTF-8''${encodeURIComponent('Sales.zip')}`);
|
|
241
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
242
|
+
expect(buf.equals(ZIP_BYTES)).toBe(true);
|
|
243
|
+
expect(h.canDownload).toHaveBeenCalledWith(WORKSPACE_ID, USER.email, 'Knowledge/Sales');
|
|
244
|
+
expect(h.createFolderZip).toHaveBeenCalledWith(WORKSPACE_ID, 'Knowledge/Sales');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('user without Download role gets 403 and no zip is built', async () => {
|
|
248
|
+
h = await makeHarness({ canDownload: false });
|
|
249
|
+
const res = await fetch(
|
|
250
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Sales')}&download=1`,
|
|
251
|
+
);
|
|
252
|
+
expect(res.status).toBe(403);
|
|
253
|
+
const body = (await res.json()) as { error: string };
|
|
254
|
+
expect(body.error).toBe('Download permission required');
|
|
255
|
+
expect(h.createFolderZip).not.toHaveBeenCalled();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('requires ?download=1 — a stray inline call is rejected with 400', async () => {
|
|
259
|
+
// Folder zip is download-only; refuse the inline shape so the URL contract
|
|
260
|
+
// matches the file route and there's no ambiguity about what should happen.
|
|
261
|
+
h = await makeHarness({ canDownload: true });
|
|
262
|
+
const res = await fetch(
|
|
263
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Sales')}`,
|
|
264
|
+
);
|
|
265
|
+
expect(res.status).toBe(400);
|
|
266
|
+
expect(h.createFolderZip).not.toHaveBeenCalled();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('maps FolderTooLargeError to 413 with the limit-bearing message', async () => {
|
|
270
|
+
h = await makeHarness({
|
|
271
|
+
canDownload: true,
|
|
272
|
+
folderZip: async () => { throw new FolderTooLargeError(500 * 1024 * 1024); },
|
|
273
|
+
});
|
|
274
|
+
const res = await fetch(
|
|
275
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Big')}&download=1`,
|
|
276
|
+
);
|
|
277
|
+
expect(res.status).toBe(413);
|
|
278
|
+
const body = (await res.json()) as { error: string };
|
|
279
|
+
expect(body.error).toContain('524288000');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('maps Path traversal detected to 403', async () => {
|
|
283
|
+
h = await makeHarness({
|
|
284
|
+
canDownload: true,
|
|
285
|
+
folderZip: async () => { throw new Error('Path traversal detected'); },
|
|
286
|
+
});
|
|
287
|
+
const res = await fetch(
|
|
288
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('../escape')}&download=1`,
|
|
289
|
+
);
|
|
290
|
+
expect(res.status).toBe(403);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('maps Not a directory to 400 (user pointed at a file)', async () => {
|
|
294
|
+
h = await makeHarness({
|
|
295
|
+
canDownload: true,
|
|
296
|
+
folderZip: async () => { throw new Error('Not a directory'); },
|
|
297
|
+
});
|
|
298
|
+
const res = await fetch(
|
|
299
|
+
`${h.baseUrl}/api/workspace/${WORKSPACE_ID}/folder/zip?path=${encodeURIComponent('Knowledge/Foo.md')}&download=1`,
|
|
300
|
+
);
|
|
301
|
+
expect(res.status).toBe(400);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
@@ -0,0 +1,276 @@
|
|
|
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
|
+
import type { IWorkflowService } from '@bevel-software/platform-shared';
|
|
6
|
+
import type { IAccessControl } from '../../access/access-control.interface.js';
|
|
7
|
+
import type { ICreatorAccess } from '../../access/creator-access.js';
|
|
8
|
+
import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
9
|
+
import type { AuthService } from '../../auth/auth.service.js';
|
|
10
|
+
import { createWorkspaceRoutes } from '../workspace.routes.js';
|
|
11
|
+
import type { WorkspaceService, ReadTreeFilter } from '../workspace.service.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Contract test for the read-permission gates on the human read routes:
|
|
15
|
+
* GET /file, /file/raw (inline), /resolve-id, /kb-files, /files.
|
|
16
|
+
* `allow(path)` decides each verdict; by convention any path containing
|
|
17
|
+
* "Secret" is denied. The harness wires the real route layer over mocked
|
|
18
|
+
* access + workspace services. (The /graph + /resolve-id routes live in the
|
|
19
|
+
* enterprise kb-graph module; their gate tests moved with them.)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const USER_ID = 'user-1';
|
|
23
|
+
const USER = { id: USER_ID, email: 'alice@example.com', name: 'Alice' };
|
|
24
|
+
const WS = 'target-company-state';
|
|
25
|
+
const KB = 'knowledge-base';
|
|
26
|
+
|
|
27
|
+
const allow = (p: string) => !p.includes('Secret');
|
|
28
|
+
|
|
29
|
+
const stubCreatorAccess: ICreatorAccess = {
|
|
30
|
+
planForCreate: async () => null,
|
|
31
|
+
grantInExtractedFile: async () => null,
|
|
32
|
+
noteAccessFileWritten: () => {},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
interface Harness {
|
|
37
|
+
server: Server;
|
|
38
|
+
baseUrl: string;
|
|
39
|
+
canRead: ReturnType<typeof vi.fn>;
|
|
40
|
+
canReadBatchFull: ReturnType<typeof vi.fn>;
|
|
41
|
+
listFilesFilter: () => ReadTreeFilter | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function makeHarness(opts: {
|
|
45
|
+
kbFiles?: Record<string, string>;
|
|
46
|
+
allowFn?: (p: string) => boolean;
|
|
47
|
+
} = {}): Promise<Harness> {
|
|
48
|
+
const allowP = opts.allowFn ?? allow;
|
|
49
|
+
const canRead = vi.fn(async (_w: string, _e: string, p: string) => allowP(p));
|
|
50
|
+
const batch = async (_w: string, _e: string, paths: string[]) =>
|
|
51
|
+
new Map(paths.map((p) => [p, allowP(p)]));
|
|
52
|
+
const canReadBatchFull = vi.fn(batch);
|
|
53
|
+
const accessControl = {
|
|
54
|
+
canRead,
|
|
55
|
+
canReadBatch: canReadBatchFull,
|
|
56
|
+
} as unknown as IAccessControl;
|
|
57
|
+
|
|
58
|
+
let captured: ReadTreeFilter | undefined;
|
|
59
|
+
const workspaceService = {
|
|
60
|
+
getOrCreateForBranch: vi.fn(async (branch: string) => ({
|
|
61
|
+
id: branch,
|
|
62
|
+
name: branch,
|
|
63
|
+
absolutePath: `/tmp/${branch}`,
|
|
64
|
+
createdAt: new Date(0).toISOString(),
|
|
65
|
+
kbDirName: KB,
|
|
66
|
+
})),
|
|
67
|
+
readFile: vi.fn(async () => 'CONTENT'),
|
|
68
|
+
readFileBinary: vi.fn(async () => Buffer.from('BYTES')),
|
|
69
|
+
readAllKbFiles: vi.fn(async () => opts.kbFiles ?? {}),
|
|
70
|
+
listFiles: vi.fn(async (_id: string, filter?: ReadTreeFilter) => {
|
|
71
|
+
captured = filter;
|
|
72
|
+
return { name: 'root', relativePath: '.', type: 'directory', children: [] };
|
|
73
|
+
}),
|
|
74
|
+
} as unknown as WorkspaceService;
|
|
75
|
+
|
|
76
|
+
const authService = { getUserById: vi.fn(async () => USER) } as unknown as AuthService;
|
|
77
|
+
|
|
78
|
+
const app = express();
|
|
79
|
+
app.use(express.json());
|
|
80
|
+
app.use('/api', (req, _res, next) => {
|
|
81
|
+
(req as unknown as { userId: string }).userId = USER_ID;
|
|
82
|
+
next();
|
|
83
|
+
});
|
|
84
|
+
app.use(
|
|
85
|
+
'/api',
|
|
86
|
+
createWorkspaceRoutes(
|
|
87
|
+
workspaceService,
|
|
88
|
+
authService,
|
|
89
|
+
{} as unknown as IWorkflowService,
|
|
90
|
+
{} as unknown as WorkflowEventBus,
|
|
91
|
+
accessControl,
|
|
92
|
+
KB,
|
|
93
|
+
stubCreatorAccess,
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
const server = await new Promise<Server>((resolve) => {
|
|
97
|
+
const s = app.listen(0, () => resolve(s));
|
|
98
|
+
});
|
|
99
|
+
const addr = server.address() as AddressInfo;
|
|
100
|
+
return {
|
|
101
|
+
server,
|
|
102
|
+
baseUrl: `http://127.0.0.1:${addr.port}`,
|
|
103
|
+
canRead,
|
|
104
|
+
canReadBatchFull,
|
|
105
|
+
listFilesFilter: () => captured,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function close(s: Server): Promise<void> {
|
|
110
|
+
return new Promise((resolve, reject) => s.close((e) => (e ? reject(e) : resolve())));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
describe('read-permission gates on human read routes', () => {
|
|
114
|
+
let h: Harness | null = null;
|
|
115
|
+
afterEach(async () => { if (h) await close(h.server); h = null; });
|
|
116
|
+
|
|
117
|
+
const get = (path: string) => fetch(`${h!.baseUrl}/api/workspace/${WS}${path}`);
|
|
118
|
+
|
|
119
|
+
it('GET /file: readable → 200, denied → 403', async () => {
|
|
120
|
+
h = await makeHarness();
|
|
121
|
+
// Paths are workspace-relative (kbDir-prefixed), as the file tree produces
|
|
122
|
+
// and `readFile` resolves them.
|
|
123
|
+
const ok = await get(`/file?path=${encodeURIComponent(`${KB}/Knowledge/Open/a.md`)}`);
|
|
124
|
+
expect(ok.status).toBe(200);
|
|
125
|
+
expect((await ok.json()).content).toBe('CONTENT');
|
|
126
|
+
|
|
127
|
+
const denied = await get(`/file?path=${encodeURIComponent(`${KB}/Knowledge/Secret/x.md`)}`);
|
|
128
|
+
expect(denied.status).toBe(403);
|
|
129
|
+
// canRead consulted with the KB-repo-relative path (kbDir prefix stripped).
|
|
130
|
+
expect(h.canRead).toHaveBeenCalledWith(WS, USER.email, 'Knowledge/Secret/x.md');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('GET /file: a non-KB path (reserved workspace file) is not read-gated', async () => {
|
|
134
|
+
h = await makeHarness();
|
|
135
|
+
// Outside `kbDirName` → carries no read rules → served without a canRead
|
|
136
|
+
// check (matches the agent tools and diff routes). Previously this 403'd
|
|
137
|
+
// because the full path was passed to default-deny canRead.
|
|
138
|
+
const res = await get(`/file?path=${encodeURIComponent('reserved-config.json')}`);
|
|
139
|
+
expect(res.status).toBe(200);
|
|
140
|
+
expect(h.canRead).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('GET /file/raw (inline, no download) is read-gated', async () => {
|
|
144
|
+
h = await makeHarness();
|
|
145
|
+
const denied = await get(`/file/raw?path=${encodeURIComponent(`${KB}/Secret/x.png`)}`);
|
|
146
|
+
expect(denied.status).toBe(403);
|
|
147
|
+
const ok = await get(`/file/raw?path=${encodeURIComponent(`${KB}/Open/a.png`)}`);
|
|
148
|
+
expect(ok.status).toBe(200);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// NOTE (core split): the `GET /resolve-id` and `GET /graph` read-gate tests
|
|
152
|
+
// moved with the kb-graph module (enterprise) — those routes are not part of
|
|
153
|
+
// the core workspace surface.
|
|
154
|
+
|
|
155
|
+
it('GET /kb-files drops unreadable nodes from the graph payload', async () => {
|
|
156
|
+
h = await makeHarness({ kbFiles: { 'Knowledge/Open/a.md': 'A', 'Knowledge/Secret/x.md': 'S' } });
|
|
157
|
+
const res = await get('/kb-files');
|
|
158
|
+
expect(res.status).toBe(200);
|
|
159
|
+
const { files } = await res.json();
|
|
160
|
+
expect(files['Knowledge/Open/a.md']).toBe('A');
|
|
161
|
+
expect(files['Knowledge/Secret/x.md']).toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('GET /files passes a working shallow read filter to listFiles', async () => {
|
|
165
|
+
h = await makeHarness();
|
|
166
|
+
expect((await get('/files')).status).toBe(200);
|
|
167
|
+
const filter = h.listFilesFilter();
|
|
168
|
+
expect(typeof filter).toBe('function');
|
|
169
|
+
// The filter maps workspace-relative paths (kbDir-prefixed) → verdicts,
|
|
170
|
+
// stripping the kbDir and consulting the full canReadBatch.
|
|
171
|
+
const verdict = await filter!([
|
|
172
|
+
`${KB}/Knowledge/Open/a.md`,
|
|
173
|
+
`${KB}/Knowledge/Secret/x.md`,
|
|
174
|
+
]);
|
|
175
|
+
expect(verdict.get(`${KB}/Knowledge/Open/a.md`)).toBe(true);
|
|
176
|
+
expect(verdict.get(`${KB}/Knowledge/Secret/x.md`)).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('keeps the structural root folders visible even when nothing is readable', async () => {
|
|
180
|
+
// A user with no read grants: the access batch denies everything. The
|
|
181
|
+
// structural top-level folders (KnowledgeBase, Data, Agents, Pipelines,
|
|
182
|
+
// Skills, Tools) must still resolve readable (so the explorer renders its
|
|
183
|
+
// section view), while their contents stay gated.
|
|
184
|
+
h = await makeHarness({ allowFn: () => false });
|
|
185
|
+
expect((await get('/files')).status).toBe(200);
|
|
186
|
+
const filter = h.listFilesFilter();
|
|
187
|
+
const verdict = await filter!([
|
|
188
|
+
`${KB}/KnowledgeBase`,
|
|
189
|
+
`${KB}/Data`,
|
|
190
|
+
`${KB}/Agents`,
|
|
191
|
+
`${KB}/Pipelines`,
|
|
192
|
+
`${KB}/Skills`,
|
|
193
|
+
`${KB}/Tools`,
|
|
194
|
+
`${KB}/KnowledgeBase/Open/a.md`,
|
|
195
|
+
`${KB}/Data/Ops/Knowledge/item.md`,
|
|
196
|
+
`${KB}/Skills/rfi/SKILL.md`,
|
|
197
|
+
]);
|
|
198
|
+
// Folders themselves: forced visible.
|
|
199
|
+
expect(verdict.get(`${KB}/KnowledgeBase`)).toBe(true);
|
|
200
|
+
expect(verdict.get(`${KB}/Data`)).toBe(true);
|
|
201
|
+
expect(verdict.get(`${KB}/Agents`)).toBe(true);
|
|
202
|
+
expect(verdict.get(`${KB}/Pipelines`)).toBe(true);
|
|
203
|
+
expect(verdict.get(`${KB}/Skills`)).toBe(true);
|
|
204
|
+
expect(verdict.get(`${KB}/Tools`)).toBe(true);
|
|
205
|
+
// Their contents: still gated (the override matches only the exact folder).
|
|
206
|
+
expect(verdict.get(`${KB}/KnowledgeBase/Open/a.md`)).toBe(false);
|
|
207
|
+
expect(verdict.get(`${KB}/Data/Ops/Knowledge/item.md`)).toBe(false);
|
|
208
|
+
expect(verdict.get(`${KB}/Skills/rfi/SKILL.md`)).toBe(false);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('resolves every entry with the FULL read batch (frontmatter grants AND denies honoured)', async () => {
|
|
212
|
+
// The tree filter consults the full canReadBatch for ALL paths in one
|
|
213
|
+
// call — a per-file frontmatter grant (e.g. the creator read grant a
|
|
214
|
+
// loose root-level .md gets on creation) makes the file visible, and a
|
|
215
|
+
// frontmatter deny hides it, exactly matching what the content route
|
|
216
|
+
// will enforce on open.
|
|
217
|
+
h = await makeHarness({ allowFn: () => false });
|
|
218
|
+
const full = h.canReadBatchFull;
|
|
219
|
+
full.mockImplementation(async (_w: string, _e: string, paths: string[]) =>
|
|
220
|
+
new Map(paths.map((p) => [p, p === 'KnowledgeBase/mine.md'])));
|
|
221
|
+
expect((await get('/files')).status).toBe(200);
|
|
222
|
+
const filter = h.listFilesFilter();
|
|
223
|
+
const verdict = await filter!([
|
|
224
|
+
`${KB}/KnowledgeBase/mine.md`, // frontmatter grant → visible
|
|
225
|
+
`${KB}/KnowledgeBase/other.md`, // no grant → hidden
|
|
226
|
+
`${KB}/KnowledgeBase/Secret/deep.md`, // no grant → hidden
|
|
227
|
+
]);
|
|
228
|
+
expect(verdict.get(`${KB}/KnowledgeBase/mine.md`)).toBe(true);
|
|
229
|
+
expect(verdict.get(`${KB}/KnowledgeBase/other.md`)).toBe(false);
|
|
230
|
+
expect(verdict.get(`${KB}/KnowledgeBase/Secret/deep.md`)).toBe(false);
|
|
231
|
+
// ONE batched call covers every path — no per-file fan-out.
|
|
232
|
+
expect(full).toHaveBeenCalledTimes(1);
|
|
233
|
+
expect(full.mock.calls[0][2]).toEqual([
|
|
234
|
+
'KnowledgeBase/mine.md',
|
|
235
|
+
'KnowledgeBase/other.md',
|
|
236
|
+
'KnowledgeBase/Secret/deep.md',
|
|
237
|
+
]);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('legacy layout: root-level Knowledge/ is forced visible and its loose files get the full check', async () => {
|
|
241
|
+
// Clones that predate the KnowledgeBase/Skills split keep their knowledge
|
|
242
|
+
// root at `Knowledge/` (repo root). It must get the same structural-root
|
|
243
|
+
// treatment: the folder itself stays visible, and a loose file directly
|
|
244
|
+
// under it (whose creator grant lives in frontmatter) is re-checked with
|
|
245
|
+
// the FULL batch.
|
|
246
|
+
h = await makeHarness({ allowFn: () => false });
|
|
247
|
+
h.canReadBatchFull.mockImplementation(async (_w: string, _e: string, paths: string[]) =>
|
|
248
|
+
new Map(paths.map((p) => [p, p === 'Knowledge/mine.md'])));
|
|
249
|
+
expect((await get('/files')).status).toBe(200);
|
|
250
|
+
const filter = h.listFilesFilter();
|
|
251
|
+
const verdict = await filter!([
|
|
252
|
+
`${KB}/Knowledge`,
|
|
253
|
+
`${KB}/Knowledge/mine.md`,
|
|
254
|
+
`${KB}/Knowledge/other.md`,
|
|
255
|
+
`${KB}/Knowledge/Secret/deep.md`,
|
|
256
|
+
]);
|
|
257
|
+
expect(verdict.get(`${KB}/Knowledge`)).toBe(true);
|
|
258
|
+
expect(verdict.get(`${KB}/Knowledge/mine.md`)).toBe(true);
|
|
259
|
+
expect(verdict.get(`${KB}/Knowledge/other.md`)).toBe(false);
|
|
260
|
+
expect(verdict.get(`${KB}/Knowledge/Secret/deep.md`)).toBe(false);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('GET /workspace bootstrap also returns a read-filtered file tree', async () => {
|
|
264
|
+
h = await makeHarness();
|
|
265
|
+
const res = await fetch(`${h.baseUrl}/api/workspace?branch=${encodeURIComponent(WS)}`);
|
|
266
|
+
expect(res.status).toBe(200);
|
|
267
|
+
const filter = h.listFilesFilter();
|
|
268
|
+
expect(typeof filter).toBe('function');
|
|
269
|
+
const verdict = await filter!([
|
|
270
|
+
`${KB}/Knowledge/Open/a.md`,
|
|
271
|
+
`${KB}/Knowledge/Secret/x.md`,
|
|
272
|
+
]);
|
|
273
|
+
expect(verdict.get(`${KB}/Knowledge/Open/a.md`)).toBe(true);
|
|
274
|
+
expect(verdict.get(`${KB}/Knowledge/Secret/x.md`)).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
});
|