@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,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parse → edit → re-emit editor for `roles.yaml`.
|
|
3
|
+
*
|
|
4
|
+
* WHY re-emit and not a line-splice (cf. `access-splice.ts`): `access-splice.ts`
|
|
5
|
+
* splices `access.md` line-by-line specifically because those files carry a
|
|
6
|
+
* markdown BODY below the frontmatter (the repo-root one is a whole README)
|
|
7
|
+
* that a parse→emit round-trip would silently delete. `roles.yaml` has no body
|
|
8
|
+
* and no frontmatter fence — it is a plain `roles:` mapping of display-name →
|
|
9
|
+
* email list — and no real `roles.yaml` in the repo carries comments. So we
|
|
10
|
+
* parse the file into a model, apply the edit, and re-emit a canonical,
|
|
11
|
+
* deterministic file. Re-emit is idempotent (re-emitting an unchanged model
|
|
12
|
+
* yields byte-identical text), so a single edit moves only the lines it
|
|
13
|
+
* actually changes. The trade-off — hand-written comments are not preserved —
|
|
14
|
+
* is a documented non-goal.
|
|
15
|
+
*
|
|
16
|
+
* roles: (always the top-level key)
|
|
17
|
+
* Admin: (role display name, indent 2)
|
|
18
|
+
* - a@x.eu (member email, indent 4)
|
|
19
|
+
* - b@x.eu
|
|
20
|
+
* Empty Role: [] (the only empty-list form the subset parser
|
|
21
|
+
* reads back — a bare `Empty Role:` parses to
|
|
22
|
+
* null and fails parseRolesYaml's list check)
|
|
23
|
+
*
|
|
24
|
+
* Every mutation returns `{ text, changed }`; `changed: false` means the edit
|
|
25
|
+
* was a no-op (e.g. adding a member who already exists) and the caller should
|
|
26
|
+
* skip the commit. Role ORDER is preserved as parsed; a newly created role is
|
|
27
|
+
* appended last. Member emails within a role are canonicalised + de-duplicated.
|
|
28
|
+
*
|
|
29
|
+
* This module does NOT enforce the Admin-must-exist / no-self-lockout
|
|
30
|
+
* invariants — those are policy and live in `roles-admin.service.ts`. It only
|
|
31
|
+
* guarantees the emitted text is structurally well-formed YAML the resolver can
|
|
32
|
+
* load (the service additionally runs `validateRolesYaml` as a backstop gate).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
parseYamlSubset,
|
|
37
|
+
canonicalRoleName,
|
|
38
|
+
canonicalEmail,
|
|
39
|
+
EMAIL_REGEX,
|
|
40
|
+
RESERVED_ROLE_NAMES,
|
|
41
|
+
} from './access-control.service.js';
|
|
42
|
+
|
|
43
|
+
/** Bad-input failure when editing roles.yaml (invalid name/email, unknown role). */
|
|
44
|
+
export class RolesEditError extends Error {
|
|
45
|
+
readonly status: number;
|
|
46
|
+
constructor(message: string, status = 422) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = 'RolesEditError';
|
|
49
|
+
this.status = status;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** One role in parse order: display name + its de-duplicated member emails. */
|
|
54
|
+
export interface RoleModel {
|
|
55
|
+
displayName: string;
|
|
56
|
+
/** canonicalised, de-duplicated, in first-seen order */
|
|
57
|
+
members: string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type RolesModel = RoleModel[];
|
|
61
|
+
|
|
62
|
+
export interface EditResult {
|
|
63
|
+
text: string;
|
|
64
|
+
changed: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Reject role display names whose characters would corrupt the emitted YAML.
|
|
69
|
+
* `canonicalRoleName` only trims/lowercases/collapses spaces — it does NOT
|
|
70
|
+
* reject structural characters, so a name like `Sales: West` or `#temp` would
|
|
71
|
+
* round-trip into a broken file. This guard is the structural complement to the
|
|
72
|
+
* reserved/duplicate checks the service layer applies.
|
|
73
|
+
*
|
|
74
|
+
* `:` mis-tokenises as a nested mapping key
|
|
75
|
+
* `#` truncated as a comment by the subset parser's stripComment
|
|
76
|
+
* `<` / `>` collide with the `Name <email>` user-reference shape
|
|
77
|
+
* leading - tokenises as a list item
|
|
78
|
+
* \x00-\x1f control chars / newlines break line structure outright
|
|
79
|
+
*/
|
|
80
|
+
export function assertSafeRoleDisplayName(displayName: string): void {
|
|
81
|
+
const trimmed = displayName.trim();
|
|
82
|
+
if (!trimmed) throw new RolesEditError('role name must not be empty');
|
|
83
|
+
if (/[:#<>]/.test(trimmed)) {
|
|
84
|
+
throw new RolesEditError(`role name must not contain ':', '#', '<', or '>': ${JSON.stringify(displayName)}`);
|
|
85
|
+
}
|
|
86
|
+
// eslint-disable-next-line no-control-regex
|
|
87
|
+
if (/[\x00-\x1f]/.test(trimmed)) {
|
|
88
|
+
throw new RolesEditError(`role name must not contain control characters: ${JSON.stringify(displayName)}`);
|
|
89
|
+
}
|
|
90
|
+
if (trimmed.startsWith('-')) {
|
|
91
|
+
throw new RolesEditError(`role name must not start with '-': ${JSON.stringify(displayName)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Parse `roles.yaml` text into the ordered model. Tolerant of an empty/missing
|
|
97
|
+
* file (yields `[]`). Throws RolesEditError on structurally-broken YAML — the
|
|
98
|
+
* caller reads under the lock, so a broken on-disk file is an operator problem
|
|
99
|
+
* surfaced as a 422, not a silent reset.
|
|
100
|
+
*/
|
|
101
|
+
export function parseRolesModel(text: string): RolesModel {
|
|
102
|
+
const trimmed = text.trim();
|
|
103
|
+
if (!trimmed) return [];
|
|
104
|
+
const parsed = parseYamlSubset(text);
|
|
105
|
+
if (!parsed.ok) throw new RolesEditError(`roles.yaml: ${parsed.error}`);
|
|
106
|
+
const root = parsed.value;
|
|
107
|
+
if (root == null || typeof root !== 'object' || Array.isArray(root)) {
|
|
108
|
+
throw new RolesEditError('roles.yaml: must be a top-level mapping');
|
|
109
|
+
}
|
|
110
|
+
const rolesNode = (root as Record<string, unknown>).roles;
|
|
111
|
+
if (rolesNode == null) return [];
|
|
112
|
+
if (typeof rolesNode !== 'object' || Array.isArray(rolesNode)) {
|
|
113
|
+
throw new RolesEditError("roles.yaml: 'roles' must be a mapping");
|
|
114
|
+
}
|
|
115
|
+
const model: RolesModel = [];
|
|
116
|
+
for (const [displayName, value] of Object.entries(rolesNode as Record<string, unknown>)) {
|
|
117
|
+
const members: string[] = [];
|
|
118
|
+
// A role value must be a list of emails, an empty list (`[]`), or null
|
|
119
|
+
// (`Role:` with no members). Any OTHER shape (a scalar string, a nested
|
|
120
|
+
// mapping) is a malformed file — surface it as the documented 422 rather
|
|
121
|
+
// than silently coercing it to an empty role (CodeRabbit: a coerce would
|
|
122
|
+
// let an unrelated edit rewrite a broken roles.yaml).
|
|
123
|
+
if (value !== null && !Array.isArray(value)) {
|
|
124
|
+
throw new RolesEditError(`roles.yaml: role '${displayName.trim()}' must be a list of emails`);
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
const seen = new Set<string>();
|
|
128
|
+
for (const raw of value) {
|
|
129
|
+
// A non-string member entry is malformed — reject, don't drop.
|
|
130
|
+
if (typeof raw !== 'string') {
|
|
131
|
+
throw new RolesEditError(`roles.yaml: role '${displayName.trim()}' has a non-string member`);
|
|
132
|
+
}
|
|
133
|
+
const email = canonicalEmail(raw);
|
|
134
|
+
if (!email || seen.has(email)) continue;
|
|
135
|
+
seen.add(email);
|
|
136
|
+
members.push(email);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
model.push({ displayName: displayName.trim(), members });
|
|
140
|
+
}
|
|
141
|
+
return model;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Re-emit the model as canonical `roles.yaml` text. Deterministic: same model →
|
|
146
|
+
* same bytes. An empty role is emitted as `Name: []` (round-trippable); a role
|
|
147
|
+
* with members is emitted as a block list. Always ends with a trailing newline.
|
|
148
|
+
*/
|
|
149
|
+
export function emitRolesModel(model: RolesModel): string {
|
|
150
|
+
const lines: string[] = ['roles:'];
|
|
151
|
+
for (const role of model) {
|
|
152
|
+
if (role.members.length === 0) {
|
|
153
|
+
lines.push(` ${role.displayName}: []`);
|
|
154
|
+
} else {
|
|
155
|
+
lines.push(` ${role.displayName}:`);
|
|
156
|
+
for (const email of role.members) lines.push(` - ${email}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return lines.join('\n') + '\n';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function findRole(model: RolesModel, canonical: string): RoleModel | undefined {
|
|
163
|
+
return model.find((r) => canonicalRoleName(r.displayName) === canonical);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function reemit(original: string, model: RolesModel): EditResult {
|
|
167
|
+
const text = emitRolesModel(model);
|
|
168
|
+
// Compare against a re-emit of the ORIGINAL so a pure formatting difference
|
|
169
|
+
// (e.g. legacy spacing) doesn't masquerade as a content change — `changed`
|
|
170
|
+
// tracks whether the canonical model moved, which is what the commit cares
|
|
171
|
+
// about.
|
|
172
|
+
let originalCanonical: string;
|
|
173
|
+
try {
|
|
174
|
+
originalCanonical = emitRolesModel(parseRolesModel(original));
|
|
175
|
+
} catch {
|
|
176
|
+
originalCanonical = original;
|
|
177
|
+
}
|
|
178
|
+
return { text, changed: text !== originalCanonical };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Create a new empty role. 422 on reserved/duplicate/structurally-unsafe name. */
|
|
182
|
+
export function createRole(text: string, displayName: string): EditResult {
|
|
183
|
+
assertSafeRoleDisplayName(displayName);
|
|
184
|
+
const canonical = canonicalRoleName(displayName);
|
|
185
|
+
if (RESERVED_ROLE_NAMES.has(canonical)) {
|
|
186
|
+
throw new RolesEditError(`'${displayName.trim()}' is a reserved name and cannot be a role`);
|
|
187
|
+
}
|
|
188
|
+
const model = parseRolesModel(text);
|
|
189
|
+
if (findRole(model, canonical)) {
|
|
190
|
+
throw new RolesEditError(`a role named '${displayName.trim()}' already exists`);
|
|
191
|
+
}
|
|
192
|
+
model.push({ displayName: displayName.trim(), members: [] });
|
|
193
|
+
return reemit(text, model);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Delete a role by canonical name. 404 if absent. */
|
|
197
|
+
export function deleteRole(text: string, canonical: string): EditResult {
|
|
198
|
+
const model = parseRolesModel(text);
|
|
199
|
+
const idx = model.findIndex((r) => canonicalRoleName(r.displayName) === canonical);
|
|
200
|
+
if (idx < 0) throw new RolesEditError(`role not found: ${canonical}`, 404);
|
|
201
|
+
model.splice(idx, 1);
|
|
202
|
+
return reemit(text, model);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Add a member email to a role. Idempotent. 422 bad email; 404 unknown role. */
|
|
206
|
+
export function addMember(text: string, canonical: string, rawEmail: string): EditResult {
|
|
207
|
+
const email = canonicalEmail(rawEmail);
|
|
208
|
+
if (!EMAIL_REGEX.test(email)) throw new RolesEditError(`malformed email: ${JSON.stringify(rawEmail)}`);
|
|
209
|
+
const model = parseRolesModel(text);
|
|
210
|
+
const role = findRole(model, canonical);
|
|
211
|
+
if (!role) throw new RolesEditError(`role not found: ${canonical}`, 404);
|
|
212
|
+
if (role.members.includes(email)) return { text: emitRolesModel(model), changed: false };
|
|
213
|
+
role.members.push(email);
|
|
214
|
+
return reemit(text, model);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Remove a member email from a role. 404 unknown role; no-op if not a member. */
|
|
218
|
+
export function removeMember(text: string, canonical: string, rawEmail: string): EditResult {
|
|
219
|
+
const email = canonicalEmail(rawEmail);
|
|
220
|
+
const model = parseRolesModel(text);
|
|
221
|
+
const role = findRole(model, canonical);
|
|
222
|
+
if (!role) throw new RolesEditError(`role not found: ${canonical}`, 404);
|
|
223
|
+
const idx = role.members.indexOf(email);
|
|
224
|
+
if (idx < 0) return { text: emitRolesModel(model), changed: false };
|
|
225
|
+
role.members.splice(idx, 1);
|
|
226
|
+
return reemit(text, model);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Rename a role's display name. The canonical name MAY change (the service
|
|
231
|
+
* layer is responsible for the reference rewrite + Admin guard when it does).
|
|
232
|
+
* 404 unknown source; 422 reserved/duplicate/unsafe target.
|
|
233
|
+
*/
|
|
234
|
+
export function renameRoleDisplay(text: string, canonical: string, newDisplayName: string): EditResult {
|
|
235
|
+
assertSafeRoleDisplayName(newDisplayName);
|
|
236
|
+
const newCanonical = canonicalRoleName(newDisplayName);
|
|
237
|
+
if (RESERVED_ROLE_NAMES.has(newCanonical)) {
|
|
238
|
+
throw new RolesEditError(`'${newDisplayName.trim()}' is a reserved name and cannot be a role`);
|
|
239
|
+
}
|
|
240
|
+
const model = parseRolesModel(text);
|
|
241
|
+
const role = findRole(model, canonical);
|
|
242
|
+
if (!role) throw new RolesEditError(`role not found: ${canonical}`, 404);
|
|
243
|
+
if (newCanonical !== canonical && findRole(model, newCanonical)) {
|
|
244
|
+
throw new RolesEditError(`a role named '${newDisplayName.trim()}' already exists`);
|
|
245
|
+
}
|
|
246
|
+
role.displayName = newDisplayName.trim();
|
|
247
|
+
return reemit(text, model);
|
|
248
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-disk validity gate for `roles.yaml`.
|
|
3
|
+
*
|
|
4
|
+
* WHY this exists: `roles.yaml` is the single point of failure for the whole
|
|
5
|
+
* app. The runtime resolver's `loadModel` HARD-THROWS `AccessConfigError` on a
|
|
6
|
+
* parse failure, and `isAdmin` swallows that into `false` for EVERYONE — so a
|
|
7
|
+
* single malformed `roles.yaml` is an app-wide, in-app-unrecoverable admin
|
|
8
|
+
* lockout. The dedicated Roles & Members service already validates every
|
|
9
|
+
* candidate before writing (`assertLoadable`), but the two RAW write paths do
|
|
10
|
+
* not:
|
|
11
|
+
* - the human editor save (`PUT /workspace/:id/file`)
|
|
12
|
+
* - the agent's file tools (`LockingFilesystem.writeFile` / `writeFiles`)
|
|
13
|
+
*
|
|
14
|
+
* Both of those let an admin (or an admin-driven agent) commit a broken file.
|
|
15
|
+
* This guard plugs them: it runs the resolver's OWN parser on the candidate
|
|
16
|
+
* BEFORE a byte hits disk, and refuses the write with a 422 on any error. A
|
|
17
|
+
* write that passes here is loadable by `loadModel`, so the lockout becomes
|
|
18
|
+
* structurally unreachable through normal edits.
|
|
19
|
+
*
|
|
20
|
+
* The break-glass recovery path (for a file that got broken some OTHER way —
|
|
21
|
+
* a direct git push, a pre-guard commit) lives in `roles-admin.service.ts`.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { FileContent } from '@mastra/core/workspace';
|
|
25
|
+
import { parseRolesYaml } from './access-control.service.js';
|
|
26
|
+
import { WorkflowDomainError } from '../workflow/workflow.errors.js';
|
|
27
|
+
|
|
28
|
+
/** Repo-relative basename of the roles file (it lives at the KB repo root). */
|
|
29
|
+
export const ROLES_YAML_BASENAME = 'roles.yaml';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A raw write whose candidate `roles.yaml` would not parse. 422 (bad input) —
|
|
33
|
+
* the edit is refused and nothing is written. Carries the parser errors so the
|
|
34
|
+
* editor / agent can show exactly what's wrong.
|
|
35
|
+
*/
|
|
36
|
+
export class RolesYamlInvalidError extends WorkflowDomainError {
|
|
37
|
+
readonly errors: string[];
|
|
38
|
+
constructor(errors: string[]) {
|
|
39
|
+
super(
|
|
40
|
+
`roles.yaml would be invalid and was not saved: ${errors.join('; ')}`,
|
|
41
|
+
422,
|
|
42
|
+
{ kind: 'roles-yaml-invalid', rolesYamlErrors: errors },
|
|
43
|
+
);
|
|
44
|
+
this.name = 'RolesYamlInvalidError';
|
|
45
|
+
this.errors = errors;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Normalise a workspace-relative path: backslashes → `/`, strip a leading `./`. */
|
|
50
|
+
function normalizeWsPath(p: string): string {
|
|
51
|
+
return p.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* True iff `workspaceRelPath` targets the KB's `roles.yaml` (the file at
|
|
56
|
+
* `<kbDirName>/roles.yaml`). Both the human save route and the agent's
|
|
57
|
+
* `LockingFilesystem` speak workspace-relative paths, so this is the form we
|
|
58
|
+
* key on. A bare `roles.yaml` (no KB prefix) is also accepted defensively.
|
|
59
|
+
*/
|
|
60
|
+
export function isRolesYamlPath(workspaceRelPath: string, kbDirName: string): boolean {
|
|
61
|
+
const norm = normalizeWsPath(workspaceRelPath);
|
|
62
|
+
return norm === `${kbDirName}/${ROLES_YAML_BASENAME}` || norm === ROLES_YAML_BASENAME;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Throw {@link RolesYamlInvalidError} if `content` does not parse as a valid
|
|
67
|
+
* `roles.yaml` per the resolver's own parser. No-op on valid content.
|
|
68
|
+
*/
|
|
69
|
+
export function assertRolesYamlParsable(content: string): void {
|
|
70
|
+
const parsed = parseRolesYaml(content);
|
|
71
|
+
if (!parsed.ok) throw new RolesYamlInvalidError(parsed.errors);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A `LockingFilesystem` write-validator scoped to a KB dir: refuses a raw write
|
|
76
|
+
* that would leave `roles.yaml` unparseable. Pass the returned closure as the
|
|
77
|
+
* filesystem's `validateWrite` hook. Only string writes are checked — every
|
|
78
|
+
* code path that writes `roles.yaml` writes it as text; a binary write to that
|
|
79
|
+
* path is nonsensical and left alone.
|
|
80
|
+
*/
|
|
81
|
+
export function makeRolesYamlWriteValidator(
|
|
82
|
+
kbDirName: string,
|
|
83
|
+
): (path: string, content: FileContent) => void {
|
|
84
|
+
return (path, content) => {
|
|
85
|
+
if (typeof content !== 'string') return;
|
|
86
|
+
if (!isRolesYamlPath(path, kbDirName)) return;
|
|
87
|
+
assertRolesYamlParsable(content);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import type { IAdminAccessService } from './admin.interface.js';
|
|
3
|
+
import '../auth/auth.middleware.js'; // Express Request augmentation
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The CORE slice of the admin surface: just the admin-status resolver the
|
|
7
|
+
* frontend branches on (AdminMenu's "Admin only" section, the roles-recovery
|
|
8
|
+
* banner). The full admin router (feedback inbox, user administration /
|
|
9
|
+
* GDPR erasure) is an enterprise extension mounted via
|
|
10
|
+
* `ServerExtensions.authed`.
|
|
11
|
+
*
|
|
12
|
+
* NOTE (core split): in bevel-platform this endpoint lives in the enterprise
|
|
13
|
+
* `admin.routes.ts`; core serves it itself so a core-only deployment can
|
|
14
|
+
* resolve admin status. Express keeps first-match-wins, so an enterprise
|
|
15
|
+
* overlay's own `/admin/access` (mounted later) never shadows this one —
|
|
16
|
+
* both resolve through the same {@link IAdminAccessService}.
|
|
17
|
+
*/
|
|
18
|
+
export function createAdminAccessRoutes(adminAccess: IAdminAccessService): express.Router {
|
|
19
|
+
const router = express.Router();
|
|
20
|
+
|
|
21
|
+
// Open to any authenticated user — returns `false` for non-admins instead of
|
|
22
|
+
// 403 so the frontend can branch on the answer without treating a normal
|
|
23
|
+
// user as an error case.
|
|
24
|
+
router.get('/admin/access', async (req, res) => {
|
|
25
|
+
res.json({ isAdmin: await adminAccess.isAdmin(req.userEmail) });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return router;
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { IAccessControl } from '../access/access-control.interface.js';
|
|
2
|
+
import { type WorkspaceService, workspaceIdForBranch } from '../workspace/workspace.service.js';
|
|
3
|
+
import type { IAdminAccessService } from './admin.interface.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Admin = holder of the `Admin` role in `roles.yaml`. We resolve it through the
|
|
7
|
+
* access model exactly as the rest of the app does — `canWrite('roles.yaml')` is
|
|
8
|
+
* the existing source of truth for Admin-role membership (only admins may write
|
|
9
|
+
* `roles.yaml`). Read from the default-branch workspace so admin status doesn't
|
|
10
|
+
* depend on whichever branch the caller happens to be on.
|
|
11
|
+
*
|
|
12
|
+
* There is no env allow-list (`ADMIN_EMAILS` is gone) — `roles.yaml` is the
|
|
13
|
+
* single source of truth.
|
|
14
|
+
*/
|
|
15
|
+
export class AdminAccessService implements IAdminAccessService {
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly accessControl: IAccessControl,
|
|
18
|
+
private readonly workspaceService: WorkspaceService,
|
|
19
|
+
/** Branch whose `roles.yaml` defines admins (the authoritative default branch). */
|
|
20
|
+
private readonly branch: string,
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
async isAdmin(email: string | undefined): Promise<boolean> {
|
|
24
|
+
if (!email) return false;
|
|
25
|
+
try {
|
|
26
|
+
// Ensure the authoritative clone is on disk, then resolve Admin-role
|
|
27
|
+
// membership via the access model. Any failure (missing/invalid
|
|
28
|
+
// roles.yaml, clone error) resolves to "not admin" — the safe default.
|
|
29
|
+
await this.workspaceService.getOrCreateForBranch(this.branch);
|
|
30
|
+
return await this.accessControl.canWrite(workspaceIdForBranch(this.branch), email, 'roles.yaml');
|
|
31
|
+
} catch (err) {
|
|
32
|
+
// Log so an admin lockout caused by a clone / roles.yaml failure is
|
|
33
|
+
// diagnosable rather than a silent denial.
|
|
34
|
+
console.warn('[admin] isAdmin resolution failed:', err instanceof Error ? err.message : String(err));
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract for the deployment admin check. A user is an admin iff they hold the
|
|
3
|
+
* `Admin` role in the KB's `roles.yaml` — the same source of truth the access
|
|
4
|
+
* model uses for `canWrite('roles.yaml')`. Async because resolving it reads the
|
|
5
|
+
* access model from the default-branch workspace. Kept as an interface so tests
|
|
6
|
+
* can swap a static stub without loading the access tree.
|
|
7
|
+
*/
|
|
8
|
+
export interface IAdminAccessService {
|
|
9
|
+
isAdmin(email: string | undefined): Promise<boolean>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { eq } from 'drizzle-orm';
|
|
3
|
+
import type { Database } from '../database/connection.js';
|
|
4
|
+
import {
|
|
5
|
+
changeRequests,
|
|
6
|
+
externalApiKeys,
|
|
7
|
+
fileLocks,
|
|
8
|
+
oauthAuthCodes,
|
|
9
|
+
oauthTokens,
|
|
10
|
+
pendingCommits,
|
|
11
|
+
prComments,
|
|
12
|
+
prFileApprovals,
|
|
13
|
+
prMergeLog,
|
|
14
|
+
users,
|
|
15
|
+
} from '../database/schema.js';
|
|
16
|
+
|
|
17
|
+
/** A user row as the admin surface needs it (no avatar, no timestamps churn). */
|
|
18
|
+
export interface AdminUserView {
|
|
19
|
+
id: string;
|
|
20
|
+
email: string;
|
|
21
|
+
name: string;
|
|
22
|
+
createdAt: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The drizzle client erasure participants receive inside the transaction. */
|
|
26
|
+
export type ErasureTx = Parameters<Parameters<Database['transaction']>[0]>[0];
|
|
27
|
+
|
|
28
|
+
/** Identity context for one erasure run. */
|
|
29
|
+
export interface ErasureTarget {
|
|
30
|
+
userId: string;
|
|
31
|
+
/** The user's (lowercased) email at erasure time — for email-keyed rows. */
|
|
32
|
+
email: string;
|
|
33
|
+
/**
|
|
34
|
+
* Per-erasure placeholder identity for anonymized audit rows. Random per
|
|
35
|
+
* erasure: no link back to the person, but rows from ONE erasure stay
|
|
36
|
+
* correlated and email-keyed unique indexes can't collide across erasures.
|
|
37
|
+
*/
|
|
38
|
+
erasedEmail: string;
|
|
39
|
+
erasedName: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A module-owned slice of account erasure. The core service erases the rows
|
|
44
|
+
* it owns (tokens, locks, review-trail anonymization, the user row) and runs
|
|
45
|
+
* every registered participant so each module cleans up its own tables —
|
|
46
|
+
* chat threads, connector links, routine authorship, … — without the auth
|
|
47
|
+
* module knowing they exist. Registered at the composition root.
|
|
48
|
+
*
|
|
49
|
+
* - `before` runs OUTSIDE the transaction, first. For idempotent pre-cleanup
|
|
50
|
+
* against external stores (e.g. chat-thread message memory) where a failure
|
|
51
|
+
* must leave a retryable state, not a half-committed one.
|
|
52
|
+
* - `inTransaction` runs INSIDE the erasure transaction, BEFORE the users row
|
|
53
|
+
* is deleted (so FKs onto users are still satisfiable and RESTRICT FKs make
|
|
54
|
+
* missed rows fail loudly). It may return a callback, which runs after the
|
|
55
|
+
* transaction commits — for external-store cleanup of rows captured during
|
|
56
|
+
* the transaction.
|
|
57
|
+
*/
|
|
58
|
+
export interface IErasureParticipant {
|
|
59
|
+
before?(target: ErasureTarget): Promise<void>;
|
|
60
|
+
inTransaction?(tx: ErasureTx, target: ErasureTarget): Promise<void | (() => Promise<void>)>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* GDPR account erasure (Art. 17). Operator-driven: an admin deletes a user in
|
|
65
|
+
* response to an erasure request. What it guarantees:
|
|
66
|
+
*
|
|
67
|
+
* - Rows that ARE the user's personal data are hard-deleted: API/OAuth
|
|
68
|
+
* tokens, held file locks, the user row itself, and every registered
|
|
69
|
+
* participant's module-owned rows (chat threads incl. message memory,
|
|
70
|
+
* Microsoft connection, feedback, revalidation requests, upload tokens).
|
|
71
|
+
* Deleting the user row cascades whatever FKs onto it with ON DELETE
|
|
72
|
+
* CASCADE (account links, watchlist sources/findings, connector configs,
|
|
73
|
+
* vault secrets); deleting a connection key cascades its usage metering.
|
|
74
|
+
* - Audit rows that must survive for the review trail (approvals, merge log,
|
|
75
|
+
* review comments, change requests, queued commits — and, via participants,
|
|
76
|
+
* e.g. routine authorship) are kept but ANONYMIZED with the per-erasure
|
|
77
|
+
* placeholder identity.
|
|
78
|
+
*
|
|
79
|
+
* Out of scope, by firm policy (disclosed in the DPA): git history is never
|
|
80
|
+
* rewritten. Commit authorship and historical access-file entries stay in the
|
|
81
|
+
* KB's version history permanently as part of the tamper-evident record;
|
|
82
|
+
* erasure covers every database/filesystem store plus the CURRENT KB state.
|
|
83
|
+
*
|
|
84
|
+
* Note: sign-in is get-or-create by email, so a person who authenticates again
|
|
85
|
+
* after erasure simply gets a fresh, empty account — that is intended.
|
|
86
|
+
*/
|
|
87
|
+
export interface IAccountErasureService {
|
|
88
|
+
listUsers(): Promise<AdminUserView[]>;
|
|
89
|
+
/** Erase `userId`. Returns false when no such user exists. */
|
|
90
|
+
eraseUser(userId: string): Promise<boolean>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export class AccountErasureService implements IAccountErasureService {
|
|
94
|
+
constructor(
|
|
95
|
+
private readonly db: Database,
|
|
96
|
+
private readonly participants: IErasureParticipant[] = [],
|
|
97
|
+
) {}
|
|
98
|
+
|
|
99
|
+
async listUsers(): Promise<AdminUserView[]> {
|
|
100
|
+
const rows = await this.db
|
|
101
|
+
.select({ id: users.id, email: users.email, name: users.name, createdAt: users.createdAt })
|
|
102
|
+
.from(users)
|
|
103
|
+
.orderBy(users.email);
|
|
104
|
+
return rows.map((r) => ({ ...r, createdAt: r.createdAt.getTime() }));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async eraseUser(userId: string): Promise<boolean> {
|
|
108
|
+
const [user] = await this.db.select().from(users).where(eq(users.id, userId)).limit(1);
|
|
109
|
+
if (!user) return false;
|
|
110
|
+
|
|
111
|
+
const target: ErasureTarget = {
|
|
112
|
+
userId,
|
|
113
|
+
email: user.email.toLowerCase(),
|
|
114
|
+
erasedEmail: `deleted-${randomUUID()}@erased.invalid`,
|
|
115
|
+
erasedName: 'Deleted user',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Participant pre-passes (e.g. chat threads + their Mastra memory) run
|
|
119
|
+
// outside the transaction on purpose: external stores are separate
|
|
120
|
+
// systems, and the pre-passes are idempotent, so a failure here leaves a
|
|
121
|
+
// retryable state rather than a half-committed one.
|
|
122
|
+
for (const p of this.participants) {
|
|
123
|
+
if (p.before) await p.before(target);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const postCommit: Array<() => Promise<void>> = [];
|
|
127
|
+
await this.db.transaction(async (tx) => {
|
|
128
|
+
// Token-shaped rows (all hashed, but they key to the user). Dependents
|
|
129
|
+
// like the LLM-usage metering rows cascade at the DB layer.
|
|
130
|
+
await tx.delete(externalApiKeys).where(eq(externalApiKeys.userId, userId));
|
|
131
|
+
await tx.delete(oauthAuthCodes).where(eq(oauthAuthCodes.userId, userId));
|
|
132
|
+
await tx.delete(oauthTokens).where(eq(oauthTokens.userId, userId));
|
|
133
|
+
|
|
134
|
+
// Personal-data rows the core owns.
|
|
135
|
+
await tx.delete(fileLocks).where(eq(fileLocks.holderUserId, userId));
|
|
136
|
+
|
|
137
|
+
// Audit rows: anonymize in place (no user FK on these; they key by email).
|
|
138
|
+
await tx
|
|
139
|
+
.update(prFileApprovals)
|
|
140
|
+
.set({ approverEmail: target.erasedEmail, approverName: target.erasedName })
|
|
141
|
+
.where(eq(prFileApprovals.approverEmail, target.email));
|
|
142
|
+
await tx
|
|
143
|
+
.update(prMergeLog)
|
|
144
|
+
.set({ triggeredByEmail: target.erasedEmail, triggeredByName: target.erasedName })
|
|
145
|
+
.where(eq(prMergeLog.triggeredByEmail, target.email));
|
|
146
|
+
await tx
|
|
147
|
+
.update(prComments)
|
|
148
|
+
.set({ authorEmail: target.erasedEmail, authorName: target.erasedName })
|
|
149
|
+
.where(eq(prComments.authorEmail, target.email));
|
|
150
|
+
await tx
|
|
151
|
+
.update(changeRequests)
|
|
152
|
+
.set({ authorEmail: target.erasedEmail, authorName: target.erasedName })
|
|
153
|
+
.where(eq(changeRequests.authorEmail, target.email));
|
|
154
|
+
// Queued-but-uncommitted saves: the eventual git commit is authored with
|
|
155
|
+
// the placeholder instead of the erased identity. The file content still
|
|
156
|
+
// lands — erasing an account must not lose other people's KB state.
|
|
157
|
+
await tx
|
|
158
|
+
.update(pendingCommits)
|
|
159
|
+
.set({ authorEmail: target.erasedEmail, authorName: target.erasedName })
|
|
160
|
+
.where(eq(pendingCommits.authorEmail, target.email));
|
|
161
|
+
|
|
162
|
+
// Module-owned rows, before the users delete so FKs onto users are
|
|
163
|
+
// still satisfiable — and so a participant that MISSES rows makes the
|
|
164
|
+
// users delete below fail on its RESTRICT FK, rolling everything back:
|
|
165
|
+
// a loud retry, never a silent orphan.
|
|
166
|
+
for (const p of this.participants) {
|
|
167
|
+
if (!p.inTransaction) continue;
|
|
168
|
+
const cb = await p.inTransaction(tx, target);
|
|
169
|
+
if (cb) postCommit.push(cb);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Finally the user row (plus its ON DELETE CASCADE dependents).
|
|
173
|
+
await tx.delete(users).where(eq(users.id, userId));
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Post-commit callbacks (e.g. Mastra memory cleanup for chat threads
|
|
177
|
+
// captured inside the transaction).
|
|
178
|
+
for (const cb of postCommit) await cb();
|
|
179
|
+
|
|
180
|
+
console.log(`[account-erasure] erased user id=${userId}`);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import type { AuthService } from './auth.service.js';
|
|
3
|
+
|
|
4
|
+
// Augment Express Request so userId/userEmail are available after auth middleware
|
|
5
|
+
declare global {
|
|
6
|
+
namespace Express {
|
|
7
|
+
interface Request {
|
|
8
|
+
userId?: string;
|
|
9
|
+
userEmail?: string;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Name of the auth cookie set alongside the Bearer JWT at login. The cookie
|
|
16
|
+
* exists solely so the SSE route can authenticate — the browser's
|
|
17
|
+
* `EventSource` API can't set custom headers, so a Bearer token in
|
|
18
|
+
* `Authorization` can't reach it. All other routes still use the Bearer
|
|
19
|
+
* header, which the frontend stores in memory and attaches via `authFetch`.
|
|
20
|
+
*
|
|
21
|
+
* Same JWT value as the Bearer token. The cookie is `HttpOnly` + `SameSite=Lax`
|
|
22
|
+
* + `Secure` (in prod) — see `auth.routes.ts` for the issue site.
|
|
23
|
+
*/
|
|
24
|
+
export const AUTH_COOKIE_NAME = 'bevel_token';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read the auth cookie out of the raw `Cookie` header without pulling in
|
|
28
|
+
* `cookie-parser`. We only ever look for one cookie, so a tiny parser
|
|
29
|
+
* beats the dep surface of a middleware. Returns `null` if the cookie
|
|
30
|
+
* is absent or empty.
|
|
31
|
+
*/
|
|
32
|
+
function readAuthCookie(req: Request): string | null {
|
|
33
|
+
const header = req.headers.cookie;
|
|
34
|
+
if (!header) return null;
|
|
35
|
+
for (const segment of header.split(';')) {
|
|
36
|
+
const trimmed = segment.trim();
|
|
37
|
+
if (!trimmed.startsWith(`${AUTH_COOKIE_NAME}=`)) continue;
|
|
38
|
+
const value = trimmed.slice(AUTH_COOKIE_NAME.length + 1);
|
|
39
|
+
if (!value) return null;
|
|
40
|
+
try {
|
|
41
|
+
return decodeURIComponent(value);
|
|
42
|
+
} catch {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createAuthMiddleware(authService: AuthService) {
|
|
50
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
51
|
+
// Bearer is the primary path — every JSON API call in the frontend
|
|
52
|
+
// attaches it via `authFetch`. Cookie is the fallback for transports
|
|
53
|
+
// that can't carry headers (EventSource, image tags), set at login by
|
|
54
|
+
// `/auth/login` so SSE in particular Just Works after login without
|
|
55
|
+
// any frontend cooperation.
|
|
56
|
+
const header = req.headers.authorization;
|
|
57
|
+
let token: string | null = null;
|
|
58
|
+
if (header && header.startsWith('Bearer ')) {
|
|
59
|
+
token = header.slice('Bearer '.length).trim() || null;
|
|
60
|
+
}
|
|
61
|
+
if (!token) {
|
|
62
|
+
token = readAuthCookie(req);
|
|
63
|
+
}
|
|
64
|
+
if (!token) {
|
|
65
|
+
res.status(401).json({ error: 'Missing or invalid Authorization header or auth cookie' });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const { userId, email } = authService.verifyToken(token);
|
|
71
|
+
req.userId = userId;
|
|
72
|
+
req.userEmail = email;
|
|
73
|
+
next();
|
|
74
|
+
} catch {
|
|
75
|
+
res.status(401).json({ error: 'Invalid or expired token' });
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|