@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,150 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import type { AuthUser, IWorkflowService } from '@bevel-software/platform-shared';
|
|
6
|
+
import type { IDiffService } from '../diff.interface.js';
|
|
7
|
+
import { workspaceIdForBranch } from '../../workspace/workspace.service.js';
|
|
8
|
+
import { WorkflowValidationError } from '../../workflow/workflow.errors.js';
|
|
9
|
+
import { rejectPathsLocked } from '../diff.routes.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `rejectPathsLocked` — the BATCH reject. All paths go through ONE
|
|
13
|
+
* `LockingFilesystem.writeFiles` cycle: every lock acquired before the first
|
|
14
|
+
* disk mutation, baselines restored / agent-added files deleted, then a single
|
|
15
|
+
* `commitChanges` and lock release. Fail-closed: contention or a commit error
|
|
16
|
+
* aborts with nothing committed.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const USER: AuthUser = { id: 'user-1', email: 'alice@example.com', name: 'Alice' };
|
|
20
|
+
const BRANCH = 'alice/revert-test';
|
|
21
|
+
const WORKSPACE_ID = workspaceIdForBranch(BRANCH);
|
|
22
|
+
|
|
23
|
+
interface Harness {
|
|
24
|
+
workflow: IWorkflowService;
|
|
25
|
+
diff: IDiffService;
|
|
26
|
+
calls: string[];
|
|
27
|
+
workspaceDir: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function makeHarness(overrides: {
|
|
31
|
+
/** Per-path acquire outcome. Default: every path acquires cleanly. */
|
|
32
|
+
acquire?: (relPath: string) => { acquired: boolean; lock: { holderName: string } };
|
|
33
|
+
/** Throw from commitChanges to simulate a commit failure. */
|
|
34
|
+
commit?: () => void;
|
|
35
|
+
} = {}): Promise<Harness> {
|
|
36
|
+
const calls: string[] = [];
|
|
37
|
+
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-reject-'));
|
|
38
|
+
// Baseline-carrying file (backup exists → revert = write baseline bytes)
|
|
39
|
+
// and an agent-added file (no backup → revert = delete).
|
|
40
|
+
await fs.mkdir(path.join(workspaceDir, 'knowledge-base'), { recursive: true });
|
|
41
|
+
await fs.writeFile(path.join(workspaceDir, 'knowledge-base/a.md'), 'agent version\n');
|
|
42
|
+
await fs.writeFile(path.join(workspaceDir, 'knowledge-base/added.md'), 'agent-added\n');
|
|
43
|
+
|
|
44
|
+
const acquireLock = vi.fn(async (_ws: string, _branch: string, relPath: string) => {
|
|
45
|
+
const result = overrides.acquire
|
|
46
|
+
? overrides.acquire(relPath)
|
|
47
|
+
: { acquired: true, lock: { holderName: 'Alice' } };
|
|
48
|
+
calls.push(`acquire:${relPath}:${result.acquired}`);
|
|
49
|
+
return result;
|
|
50
|
+
});
|
|
51
|
+
const releaseLockNoCommit = vi.fn(async (_ws: string, _branch: string, relPath: string) => {
|
|
52
|
+
calls.push(`releaseNoCommit:${relPath}`);
|
|
53
|
+
});
|
|
54
|
+
const commitChanges = vi.fn(async (_ws: string, _user: AuthUser, summary: string) => {
|
|
55
|
+
calls.push(`commit:${summary}`);
|
|
56
|
+
overrides.commit?.();
|
|
57
|
+
return { sha: 'abc', summary } as never;
|
|
58
|
+
});
|
|
59
|
+
const workflow = { acquireLock, releaseLockNoCommit, commitChanges } as unknown as IWorkflowService;
|
|
60
|
+
|
|
61
|
+
const revertPlan = vi.fn(async (_ws: string, paths: string[]) => ({
|
|
62
|
+
workspaceDir,
|
|
63
|
+
writes: paths
|
|
64
|
+
.filter((p) => p.endsWith('a.md'))
|
|
65
|
+
.map((p) => ({ path: p, content: Buffer.from('baseline\n') })),
|
|
66
|
+
deletes: paths.filter((p) => p.endsWith('added.md')),
|
|
67
|
+
}));
|
|
68
|
+
const diff = { revertPlan } as unknown as IDiffService;
|
|
69
|
+
return { workflow, diff, calls, workspaceDir };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('rejectPathsLocked', () => {
|
|
73
|
+
let workspaceDir: string | undefined;
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
workspaceDir = undefined;
|
|
76
|
+
});
|
|
77
|
+
afterEach(async () => {
|
|
78
|
+
if (workspaceDir) await fs.rm(workspaceDir, { recursive: true, force: true });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('acquires EVERY lock before mutating, restores + deletes, then commits ONCE and releases', async () => {
|
|
82
|
+
const h = await makeHarness();
|
|
83
|
+
workspaceDir = h.workspaceDir;
|
|
84
|
+
|
|
85
|
+
await rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, [
|
|
86
|
+
'knowledge-base/a.md',
|
|
87
|
+
'knowledge-base/added.md',
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
// All locks first (sorted), ONE commit, then per-path release (no per-file commit).
|
|
91
|
+
expect(h.calls).toEqual([
|
|
92
|
+
'acquire:knowledge-base/a.md:true',
|
|
93
|
+
'acquire:knowledge-base/added.md:true',
|
|
94
|
+
'commit:Revert agent changes (2 files)',
|
|
95
|
+
'releaseNoCommit:knowledge-base/a.md',
|
|
96
|
+
'releaseNoCommit:knowledge-base/added.md',
|
|
97
|
+
]);
|
|
98
|
+
// Disk: baseline restored, agent-added file gone.
|
|
99
|
+
expect(await fs.readFile(path.join(h.workspaceDir, 'knowledge-base/a.md'), 'utf-8')).toBe('baseline\n');
|
|
100
|
+
await expect(fs.access(path.join(h.workspaceDir, 'knowledge-base/added.md'))).rejects.toThrow();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('is a no-op for an empty path list', async () => {
|
|
104
|
+
const h = await makeHarness();
|
|
105
|
+
workspaceDir = h.workspaceDir;
|
|
106
|
+
await expect(
|
|
107
|
+
rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, []),
|
|
108
|
+
).resolves.toBeUndefined();
|
|
109
|
+
expect(h.calls).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('fails CLOSED on a contended lock: nothing mutated, nothing committed', async () => {
|
|
113
|
+
const h = await makeHarness({
|
|
114
|
+
acquire: (relPath) =>
|
|
115
|
+
relPath.endsWith('added.md')
|
|
116
|
+
? { acquired: false, lock: { holderName: 'Bob' } }
|
|
117
|
+
: { acquired: true, lock: { holderName: 'Alice' } },
|
|
118
|
+
});
|
|
119
|
+
workspaceDir = h.workspaceDir;
|
|
120
|
+
|
|
121
|
+
await expect(
|
|
122
|
+
rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, [
|
|
123
|
+
'knowledge-base/a.md',
|
|
124
|
+
'knowledge-base/added.md',
|
|
125
|
+
]),
|
|
126
|
+
).rejects.toBeInstanceOf(WorkflowValidationError);
|
|
127
|
+
|
|
128
|
+
// No commit landed and the disk is untouched — the batch is atomic.
|
|
129
|
+
expect(h.calls.some((c) => c.startsWith('commit:'))).toBe(false);
|
|
130
|
+
expect(await fs.readFile(path.join(h.workspaceDir, 'knowledge-base/a.md'), 'utf-8')).toBe('agent version\n');
|
|
131
|
+
await expect(fs.access(path.join(h.workspaceDir, 'knowledge-base/added.md'))).resolves.toBeUndefined();
|
|
132
|
+
}, 15_000);
|
|
133
|
+
|
|
134
|
+
it('surfaces a commit failure as a WorkflowValidationError and releases without committing', async () => {
|
|
135
|
+
const h = await makeHarness({
|
|
136
|
+
commit: () => {
|
|
137
|
+
throw new Error('push rejected');
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
workspaceDir = h.workspaceDir;
|
|
141
|
+
|
|
142
|
+
await expect(
|
|
143
|
+
rejectPathsLocked(h.workflow, h.diff, WORKSPACE_ID, USER, [
|
|
144
|
+
'knowledge-base/a.md',
|
|
145
|
+
]),
|
|
146
|
+
).rejects.toBeInstanceOf(WorkflowValidationError);
|
|
147
|
+
// Locks were dropped via the no-commit path after the failure.
|
|
148
|
+
expect(h.calls.filter((c) => c.startsWith('releaseNoCommit:'))).toHaveLength(1);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { WorkspaceService, workspaceIdForBranch } from '../../workspace/workspace.service.js';
|
|
6
|
+
import { WorkspaceMutex } from '../../workflow/git/mutex.js';
|
|
7
|
+
import { DiffService } from '../diff.service.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Atomic-seed regression suite.
|
|
11
|
+
*
|
|
12
|
+
* Bug we're guarding against (reproduced once in production): two users
|
|
13
|
+
* switch to a fresh, not-yet-cloned branch concurrently. One of them sees
|
|
14
|
+
* EVERY cloned file as an `added` pending change.
|
|
15
|
+
*
|
|
16
|
+
* Root cause: the original `ensureSeededUnlocked` did `mkdir(backupDir)`
|
|
17
|
+
* THEN `copyDiffableTree(...)`. If the copy threw mid-stream (any I/O
|
|
18
|
+
* hiccup, EACCES on one file, the process restarted, …) `backupDir`
|
|
19
|
+
* already existed but was empty. The next call's `fs.access(backupDir)`
|
|
20
|
+
* succeeded → skipped seeding → `listPendingUnlocked` saw "workspace has
|
|
21
|
+
* files, backup is empty" and produced phantom `added` entries for every
|
|
22
|
+
* cloned file.
|
|
23
|
+
*
|
|
24
|
+
* Fix: stage into `<backupDir>.seed-<rand>`, then `fs.rename` to commit.
|
|
25
|
+
* Either the fully-seeded backup appears at its canonical path, or
|
|
26
|
+
* nothing does and the next caller retries from scratch. Same pattern
|
|
27
|
+
* applies to `reseedUnlocked` (branch-switch hook).
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const TEST_BRANCH = 'seed-test-branch';
|
|
31
|
+
|
|
32
|
+
interface Fixture {
|
|
33
|
+
workspacesRoot: string;
|
|
34
|
+
backupsRoot: string;
|
|
35
|
+
workspaceDir: string;
|
|
36
|
+
repoDir: string;
|
|
37
|
+
workspaceId: string;
|
|
38
|
+
workspaceService: WorkspaceService;
|
|
39
|
+
diffService: DiffService;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function mkTmpRoot(prefix: string): Promise<string> {
|
|
43
|
+
return fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function setup(): Promise<Fixture> {
|
|
47
|
+
const workspacesRoot = await mkTmpRoot('bevel-seed-ws-');
|
|
48
|
+
const backupsRoot = await mkTmpRoot('bevel-seed-backup-');
|
|
49
|
+
const workspaceId = workspaceIdForBranch(TEST_BRANCH);
|
|
50
|
+
const workspaceDir = path.join(workspacesRoot, workspaceId);
|
|
51
|
+
const repoDir = path.join(workspaceDir, 'knowledge-base');
|
|
52
|
+
await fs.mkdir(path.join(repoDir, '.git'), { recursive: true });
|
|
53
|
+
const workspaceService = new WorkspaceService(
|
|
54
|
+
workspacesRoot,
|
|
55
|
+
'https://github.com/Bevel-Software/knowledge-base.git',
|
|
56
|
+
'knowledge-base',
|
|
57
|
+
);
|
|
58
|
+
const mutex = new WorkspaceMutex();
|
|
59
|
+
const diffService = new DiffService(
|
|
60
|
+
workspaceService,
|
|
61
|
+
mutex,
|
|
62
|
+
workspacesRoot,
|
|
63
|
+
backupsRoot,
|
|
64
|
+
'knowledge-base',
|
|
65
|
+
);
|
|
66
|
+
workspaceService.setDiffService(diffService);
|
|
67
|
+
return { workspacesRoot, backupsRoot, workspaceDir, repoDir, workspaceId, workspaceService, diffService };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function teardown(fx: Fixture): Promise<void> {
|
|
71
|
+
await fs.rm(fx.workspacesRoot, { recursive: true, force: true });
|
|
72
|
+
await fs.rm(fx.backupsRoot, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function listDirEntries(dir: string): Promise<string[]> {
|
|
76
|
+
try {
|
|
77
|
+
return await fs.readdir(dir);
|
|
78
|
+
} catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
describe('DiffService — atomic seed (production regression suite)', () => {
|
|
84
|
+
let fx: Fixture;
|
|
85
|
+
beforeEach(async () => { fx = await setup(); });
|
|
86
|
+
afterEach(async () => { await teardown(fx); });
|
|
87
|
+
|
|
88
|
+
it('a failed seed leaves NO backupDir behind — the next call retries from scratch', async () => {
|
|
89
|
+
// Stub `fs.copyFile` so the inner copy throws partway through. The
|
|
90
|
+
// seed must then clean up its staging dir AND propagate the error
|
|
91
|
+
// without leaving a phantom backup root. Otherwise the next seed
|
|
92
|
+
// would no-op on the empty backupDir and report every file as
|
|
93
|
+
// `added`.
|
|
94
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'a\n', 'utf-8');
|
|
95
|
+
await fs.writeFile(path.join(fx.repoDir, 'b.md'), 'b\n', 'utf-8');
|
|
96
|
+
|
|
97
|
+
const copySpy = vi.spyOn(fs, 'copyFile').mockRejectedValueOnce(new Error('disk full'));
|
|
98
|
+
await expect(fx.diffService.currentSession(fx.workspaceId)).rejects.toThrow(/disk full/);
|
|
99
|
+
copySpy.mockRestore();
|
|
100
|
+
|
|
101
|
+
// The CANONICAL backupDir must not exist.
|
|
102
|
+
const backupDir = path.join(fx.backupsRoot, fx.workspaceId);
|
|
103
|
+
await expect(fs.access(backupDir)).rejects.toThrow();
|
|
104
|
+
|
|
105
|
+
// And no staging dirs left behind (the production symptom of the
|
|
106
|
+
// old bug was the directory shell sitting there empty).
|
|
107
|
+
const siblings = await listDirEntries(fx.backupsRoot);
|
|
108
|
+
expect(siblings).toEqual([]);
|
|
109
|
+
|
|
110
|
+
// A follow-up call with the failure cleared seeds cleanly and
|
|
111
|
+
// reports no pending changes.
|
|
112
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
113
|
+
expect(session).toBeNull();
|
|
114
|
+
expect(await fs.readFile(path.join(backupDir, 'knowledge-base', 'a.md'), 'utf-8')).toBe('a\n');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('two concurrent seeds against the same workspace produce a single backup with no phantom pending', async () => {
|
|
118
|
+
// Mutex-serialised in practice, but we exercise the concurrency
|
|
119
|
+
// anyway so a future refactor that drops the mutex still has to
|
|
120
|
+
// keep the seed atomic. Both callers should observe `session === null`
|
|
121
|
+
// (workspace == backup); neither should see phantom `added` entries.
|
|
122
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'foo\n', 'utf-8');
|
|
123
|
+
await fs.writeFile(path.join(fx.repoDir, 'bar.md'), 'bar\n', 'utf-8');
|
|
124
|
+
|
|
125
|
+
const [s1, s2] = await Promise.all([
|
|
126
|
+
fx.diffService.currentSession(fx.workspaceId),
|
|
127
|
+
fx.diffService.currentSession(fx.workspaceId),
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
expect(s1).toBeNull();
|
|
131
|
+
expect(s2).toBeNull();
|
|
132
|
+
|
|
133
|
+
// Backup is fully populated.
|
|
134
|
+
const backupRepo = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base');
|
|
135
|
+
expect(await fs.readFile(path.join(backupRepo, 'foo.md'), 'utf-8')).toBe('foo\n');
|
|
136
|
+
expect(await fs.readFile(path.join(backupRepo, 'bar.md'), 'utf-8')).toBe('bar\n');
|
|
137
|
+
|
|
138
|
+
// No stray staging dirs.
|
|
139
|
+
const entries = await listDirEntries(fx.backupsRoot);
|
|
140
|
+
expect(entries).toEqual([fx.workspaceId]);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('reseed (branch-switch hook) leaves the previous backup intact when the new copy fails', async () => {
|
|
144
|
+
// The branch-switch reseed wipes the existing backup and replaces it
|
|
145
|
+
// with a fresh copy of the new branch's working tree. If the copy
|
|
146
|
+
// fails mid-stream, we must NOT vaporise the old backup — the user
|
|
147
|
+
// would otherwise see every file on the OLD branch flip to pending.
|
|
148
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'before\n', 'utf-8');
|
|
149
|
+
// Seed the initial backup.
|
|
150
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
151
|
+
const backupFile = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base', 'a.md');
|
|
152
|
+
expect(await fs.readFile(backupFile, 'utf-8')).toBe('before\n');
|
|
153
|
+
|
|
154
|
+
// Disk advances (e.g. branch checkout swapped the bytes), then reseed.
|
|
155
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'after\n', 'utf-8');
|
|
156
|
+
const copySpy = vi.spyOn(fs, 'copyFile').mockRejectedValueOnce(new Error('disk full'));
|
|
157
|
+
await expect(fx.diffService.resetBackup(fx.workspaceId)).rejects.toThrow(/disk full/);
|
|
158
|
+
copySpy.mockRestore();
|
|
159
|
+
|
|
160
|
+
// The OLD backup is still there. `listPendingUnlocked` sees `after`
|
|
161
|
+
// on disk vs `before` in backup → one modified entry (correct
|
|
162
|
+
// behavior because the new bytes haven't been promoted yet).
|
|
163
|
+
expect(await fs.readFile(backupFile, 'utf-8')).toBe('before\n');
|
|
164
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
165
|
+
expect(session?.changes.length).toBe(1);
|
|
166
|
+
expect(session!.changes[0]).toMatchObject({
|
|
167
|
+
path: 'knowledge-base/a.md',
|
|
168
|
+
kind: 'modified',
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('reseed succeeds: old backup vaporised, new backup matches new disk state, session is clean', async () => {
|
|
173
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v1\n', 'utf-8');
|
|
174
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
175
|
+
// Branch switched (simulated): disk now has different bytes.
|
|
176
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v2\n', 'utf-8');
|
|
177
|
+
await fx.diffService.resetBackup(fx.workspaceId);
|
|
178
|
+
|
|
179
|
+
// Backup advanced; session reports no drift.
|
|
180
|
+
const backupFile = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base', 'a.md');
|
|
181
|
+
expect(await fs.readFile(backupFile, 'utf-8')).toBe('v2\n');
|
|
182
|
+
expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { WorkspaceService, workspaceIdForBranch } from '../../workspace/workspace.service.js';
|
|
6
|
+
import { WorkspaceMutex } from '../../workflow/git/mutex.js';
|
|
7
|
+
import { DiffService } from '../diff.service.js';
|
|
8
|
+
|
|
9
|
+
async function mkTmpRoot(prefix: string): Promise<string> {
|
|
10
|
+
return fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Workspace-per-branch (PLAN §3) — the test fixture uses a fake branch
|
|
14
|
+
// `diff-test-branch`. The workspace id is `encodeURIComponent(branch)`
|
|
15
|
+
// and the on-disk path is `<workspacesRoot>/<id>/<kbDirName>/`. We
|
|
16
|
+
// pre-seed the inner `.git` so `WorkspaceService.resolveWorkspaceDir`
|
|
17
|
+
// accepts the path without falling back to the clone-from-origin
|
|
18
|
+
// bootstrap that the unit suite can't perform.
|
|
19
|
+
const TEST_BRANCH = 'diff-test-branch';
|
|
20
|
+
|
|
21
|
+
interface Fixture {
|
|
22
|
+
workspacesRoot: string;
|
|
23
|
+
backupsRoot: string;
|
|
24
|
+
workspaceDir: string;
|
|
25
|
+
repoDir: string;
|
|
26
|
+
workspaceId: string;
|
|
27
|
+
workspaceService: WorkspaceService;
|
|
28
|
+
diffService: DiffService;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function setup(): Promise<Fixture> {
|
|
32
|
+
const workspacesRoot = await mkTmpRoot('bevel-diff-ws-');
|
|
33
|
+
const backupsRoot = await mkTmpRoot('bevel-diff-backup-');
|
|
34
|
+
const workspaceId = workspaceIdForBranch(TEST_BRANCH);
|
|
35
|
+
const workspaceDir = path.join(workspacesRoot, workspaceId);
|
|
36
|
+
const repoDir = path.join(workspaceDir, 'knowledge-base');
|
|
37
|
+
// Inner `.git` dir is the marker `resolveWorkspaceDir` looks for.
|
|
38
|
+
await fs.mkdir(path.join(repoDir, '.git'), { recursive: true });
|
|
39
|
+
const workspaceService = new WorkspaceService(
|
|
40
|
+
workspacesRoot,
|
|
41
|
+
'https://github.com/Bevel-Software/knowledge-base.git',
|
|
42
|
+
'knowledge-base',
|
|
43
|
+
);
|
|
44
|
+
const mutex = new WorkspaceMutex();
|
|
45
|
+
const diffService = new DiffService(
|
|
46
|
+
workspaceService,
|
|
47
|
+
mutex,
|
|
48
|
+
workspacesRoot,
|
|
49
|
+
backupsRoot,
|
|
50
|
+
'knowledge-base',
|
|
51
|
+
);
|
|
52
|
+
workspaceService.setDiffService(diffService);
|
|
53
|
+
return { workspacesRoot, backupsRoot, workspaceDir, repoDir, workspaceId, workspaceService, diffService };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function teardown(fx: Fixture): Promise<void> {
|
|
57
|
+
await fs.rm(fx.workspacesRoot, { recursive: true, force: true });
|
|
58
|
+
await fs.rm(fx.backupsRoot, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe('DiffService', () => {
|
|
62
|
+
let fx: Fixture;
|
|
63
|
+
beforeEach(async () => { fx = await setup(); });
|
|
64
|
+
afterEach(async () => { await teardown(fx); });
|
|
65
|
+
|
|
66
|
+
it('seeds the backup folder lazily on first session read and reports no drift on a clean tree', async () => {
|
|
67
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), '# Hello\n', 'utf-8');
|
|
68
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
69
|
+
// Nothing pending — disk == backup after seed.
|
|
70
|
+
expect(session).toBeNull();
|
|
71
|
+
// Backup file was written.
|
|
72
|
+
const backupPath = path.join(fx.backupsRoot, fx.workspaceId, 'knowledge-base', 'foo.md');
|
|
73
|
+
expect(await fs.readFile(backupPath, 'utf-8')).toBe('# Hello\n');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('surfaces an agent-created untracked file as added (the juju.md regression)', async () => {
|
|
77
|
+
// First session call seeds with no diffable files.
|
|
78
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
79
|
+
// Agent writes a new file directly to disk (bypassing WorkspaceService).
|
|
80
|
+
await fs.writeFile(path.join(fx.repoDir, 'juju.md'), '# new\n', 'utf-8');
|
|
81
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
82
|
+
expect(session?.changes.length).toBe(1);
|
|
83
|
+
expect(session!.changes[0]).toMatchObject({
|
|
84
|
+
path: 'knowledge-base/juju.md',
|
|
85
|
+
kind: 'added',
|
|
86
|
+
isBinary: false,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('user-saved edit through WorkspaceService advances the backup so it does NOT show as pending', async () => {
|
|
91
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'before\n', 'utf-8');
|
|
92
|
+
await fx.diffService.currentSession(fx.workspaceId); // seed
|
|
93
|
+
// User edit goes through WorkspaceService → triggers diffService.syncFromDisk.
|
|
94
|
+
await fx.workspaceService.writeFile(fx.workspaceId, 'knowledge-base/foo.md', 'after\n');
|
|
95
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
96
|
+
expect(session).toBeNull(); // backup advanced; no drift
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('detects a modified file (agent edit on top of seeded backup)', async () => {
|
|
100
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'before\n', 'utf-8');
|
|
101
|
+
await fx.diffService.currentSession(fx.workspaceId); // seed
|
|
102
|
+
// Agent edits via shell — disk only.
|
|
103
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'after\n', 'utf-8');
|
|
104
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
105
|
+
expect(session?.changes[0]).toMatchObject({
|
|
106
|
+
path: 'knowledge-base/foo.md',
|
|
107
|
+
kind: 'modified',
|
|
108
|
+
linesAdded: 1,
|
|
109
|
+
linesRemoved: 1,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('acceptOne promotes disk → backup so the next session is clean', async () => {
|
|
114
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v1\n', 'utf-8');
|
|
115
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
116
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v2\n', 'utf-8');
|
|
117
|
+
expect((await fx.diffService.currentSession(fx.workspaceId))?.changes.length).toBe(1);
|
|
118
|
+
await fx.diffService.acceptOne(fx.workspaceId, 'knowledge-base/foo.md');
|
|
119
|
+
expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('acceptAll with an allow-list accepts only those paths in ONE batch; the rest stay pending', async () => {
|
|
123
|
+
// The accept route passes the caller's READABLE paths — a restricted node
|
|
124
|
+
// must stay pending. One service call covers the whole batch (looping
|
|
125
|
+
// acceptOne per path re-walks the workspace per file, O(changes × files)).
|
|
126
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v1\n', 'utf-8');
|
|
127
|
+
await fs.writeFile(path.join(fx.repoDir, 'b.md'), 'v1\n', 'utf-8');
|
|
128
|
+
await fx.diffService.currentSession(fx.workspaceId); // seed
|
|
129
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v2\n', 'utf-8');
|
|
130
|
+
await fs.writeFile(path.join(fx.repoDir, 'b.md'), 'v2\n', 'utf-8');
|
|
131
|
+
expect((await fx.diffService.currentSession(fx.workspaceId))?.changes.length).toBe(2);
|
|
132
|
+
await fx.diffService.acceptAll(fx.workspaceId, ['knowledge-base/a.md']);
|
|
133
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
134
|
+
expect(session?.changes.map((c) => c.path)).toEqual(['knowledge-base/b.md']);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('acceptAll without an allow-list accepts everything', async () => {
|
|
138
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v1\n', 'utf-8');
|
|
139
|
+
await fx.diffService.currentSession(fx.workspaceId); // seed
|
|
140
|
+
await fs.writeFile(path.join(fx.repoDir, 'a.md'), 'v2\n', 'utf-8');
|
|
141
|
+
await fx.diffService.acceptAll(fx.workspaceId);
|
|
142
|
+
expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('rejectOne restores disk ← backup', async () => {
|
|
146
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v1\n', 'utf-8');
|
|
147
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
148
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v2\n', 'utf-8');
|
|
149
|
+
await fx.diffService.rejectOne(fx.workspaceId, 'knowledge-base/foo.md');
|
|
150
|
+
expect(await fs.readFile(path.join(fx.repoDir, 'foo.md'), 'utf-8')).toBe('v1\n');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('rejectOne on an agent-created file removes it from disk', async () => {
|
|
154
|
+
await fx.diffService.currentSession(fx.workspaceId); // seed (empty)
|
|
155
|
+
await fs.writeFile(path.join(fx.repoDir, 'created.md'), 'agent\n', 'utf-8');
|
|
156
|
+
await fx.diffService.rejectOne(fx.workspaceId, 'knowledge-base/created.md');
|
|
157
|
+
await expect(fs.access(path.join(fx.repoDir, 'created.md'))).rejects.toThrow();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('non-diffable extensions never enter the pending list', async () => {
|
|
161
|
+
await fs.writeFile(path.join(fx.repoDir, 'image.png'), Buffer.from([0xff, 0xd8, 0xff]));
|
|
162
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
163
|
+
// Agent writes another binary.
|
|
164
|
+
await fs.writeFile(path.join(fx.repoDir, 'other.png'), Buffer.from([0x89, 0x50, 0x4e]));
|
|
165
|
+
expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('resetBackup wipes + reseeds (branch-switch hook): pending agent change disappears', async () => {
|
|
169
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v1\n', 'utf-8');
|
|
170
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
171
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'v2\n', 'utf-8');
|
|
172
|
+
expect((await fx.diffService.currentSession(fx.workspaceId))?.changes.length).toBe(1);
|
|
173
|
+
await fx.diffService.resetBackup(fx.workspaceId);
|
|
174
|
+
expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('rejects path-traversal attempts (must use a diffable extension to even reach the path check)', async () => {
|
|
178
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'x', 'utf-8');
|
|
179
|
+
await fx.diffService.currentSession(fx.workspaceId);
|
|
180
|
+
await expect(
|
|
181
|
+
fx.diffService.acceptOne(fx.workspaceId, '../../../etc/passwd.md'),
|
|
182
|
+
).rejects.toThrow(/Path traversal/);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('reports kind=deleted when the user deletes a file via WorkspaceService — backup drops, no phantom', async () => {
|
|
186
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'x', 'utf-8');
|
|
187
|
+
await fx.diffService.currentSession(fx.workspaceId); // seeds backup with foo.md
|
|
188
|
+
await fx.workspaceService.deleteFile(fx.workspaceId, 'knowledge-base/foo.md');
|
|
189
|
+
expect(await fx.diffService.currentSession(fx.workspaceId)).toBeNull();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('agent-deleted file (disk-rm without WorkspaceService) shows as kind=deleted', async () => {
|
|
193
|
+
await fs.writeFile(path.join(fx.repoDir, 'foo.md'), 'x\n', 'utf-8');
|
|
194
|
+
await fx.diffService.currentSession(fx.workspaceId); // seed
|
|
195
|
+
await fs.rm(path.join(fx.repoDir, 'foo.md'));
|
|
196
|
+
const session = await fx.diffService.currentSession(fx.workspaceId);
|
|
197
|
+
expect(session?.changes[0]).toMatchObject({
|
|
198
|
+
path: 'knowledge-base/foo.md',
|
|
199
|
+
kind: 'deleted',
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
export function assertWithinDirectory(absolutePath: string, dir: string): void {
|
|
4
|
+
const resolved = path.resolve(absolutePath);
|
|
5
|
+
const root = path.resolve(dir);
|
|
6
|
+
if (!resolved.startsWith(root + path.sep) && resolved !== root) {
|
|
7
|
+
throw new Error('Path traversal detected');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File extensions that participate in diff review.
|
|
3
|
+
* Only these extensions get backup copies under <backupsRoot>/<userId>/...
|
|
4
|
+
* and surface in the pending-changes list.
|
|
5
|
+
*/
|
|
6
|
+
export const DIFFABLE_EXTENSIONS: ReadonlySet<string> = new Set(['.md']);
|
|
7
|
+
|
|
8
|
+
export function isDiffable(filePath: string): boolean {
|
|
9
|
+
const dot = filePath.lastIndexOf('.');
|
|
10
|
+
if (dot < 0) return false;
|
|
11
|
+
return DIFFABLE_EXTENSIONS.has(filePath.slice(dot).toLowerCase());
|
|
12
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { FileDiffPayload, ReviewSession } from '@bevel-software/platform-shared';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shape of the diff service. Backups live in a sibling root that mirrors the
|
|
5
|
+
* workspaces layout: each user/repo has `<backupsRoot>/<userId>/<repoName>/...`
|
|
6
|
+
* holding the "last user-confirmed state" of every diffable file. Drift between
|
|
7
|
+
* disk and backup is the pending-change set.
|
|
8
|
+
*
|
|
9
|
+
* Triggers that update the backup:
|
|
10
|
+
* - First-time mount (lazy via `currentSession`) seeds from disk.
|
|
11
|
+
* - User write/delete/rename through WorkspaceService → `syncFromDisk` /
|
|
12
|
+
* `markUserDeleted`.
|
|
13
|
+
* - User accept/reject of pending changes.
|
|
14
|
+
*
|
|
15
|
+
* Under the per-branch workspace model each branch has its own clone +
|
|
16
|
+
* its own backup folder, both seeded at bootstrap, so the legacy
|
|
17
|
+
* "branch switch wipes + reseeds the backup" trigger is gone. `resetBackup`
|
|
18
|
+
* + `resetBackupUnlocked` are still on the interface as primitives but
|
|
19
|
+
* have **no production callers** today — kept for potential future
|
|
20
|
+
* mutex-holding use cases.
|
|
21
|
+
*
|
|
22
|
+
* Agent edits, agent shell commits, and `git pull/merge/fetch/commit/push`
|
|
23
|
+
* leave the backup alone — the resulting drift is what the user reviews.
|
|
24
|
+
*/
|
|
25
|
+
export interface IDiffService {
|
|
26
|
+
/** Wipe the backup folder and reseed from the current working tree. */
|
|
27
|
+
resetBackup(workspaceId: string): Promise<void>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Same as `resetBackup` but assumes the caller is already holding the
|
|
31
|
+
* workspace mutex (via `WorkspaceMutex.run`). Originally the post-switch
|
|
32
|
+
* backup-reseed hook for `GitService.switchBranch`; that caller is gone
|
|
33
|
+
* with the per-branch workspace model (each branch has its own workspace
|
|
34
|
+
* + its own backup, seeded at bootstrap), so this currently has no
|
|
35
|
+
* production callers — kept in the interface as a primitive for future
|
|
36
|
+
* mutex-holding callers that need a backup reset.
|
|
37
|
+
*/
|
|
38
|
+
resetBackupUnlocked(workspaceId: string): Promise<void>;
|
|
39
|
+
|
|
40
|
+
/** Pending changes for the current branch, or null when none. */
|
|
41
|
+
currentSession(workspaceId: string): Promise<ReviewSession | null>;
|
|
42
|
+
|
|
43
|
+
/** Diff payload (baseline + current) for one path. */
|
|
44
|
+
fileDiff(workspaceId: string, relativePath: string): Promise<FileDiffPayload>;
|
|
45
|
+
|
|
46
|
+
/** Promote disk → backup for `relativePath`. Handles file or directory. */
|
|
47
|
+
syncFromDisk(workspaceId: string, relativePath: string): Promise<void>;
|
|
48
|
+
|
|
49
|
+
/** Drop backup entry for `relativePath` (file or dir, recursive). */
|
|
50
|
+
markUserDeleted(workspaceId: string, relativePath: string): Promise<void>;
|
|
51
|
+
|
|
52
|
+
/** Accept one pending path: backup ← disk (or drop backup if disk-gone). */
|
|
53
|
+
acceptOne(workspaceId: string, relativePath: string): Promise<ReviewSession | null>;
|
|
54
|
+
|
|
55
|
+
/** Reject one pending path: disk ← backup (or rm disk if backup-gone). */
|
|
56
|
+
rejectOne(workspaceId: string, relativePath: string): Promise<ReviewSession | null>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Compute the batch revert plan for `paths` WITHOUT mutating anything: each
|
|
60
|
+
* diffable path resolves to either a write of its pre-agent baseline bytes
|
|
61
|
+
* (backup exists) or a delete (agent-added file, no backup). Also returns
|
|
62
|
+
* the workspace dir so the caller can run the plan through a
|
|
63
|
+
* `LockingFilesystem.writeFiles` batch — one lock sweep, one commit + push.
|
|
64
|
+
* Non-diffable paths are skipped.
|
|
65
|
+
*/
|
|
66
|
+
revertPlan(
|
|
67
|
+
workspaceId: string,
|
|
68
|
+
paths: string[],
|
|
69
|
+
): Promise<{ workspaceDir: string; writes: { path: string; content: Buffer }[]; deletes: string[] }>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Accept every pending path in ONE batch (single mutex hold, single
|
|
73
|
+
* pending-list walk). `paths` optionally restricts the batch to an
|
|
74
|
+
* allow-list — the accept route passes the caller's readable paths so
|
|
75
|
+
* restricted nodes stay pending.
|
|
76
|
+
*/
|
|
77
|
+
acceptAll(workspaceId: string, paths?: string[]): Promise<void>;
|
|
78
|
+
}
|