@bevel-software/platform-core-backend 0.14.0 → 0.15.1
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/THIRD-PARTY-NOTICES.md +6 -6
- package/dist/core/catalog-cache-invalidation.d.ts +29 -0
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -0
- package/dist/core/catalog-cache-invalidation.js +71 -0
- package/dist/core/catalog-cache-invalidation.js.map +1 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +99 -8
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +18 -1
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +96 -43
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +18 -6
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +26 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +213 -71
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +59 -7
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +51 -1
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +95 -1
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/group-files.d.ts.map +1 -1
- package/dist/modules/access-model/group-files.js +4 -1
- package/dist/modules/access-model/group-files.js.map +1 -1
- package/dist/modules/access-model/plugin-principals.d.ts +49 -0
- package/dist/modules/access-model/plugin-principals.d.ts.map +1 -0
- package/dist/modules/access-model/plugin-principals.js +125 -0
- package/dist/modules/access-model/plugin-principals.js.map +1 -0
- package/dist/modules/database/core-schema.d.ts +326 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +52 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/kb-sync/index.d.ts +6 -0
- package/dist/modules/kb-sync/index.d.ts.map +1 -0
- package/dist/modules/kb-sync/index.js +5 -0
- package/dist/modules/kb-sync/index.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +77 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.js +2 -0
- package/dist/modules/kb-sync/kb-sync.interface.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts +70 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.js +135 -0
- package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts +45 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.js +183 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -0
- package/dist/modules/kb-sync/sync-auth.d.ts +59 -0
- package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-auth.js +62 -0
- package/dist/modules/kb-sync/sync-auth.js.map +1 -0
- package/dist/modules/kb-sync/sync-payload.d.ts +32 -0
- package/dist/modules/kb-sync/sync-payload.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-payload.js +105 -0
- package/dist/modules/kb-sync/sync-payload.js.map +1 -0
- package/dist/modules/marketplace/git-http.routes.d.ts +42 -0
- package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/git-http.routes.js +190 -0
- package/dist/modules/marketplace/git-http.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +23 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +59 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts +59 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js +96 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +99 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +147 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts +47 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js +228 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +109 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js +172 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/index.d.ts +6 -0
- package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/index.js +6 -0
- package/dist/modules/marketplace/github-facade/index.js.map +1 -0
- package/dist/modules/marketplace/index.d.ts +4 -0
- package/dist/modules/marketplace/index.d.ts.map +1 -0
- package/dist/modules/marketplace/index.js +4 -0
- package/dist/modules/marketplace/index.js.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts +122 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.js +300 -0
- package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +4 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +14 -8
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +13 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.d.ts +13 -2
- package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.js +3 -1
- package/dist/modules/mcp/oauth/oauth-state.js.map +1 -1
- package/dist/modules/plugins/compile/compile-marketplace.d.ts +97 -0
- package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -0
- package/dist/modules/plugins/compile/compile-marketplace.js +240 -0
- package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +66 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +132 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +37 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +135 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +45 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js +147 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +37 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js +107 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -0
- package/dist/modules/plugins/discovery/native.source.d.ts +15 -0
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/native.source.js +102 -0
- package/dist/modules/plugins/discovery/native.source.js.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts +96 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.js +2 -0
- package/dist/modules/plugins/discovery/plugin-source.js.map +1 -0
- package/dist/modules/plugins/index.d.ts +10 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +8 -0
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +66 -0
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.js +160 -0
- package/dist/modules/plugins/plugin-links.js.map +1 -0
- package/dist/modules/plugins/plugin-links.service.d.ts +114 -0
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.service.js +304 -0
- package/dist/modules/plugins/plugin-links.service.js.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts +39 -3
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +152 -52
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +69 -0
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-rename.service.js +245 -0
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -0
- package/dist/modules/plugins/plugins.contract.d.ts +27 -1
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +7 -1
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +120 -12
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +50 -3
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +108 -47
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +9 -0
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +82 -2
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +26 -0
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +9 -1
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/index.d.ts +1 -0
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +9 -4
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +38 -0
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -0
- package/dist/modules/skills/skill-access-requests.routes.js +168 -0
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -0
- package/dist/modules/skills/skills.contract.d.ts +31 -0
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +28 -3
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +42 -3
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +4 -0
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +83 -22
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +18 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.js +2 -1
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +13 -4
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +26 -9
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +36 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +130 -92
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +6 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +18 -25
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +40 -2
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +227 -54
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts +6 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +8 -0
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +52 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +242 -9
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +35 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +121 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +46 -2
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +112 -25
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +9 -9
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +33 -0
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +110 -16
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +20 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +29 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/fs-errors.d.ts +8 -0
- package/dist/shared/fs-errors.d.ts.map +1 -0
- package/dist/shared/fs-errors.js +11 -0
- package/dist/shared/fs-errors.js.map +1 -0
- package/dist/shared/fs-walk.d.ts +20 -6
- package/dist/shared/fs-walk.d.ts.map +1 -1
- package/dist/shared/fs-walk.js +35 -27
- package/dist/shared/fs-walk.js.map +1 -1
- package/dist/shared/kb-walk.d.ts +52 -0
- package/dist/shared/kb-walk.d.ts.map +1 -0
- package/dist/shared/kb-walk.js +76 -0
- package/dist/shared/kb-walk.js.map +1 -0
- package/dist/shared/path-order.d.ts +18 -0
- package/dist/shared/path-order.d.ts.map +1 -0
- package/dist/shared/path-order.js +35 -0
- package/dist/shared/path-order.js.map +1 -0
- package/dist/shared/ttl-cache.d.ts +28 -1
- package/dist/shared/ttl-cache.d.ts.map +1 -1
- package/dist/shared/ttl-cache.js +33 -1
- package/dist/shared/ttl-cache.js.map +1 -1
- package/kb-template/.bevelignore +1 -1
- package/kb-template/AGENTS.md +95 -30
- package/kb-template/Skills/.gitkeep +0 -0
- package/migrations/0005_github_facade.sql +25 -0
- package/migrations/meta/0005_snapshot.json +1717 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +6 -6
- package/src/__tests__/kb-layout-config.test.ts +110 -0
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +146 -0
- package/src/core/catalog-cache-invalidation.ts +92 -0
- package/src/core/create-core-server.ts +123 -7
- package/src/core/create-core-services.ts +162 -41
- package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +86 -0
- package/src/modules/access/__tests__/access-control.service.test.ts +19 -3
- package/src/modules/access/__tests__/access-plugin-principals.test.ts +368 -0
- package/src/modules/access/__tests__/groups-admin.service.test.ts +8 -0
- package/src/modules/access/access-control.interface.ts +20 -9
- package/src/modules/access/access-control.service.ts +240 -78
- package/src/modules/access/access.routes.ts +96 -14
- package/src/modules/access-model/__tests__/access-grammar.test.ts +18 -0
- package/src/modules/access-model/__tests__/plugin-token-canonical.test.ts +40 -0
- package/src/modules/access-model/access-grammar.ts +114 -2
- package/src/modules/access-model/group-files.ts +4 -0
- package/src/modules/access-model/plugin-principals.ts +137 -0
- package/src/modules/database/core-schema.ts +54 -0
- package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +297 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +295 -0
- package/src/modules/kb-sync/__tests__/sync-auth.test.ts +118 -0
- package/src/modules/kb-sync/__tests__/sync-payload.test.ts +148 -0
- package/src/modules/kb-sync/index.ts +19 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +79 -0
- package/src/modules/kb-sync/kb-sync.routes.ts +197 -0
- package/src/modules/kb-sync/kb-sync.service.ts +201 -0
- package/src/modules/kb-sync/sync-auth.ts +118 -0
- package/src/modules/kb-sync/sync-payload.ts +143 -0
- package/src/modules/marketplace/__tests__/github-facade.test.ts +443 -0
- package/src/modules/marketplace/__tests__/marketplace-git.test.ts +179 -0
- package/src/modules/marketplace/git-http.routes.ts +193 -0
- package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +78 -0
- package/src/modules/marketplace/github-facade/github-facade-codes.store.ts +145 -0
- package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +208 -0
- package/src/modules/marketplace/github-facade/github-facade.routes.ts +268 -0
- package/src/modules/marketplace/github-facade/github-facade.service.ts +221 -0
- package/src/modules/marketplace/github-facade/index.ts +25 -0
- package/src/modules/marketplace/index.ts +3 -0
- package/src/modules/marketplace/marketplace-repo.service.ts +337 -0
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -0
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +24 -2
- package/src/modules/mcp/oauth/oauth-state.ts +15 -3
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +163 -0
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +294 -0
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +56 -12
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +287 -0
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +205 -5
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +394 -0
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +80 -33
- package/src/modules/plugins/compile/compile-marketplace.ts +403 -0
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +150 -0
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +329 -0
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +140 -0
- package/src/modules/plugins/discovery/bundle-dialect/registry.ts +175 -0
- package/src/modules/plugins/discovery/kb-plugin-source.ts +114 -0
- package/src/modules/plugins/discovery/native.source.ts +122 -0
- package/src/modules/plugins/discovery/plugin-source.ts +99 -0
- package/src/modules/plugins/index.ts +10 -0
- package/src/modules/plugins/plugin-links.service.ts +363 -0
- package/src/modules/plugins/plugin-links.ts +184 -0
- package/src/modules/plugins/plugin-provision.service.ts +160 -54
- package/src/modules/plugins/plugin-rename.service.ts +298 -0
- package/src/modules/plugins/plugins.contract.ts +28 -1
- package/src/modules/plugins/plugins.routes.ts +128 -11
- package/src/modules/plugins/plugins.service.ts +106 -53
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +57 -0
- package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +77 -0
- package/src/modules/settings/deployment-settings.service.ts +95 -2
- package/src/modules/settings/setup.routes.ts +24 -0
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +128 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +85 -0
- package/src/modules/skills/index.ts +1 -0
- package/src/modules/skills/pending-skills.service.ts +7 -5
- package/src/modules/skills/skill-access-requests.routes.ts +199 -0
- package/src/modules/skills/skills.contract.ts +32 -0
- package/src/modules/skills/skills.routes.ts +59 -3
- package/src/modules/skills/skills.service.ts +84 -22
- package/src/modules/tool-auth/external-api-key.interface.ts +20 -1
- package/src/modules/tool-auth/external-api-key.service.ts +31 -13
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +24 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +2 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +154 -88
- package/src/modules/tool-manuals/tool-manuals.service.ts +14 -23
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +1 -1
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +1 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +2 -2
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +119 -2
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +437 -0
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +47 -4
- package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +253 -0
- package/src/modules/workflow/git/git.service.ts +239 -56
- package/src/modules/workflow/git/pull-request.service.ts +9 -0
- package/src/modules/workflow/workflow.service.ts +260 -9
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +6 -7
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +93 -0
- package/src/modules/workspace/__tests__/workspace.service.missing-branch.test.ts +28 -0
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +42 -12
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +219 -4
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +129 -0
- package/src/modules/workspace/startup/steps/seed-tree.ts +44 -2
- package/src/modules/workspace/startup/steps/template-files.step.ts +131 -27
- package/src/modules/workspace/workspace.routes.ts +9 -17
- package/src/modules/workspace/workspace.service.ts +107 -16
- package/src/shared/__tests__/filename.test.ts +0 -0
- package/src/shared/__tests__/fs-walk.test.ts +22 -1
- package/src/shared/__tests__/kb-walk.test.ts +107 -0
- package/src/shared/__tests__/path-order.test.ts +24 -0
- package/src/shared/__tests__/ttl-cache.test.ts +55 -0
- package/src/shared/domain-errors.ts +29 -0
- package/src/shared/fs-errors.ts +10 -0
- package/src/shared/fs-walk.ts +38 -23
- package/src/shared/kb-walk.ts +95 -0
- package/src/shared/path-order.ts +33 -0
- package/src/shared/ttl-cache.ts +33 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import type { AuthProviderPlugin } from '../modules/auth/auth.routes.js';
|
|
2
3
|
import type { AuthUser } from '@bevel-software/platform-shared';
|
|
3
4
|
import {
|
|
@@ -5,7 +6,7 @@ import {
|
|
|
5
6
|
configureBranchModel,
|
|
6
7
|
validateBranchModel,
|
|
7
8
|
PROTECTED_BRANCHES,
|
|
8
|
-
|
|
9
|
+
configureKbLayout,
|
|
9
10
|
} from '@bevel-software/platform-shared';
|
|
10
11
|
import { CoreConfig } from '../core-config.js';
|
|
11
12
|
import { getDb, type Database } from '../modules/database/connection.js';
|
|
@@ -15,10 +16,20 @@ import { WorkspaceService } from '../modules/workspace/workspace.service.js';
|
|
|
15
16
|
import { RoutineWritePolicyService } from '../modules/workspace/routine-write-policy.js';
|
|
16
17
|
import { KbStartupRunner } from '../modules/workspace/startup/kb-startup-runner.js';
|
|
17
18
|
import { GroupsToPluginsStep } from '../modules/workspace/startup/steps/groups-to-plugins.step.js';
|
|
19
|
+
import { PluginManifestsStep } from '../modules/workspace/startup/steps/plugin-manifests.step.js';
|
|
18
20
|
import { TemplateFilesStep } from '../modules/workspace/startup/steps/template-files.step.js';
|
|
19
21
|
import { RolesYamlStep } from '../modules/workspace/startup/steps/roles-yaml.step.js';
|
|
20
22
|
import { buildSeedTree } from '../modules/workspace/startup/steps/seed-tree.js';
|
|
21
23
|
import { DeploymentSettingsService } from '../modules/settings/deployment-settings.service.js';
|
|
24
|
+
import { KbSyncService } from '../modules/kb-sync/kb-sync.service.js';
|
|
25
|
+
|
|
26
|
+
/** The hosted MCP endpoint at a deployment address, with any userinfo stripped. */
|
|
27
|
+
function mcpEndpointUrl(publicBackendUrl: string): string {
|
|
28
|
+
const url = new URL('/api/mcp', publicBackendUrl);
|
|
29
|
+
url.username = '';
|
|
30
|
+
url.password = '';
|
|
31
|
+
return url.toString();
|
|
32
|
+
}
|
|
22
33
|
|
|
23
34
|
/** `a.com, b.com` → `['a.com','b.com']`, tolerating a leading `@` or `.`. */
|
|
24
35
|
function parseDomainList(raw: string): string[] {
|
|
@@ -40,7 +51,26 @@ import { GroupsAdminService } from '../modules/access/groups-admin.service.js';
|
|
|
40
51
|
import { PendingSkillsService, SkillService } from '../modules/skills/index.js';
|
|
41
52
|
import { ToolManualService } from '../modules/tool-manuals/index.js';
|
|
42
53
|
import { McpServerEditService } from '../modules/tool-manuals/mcp-server-edit.service.js';
|
|
43
|
-
import {
|
|
54
|
+
import {
|
|
55
|
+
PluginIndexService,
|
|
56
|
+
PluginProvisionService,
|
|
57
|
+
JoinRequestsService,
|
|
58
|
+
PluginLinkIndex,
|
|
59
|
+
PluginLinksService,
|
|
60
|
+
PluginRenameService,
|
|
61
|
+
MarketplaceCompilerService,
|
|
62
|
+
KbPluginSource,
|
|
63
|
+
} from '../modules/plugins/index.js';
|
|
64
|
+
import { MarketplaceRepoService } from '../modules/marketplace/index.js';
|
|
65
|
+
import {
|
|
66
|
+
GitHubFacadeCredentialsService,
|
|
67
|
+
GitHubFacade,
|
|
68
|
+
DbGitHubFacadeCodeStore,
|
|
69
|
+
DbGitHubFacadeCredentialsStore,
|
|
70
|
+
CLAUDE_CONSUMER,
|
|
71
|
+
GITHUB_LINK_KEY_KIND,
|
|
72
|
+
GITHUB_LINK_KEY_PREFIX,
|
|
73
|
+
} from '../modules/marketplace/github-facade/index.js';
|
|
44
74
|
import {
|
|
45
75
|
DbSecretsVaultService,
|
|
46
76
|
McpOAuthDiscoveryService,
|
|
@@ -94,6 +124,7 @@ import { TokenCrypto } from '../shared/token-crypto.js';
|
|
|
94
124
|
import { UpdateCheckService } from '../modules/update-check/update-check.service.js';
|
|
95
125
|
import { resolveAppVersion } from '../version.js';
|
|
96
126
|
import { noopRecoveryAgent, type CorePorts } from './core-ports.js';
|
|
127
|
+
import { registerCatalogCacheInvalidation } from './catalog-cache-invalidation.js';
|
|
97
128
|
|
|
98
129
|
/**
|
|
99
130
|
* Everything the CORE composition builds: the core services `createCoreServer`
|
|
@@ -128,6 +159,18 @@ export interface CoreServices {
|
|
|
128
159
|
pluginIndexService: PluginIndexService;
|
|
129
160
|
pluginProvisionService: PluginProvisionService;
|
|
130
161
|
joinRequestsService: JoinRequestsService;
|
|
162
|
+
/** Which plugins hold which skills (inline or linked) — see `PluginLinkIndex`. */
|
|
163
|
+
pluginLinkIndex: PluginLinkIndex;
|
|
164
|
+
/** Link / unlink / repair shared skills into plugins. */
|
|
165
|
+
pluginLinksService: PluginLinksService;
|
|
166
|
+
pluginRenameService: PluginRenameService;
|
|
167
|
+
/** Source layout → distribution layout, for one audience. */
|
|
168
|
+
marketplaceCompiler: MarketplaceCompilerService;
|
|
169
|
+
/** The bare repository the per-user marketplace git endpoint serves from. */
|
|
170
|
+
marketplaceRepo: MarketplaceRepoService;
|
|
171
|
+
/** The GitHub Enterprise facade products such as claude.ai add the marketplace through. */
|
|
172
|
+
githubFacade: GitHubFacade;
|
|
173
|
+
githubFacadeCredentials: GitHubFacadeCredentialsService;
|
|
131
174
|
authService: AuthService;
|
|
132
175
|
authMiddleware: ReturnType<typeof createAuthMiddleware>;
|
|
133
176
|
accountErasureService: AccountErasureService;
|
|
@@ -137,6 +180,8 @@ export interface CoreServices {
|
|
|
137
180
|
reviewWorkflowService: ReviewWorkflowService;
|
|
138
181
|
fileLockService: FileLockService;
|
|
139
182
|
workflowService: WorkflowService;
|
|
183
|
+
/** Remote sync (`POST /api/sync`): a git host or pipeline makes the clones catch up. */
|
|
184
|
+
kbSyncService: KbSyncService;
|
|
140
185
|
eventBus: WorkflowEventBus;
|
|
141
186
|
fileChangeNotifier: FileChangeNotifier;
|
|
142
187
|
pendingCommitsService: PendingCommitsService;
|
|
@@ -242,6 +287,11 @@ export async function createCoreServices(
|
|
|
242
287
|
protectedBranches: settings.resolve('protectedBranches'),
|
|
243
288
|
};
|
|
244
289
|
if (!validateBranchModel(branchModel)) configureBranchModel(branchModel);
|
|
290
|
+
// The KB layout — the three renameable roots — applied the same way and at
|
|
291
|
+
// the same moment, before any service captures a root name. Unlike the
|
|
292
|
+
// branch model it always resolves (every root has a default), so an invalid
|
|
293
|
+
// value is a real misconfiguration and stops the boot.
|
|
294
|
+
configureKbLayout(settings.resolveKbLayout());
|
|
245
295
|
// A token supplied through the setup screen has to reach the credential
|
|
246
296
|
// helper, which reads `$GITHUB_TOKEN` at call time.
|
|
247
297
|
settings.syncGitTokenEnv();
|
|
@@ -269,6 +319,7 @@ export async function createCoreServices(
|
|
|
269
319
|
// whatever the distribution appends.
|
|
270
320
|
const kbStartupSteps = [
|
|
271
321
|
new GroupsToPluginsStep(),
|
|
322
|
+
new PluginManifestsStep(),
|
|
272
323
|
new TemplateFilesStep(extraDirs),
|
|
273
324
|
new RolesYamlStep([config.adminEmail]),
|
|
274
325
|
...(ports.kbStartupSteps ?? []),
|
|
@@ -324,17 +375,27 @@ export async function createCoreServices(
|
|
|
324
375
|
// Tool manuals: user-authored `*.tool` files under `Plugins/` in the default
|
|
325
376
|
// branch — access-controlled like Skills, served to external agents via
|
|
326
377
|
// `GET /api/agent/all-tools` and registered on the MCP proxy's UTCP client.
|
|
327
|
-
|
|
378
|
+
// Where plugins come from: one walk of the plugins root that reads every
|
|
379
|
+
// plugin folder in whichever file shape it carries (see
|
|
380
|
+
// modules/plugins/discovery). Nothing to configure, nothing to document.
|
|
381
|
+
const pluginSource = new KbPluginSource();
|
|
382
|
+
const toolManualService = new ToolManualService(workspaceService, accessControl, kbDirName, Date.now, pluginSource);
|
|
328
383
|
// Plugins: the folders under `Plugins/` that carry a
|
|
329
384
|
// team's skills AND the tools they need. Enumerated for EVERY authenticated
|
|
330
385
|
// caller — a plugin they cannot read still exists for them, as a locked one —
|
|
331
386
|
// with the counts read off the two catalogs above rather than a second scan.
|
|
387
|
+
// The link index resolves manifests against the released catalog, and the
|
|
388
|
+
// plugin index counts through it (inline + linked), so it comes first.
|
|
389
|
+
const pluginLinkIndex = new PluginLinkIndex(workspaceService, skillService, accessControl, kbDirName, Date.now, pluginSource);
|
|
332
390
|
const pluginIndexService = new PluginIndexService(
|
|
333
391
|
workspaceService,
|
|
334
392
|
accessControl,
|
|
335
393
|
skillService,
|
|
336
394
|
toolManualService,
|
|
337
395
|
kbDirName,
|
|
396
|
+
Date.now,
|
|
397
|
+
pluginLinkIndex,
|
|
398
|
+
pluginSource,
|
|
338
399
|
);
|
|
339
400
|
// Auth service — resolves identities for login, PR author attribution, and
|
|
340
401
|
// access lookups. (Change requests now store the author email directly, so
|
|
@@ -450,6 +511,62 @@ export async function createCoreServices(
|
|
|
450
511
|
// only needs to read files at refs and to close a request whose proposals
|
|
451
512
|
// have all landed.
|
|
452
513
|
const joinRequestsService = new JoinRequestsService(workspaceService, workflowService);
|
|
514
|
+
// Links land as ordinary default-branch commits and change what the plugin
|
|
515
|
+
// index counts, so a link drops that cache too.
|
|
516
|
+
const pluginLinksService = new PluginLinksService(
|
|
517
|
+
workspaceService,
|
|
518
|
+
workflowService,
|
|
519
|
+
accessControl,
|
|
520
|
+
skillService,
|
|
521
|
+
pluginLinkIndex,
|
|
522
|
+
kbDirName,
|
|
523
|
+
eventBus,
|
|
524
|
+
() => pluginIndexService.invalidate(),
|
|
525
|
+
);
|
|
526
|
+
// A rename rewrites grants across the knowledge base in one batch commit,
|
|
527
|
+
// then drops every cache that keyed on the old identity.
|
|
528
|
+
const pluginRenameService = new PluginRenameService(
|
|
529
|
+
workspaceService,
|
|
530
|
+
workflowService,
|
|
531
|
+
accessControl,
|
|
532
|
+
pluginSource,
|
|
533
|
+
kbDirName,
|
|
534
|
+
eventBus,
|
|
535
|
+
() => {
|
|
536
|
+
pluginIndexService.invalidate();
|
|
537
|
+
pluginLinkIndex.invalidate();
|
|
538
|
+
},
|
|
539
|
+
);
|
|
540
|
+
// Source → distribution. The marketplace's identity is fixed for now; the
|
|
541
|
+
// per-user git endpoint and any mirror both compile through this.
|
|
542
|
+
const marketplaceCompiler = new MarketplaceCompilerService(
|
|
543
|
+
workspaceService,
|
|
544
|
+
accessControl,
|
|
545
|
+
skillService,
|
|
546
|
+
pluginLinkIndex,
|
|
547
|
+
kbDirName,
|
|
548
|
+
{
|
|
549
|
+
name: 'hexis',
|
|
550
|
+
owner: 'Hexis',
|
|
551
|
+
description: 'Skills and plugins from this knowledge base',
|
|
552
|
+
// The knowledge base as a tool, shipped in the skills plugin so an
|
|
553
|
+
// agent installing from the marketplace can read what its skills refer
|
|
554
|
+
// to. The same address `/api/config` and the OAuth metadata publish,
|
|
555
|
+
// userinfo stripped; the client's OAuth flow supplies the identity.
|
|
556
|
+
knowledgeBaseMcp: { name: 'hexis', url: mcpEndpointUrl(config.publicBackendUrl) },
|
|
557
|
+
},
|
|
558
|
+
pluginSource,
|
|
559
|
+
);
|
|
560
|
+
// Sibling of the workspaces root, like the spill store: one bare repo, one
|
|
561
|
+
// git namespace per caller (see marketplace-repo.service.ts).
|
|
562
|
+
const marketplaceRepo = new MarketplaceRepoService(
|
|
563
|
+
path.resolve(config.workspacesRoot, '..', 'marketplace.git'),
|
|
564
|
+
marketplaceCompiler,
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
// Remote sync. Drives the workflow module's per-branch pull-and-announce
|
|
568
|
+
// over every clone the workspace service knows about; owns no git of its own.
|
|
569
|
+
const kbSyncService = new KbSyncService(workflowService, workspaceService);
|
|
453
570
|
// Server-scoped MCP editing — the tool page's edit form. One server's truth
|
|
454
571
|
// spans a plugin's mcp.json AND plugin.json extensions block, and this is
|
|
455
572
|
// the ONE writer that rewrites both entries and commits them together (see
|
|
@@ -486,43 +603,16 @@ export async function createCoreServices(
|
|
|
486
603
|
workflowService,
|
|
487
604
|
);
|
|
488
605
|
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
// default
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
// touch check drives all three caches. An access grant lands as a
|
|
500
|
-
// default-branch change to `Plugins/<plugin>/access.md`, so this is also
|
|
501
|
-
// what makes a newly-granted plugin unlock within one round-trip instead
|
|
502
|
-
// of one TTL.
|
|
503
|
-
const touched = paths.some((p) => p.startsWith(`${kbDirName}/${PLUGINS_DIR}/`));
|
|
504
|
-
if (touched) {
|
|
505
|
-
toolManualService.invalidate();
|
|
506
|
-
skillService.invalidate();
|
|
507
|
-
pluginIndexService.invalidate();
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
// Subscriber B — WRITE-time freshness for the same three caches. The
|
|
513
|
-
// workspace routes emit `fs-tree-changed` the moment bytes hit a working
|
|
514
|
-
// tree; Subscriber A above fires only when the ASYNC commit lands. Between
|
|
515
|
-
// the two, "create a skill, reload the catalog" raced the commit pipeline
|
|
516
|
-
// and lost — the new skill's card stayed invisible until a refresh outlived
|
|
517
|
-
// the TTL. The catalogs scan the working tree anyway, so invalidating at
|
|
518
|
-
// write time makes the very next read see the file. No path filter: this
|
|
519
|
-
// event carries none, and a spurious drop only costs one re-scan.
|
|
520
|
-
eventBus.onEmit((event) => {
|
|
521
|
-
if (event.kind !== 'fs-tree-changed') return;
|
|
522
|
-
if (!('branch' in event) || event.branch !== DEFAULT_BRANCH) return;
|
|
523
|
-
toolManualService.invalidate();
|
|
524
|
-
skillService.invalidate();
|
|
525
|
-
pluginIndexService.invalidate();
|
|
606
|
+
// Catalog freshness: the skill / tool-manual / plugin-index caches all scan
|
|
607
|
+
// the DEFAULT branch's working tree, and all three go stale on the same
|
|
608
|
+
// events (a commit, a working-tree write, a merge). Wired in one place so a
|
|
609
|
+
// new way of reaching the default branch cannot refresh two of them and
|
|
610
|
+
// leave the third serving last minute's answer.
|
|
611
|
+
registerCatalogCacheInvalidation({
|
|
612
|
+
eventBus,
|
|
613
|
+
fileChangeNotifier,
|
|
614
|
+
kbDirName,
|
|
615
|
+
catalogs: [toolManualService, skillService, pluginIndexService, pluginLinkIndex],
|
|
526
616
|
});
|
|
527
617
|
|
|
528
618
|
// Admin = `Admin` role in roles.yaml, resolved through the access model on the
|
|
@@ -609,7 +699,30 @@ export async function createCoreServices(
|
|
|
609
699
|
// GENERIC proxy: per session it discovers the UTCP manual at /api/agent/utcp
|
|
610
700
|
// over loopback and re-exposes every tool, dispatching calls back through the
|
|
611
701
|
// REST tool surface (so agent logic + metering live there, once).
|
|
612
|
-
|
|
702
|
+
// Connection keys also come as GitHub-shaped links (`gho_…`, kind
|
|
703
|
+
// `github-link`): the same key, minted by the marketplace facade below when
|
|
704
|
+
// a person connects an account on claude.ai, told apart by its stored kind.
|
|
705
|
+
const externalApiKeyService = new ExternalApiKeyService(db, config.externalApiKeyPrefix, {
|
|
706
|
+
[GITHUB_LINK_KEY_KIND]: GITHUB_LINK_KEY_PREFIX,
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// The facade that lets products which sync marketplaces only from a GitHub
|
|
710
|
+
// Enterprise Server (claude.ai, Cowork) add the per-user marketplace:
|
|
711
|
+
// generated app credentials an Owner registers once, and the connect flow
|
|
712
|
+
// that turns a hexis session into a connection key the product holds.
|
|
713
|
+
// Reuses the MCP flow's signed state and /connect page — see
|
|
714
|
+
// modules/marketplace/github-facade.
|
|
715
|
+
const githubFacadeCredentials = new GitHubFacadeCredentialsService(
|
|
716
|
+
new DbGitHubFacadeCredentialsStore(db, config.secretsEncKey ? new TokenCrypto(config.secretsEncKey) : null),
|
|
717
|
+
);
|
|
718
|
+
const githubFacade = new GitHubFacade({
|
|
719
|
+
credentials: githubFacadeCredentials,
|
|
720
|
+
codes: new DbGitHubFacadeCodeStore(db),
|
|
721
|
+
keys: externalApiKeyService,
|
|
722
|
+
consumers: [CLAUDE_CONSUMER],
|
|
723
|
+
stateSecret: config.jwtSecret,
|
|
724
|
+
publicFrontendUrl: config.publicFrontendUrl,
|
|
725
|
+
});
|
|
613
726
|
const mcpSessionStore = new McpSessionStore();
|
|
614
727
|
const mcpService = new McpService(
|
|
615
728
|
mcpSessionStore,
|
|
@@ -826,6 +939,13 @@ export async function createCoreServices(
|
|
|
826
939
|
pluginIndexService,
|
|
827
940
|
pluginProvisionService,
|
|
828
941
|
joinRequestsService,
|
|
942
|
+
pluginLinkIndex,
|
|
943
|
+
pluginLinksService,
|
|
944
|
+
pluginRenameService,
|
|
945
|
+
marketplaceCompiler,
|
|
946
|
+
marketplaceRepo,
|
|
947
|
+
githubFacade,
|
|
948
|
+
githubFacadeCredentials,
|
|
829
949
|
authService,
|
|
830
950
|
authMiddleware,
|
|
831
951
|
accountErasureService,
|
|
@@ -835,6 +955,7 @@ export async function createCoreServices(
|
|
|
835
955
|
reviewWorkflowService,
|
|
836
956
|
fileLockService,
|
|
837
957
|
workflowService,
|
|
958
|
+
kbSyncService,
|
|
838
959
|
eventBus,
|
|
839
960
|
fileChangeNotifier,
|
|
840
961
|
pendingCommitsService,
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
|
|
6
|
+
import type { WorkspaceService } from '../../workspace/workspace.service.js';
|
|
7
|
+
import { AccessControlService } from '../access-control.service.js';
|
|
8
|
+
|
|
9
|
+
const KB_DIR = 'knowledge-base';
|
|
10
|
+
|
|
11
|
+
async function writeFile(repo: string, rel: string, contents: string): Promise<void> {
|
|
12
|
+
const abs = path.join(repo, rel);
|
|
13
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
14
|
+
await fs.writeFile(abs, contents);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `invalidate()` fires exactly when the tree changes under a model load that
|
|
19
|
+
* is still reading it: a marketplace compile for one caller drops the caches
|
|
20
|
+
* while a compile for another caller is mid-load, or a commit lands while the
|
|
21
|
+
* explorer builds. The load that started on the OLD tree must not store its
|
|
22
|
+
* model after the drop — otherwise every read for the next TTL resolves
|
|
23
|
+
* against a tree that is already gone.
|
|
24
|
+
*/
|
|
25
|
+
describe('AccessControlService — a load that straddles invalidate() never repopulates the cache', () => {
|
|
26
|
+
let root: string;
|
|
27
|
+
const workspaceId = 'ws-invalidate-race';
|
|
28
|
+
|
|
29
|
+
beforeEach(async () => {
|
|
30
|
+
root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-invalidate-race-'));
|
|
31
|
+
});
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
vi.restoreAllMocks();
|
|
34
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('a read that starts after the drop keeps seeing the new tree once the old load lands', async () => {
|
|
38
|
+
const workspaceDir = path.join(root, workspaceId);
|
|
39
|
+
const repo = path.join(workspaceDir, KB_DIR);
|
|
40
|
+
await writeFile(repo, 'roles.yaml', 'roles:\n Admin:\n - admin@x.io\n');
|
|
41
|
+
await writeFile(repo, 'access.md', '---\nwrite:\n - Admin\n---\n');
|
|
42
|
+
// Notes is closed to zoe on the old tree.
|
|
43
|
+
await writeFile(repo, 'Notes/access.md', '---\nread:\n - Admin\n---\n');
|
|
44
|
+
await writeFile(repo, 'Notes/plan.md', '# plan\n');
|
|
45
|
+
const service = new AccessControlService(
|
|
46
|
+
{ getWorkspacePath: async () => workspaceDir } as unknown as WorkspaceService,
|
|
47
|
+
KB_DIR,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Hold the OLD load exactly where the walk reads Notes/access.md: the
|
|
51
|
+
// bytes are read now (the closed rules), but the load cannot finish until
|
|
52
|
+
// released — which is what a slow disk or a large tree does for free.
|
|
53
|
+
const realReadFile = fs.readFile.bind(fs);
|
|
54
|
+
let release: () => void = () => undefined;
|
|
55
|
+
const held = new Promise<void>((resolve) => (release = resolve));
|
|
56
|
+
let reachedHold: () => void = () => undefined;
|
|
57
|
+
const reached = new Promise<void>((resolve) => (reachedHold = resolve));
|
|
58
|
+
let holdOnce = true;
|
|
59
|
+
vi.spyOn(fs, 'readFile').mockImplementation(async (file, ...rest) => {
|
|
60
|
+
const result = await (realReadFile as (...a: unknown[]) => Promise<unknown>)(file, ...rest);
|
|
61
|
+
if (holdOnce && String(file).endsWith(path.join('Notes', 'access.md'))) {
|
|
62
|
+
holdOnce = false;
|
|
63
|
+
reachedHold();
|
|
64
|
+
await held;
|
|
65
|
+
}
|
|
66
|
+
return result as never;
|
|
67
|
+
});
|
|
68
|
+
const oldLoad = service.canRead(workspaceId, 'zoe@x.io', 'Notes/plan.md');
|
|
69
|
+
await reached;
|
|
70
|
+
|
|
71
|
+
// The tree moves and the caches are dropped while that load is in flight.
|
|
72
|
+
await writeFile(repo, 'Notes/access.md', '---\nread:\n - Admin\n - Zoe <zoe@x.io>\n---\n');
|
|
73
|
+
service.invalidate(workspaceId);
|
|
74
|
+
try {
|
|
75
|
+
await expect(service.canRead(workspaceId, 'zoe@x.io', 'Notes/plan.md')).resolves.toBe(true);
|
|
76
|
+
} finally {
|
|
77
|
+
// The old load lands (also on a failed assertion, so the test cannot hang).
|
|
78
|
+
release();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// It answers for the tree it read — closed — but must not become the
|
|
82
|
+
// cached model for the reads that follow.
|
|
83
|
+
await expect(oldLoad).resolves.toBe(false);
|
|
84
|
+
await expect(service.canRead(workspaceId, 'zoe@x.io', 'Notes/plan.md')).resolves.toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -953,7 +953,7 @@ describe('AccessControlService', () => {
|
|
|
953
953
|
|
|
954
954
|
const svc = new AccessControlService(stubWorkspaceService(workspaceId, workspaceDir), PROCESS_MAP_DIR);
|
|
955
955
|
const e = await svc.eligibleReaders(workspaceId, 'Knowledge/Foo.md');
|
|
956
|
-
expect(e).toEqual({ restricted: true, principals: [], roles: [], users: [] });
|
|
956
|
+
expect(e).toEqual({ restricted: true, principals: [], roles: [], users: [], publicVia: [] });
|
|
957
957
|
});
|
|
958
958
|
|
|
959
959
|
it('reports restricted=false when read: everyone applies', async () => {
|
|
@@ -963,7 +963,17 @@ describe('AccessControlService', () => {
|
|
|
963
963
|
|
|
964
964
|
const svc = new AccessControlService(stubWorkspaceService(workspaceId, workspaceDir), PROCESS_MAP_DIR);
|
|
965
965
|
const e = await svc.eligibleReaders(workspaceId, 'Knowledge/Foo.md');
|
|
966
|
-
|
|
966
|
+
// The flag says public; the lists still name the grants (write folds into read).
|
|
967
|
+
expect(e).toEqual({
|
|
968
|
+
restricted: false,
|
|
969
|
+
principals: [
|
|
970
|
+
{ name: 'Admin', kind: 'role' },
|
|
971
|
+
{ name: 'everyone', kind: 'role' },
|
|
972
|
+
],
|
|
973
|
+
roles: ['Admin', 'everyone'],
|
|
974
|
+
users: [],
|
|
975
|
+
publicVia: [], // public by a literal line, through no plugin
|
|
976
|
+
});
|
|
967
977
|
});
|
|
968
978
|
|
|
969
979
|
it('restricted=false when a closer everyone grant shadows a farther by-name deny', async () => {
|
|
@@ -978,7 +988,13 @@ describe('AccessControlService', () => {
|
|
|
978
988
|
// node really is readable by everyone — not restricted.
|
|
979
989
|
expect(await svc.canRead(workspaceId, 'felix@example.com', 'Knowledge/Open/Foo.md')).toBe(true);
|
|
980
990
|
const e = await svc.eligibleReaders(workspaceId, 'Knowledge/Open/Foo.md');
|
|
981
|
-
expect(e).toEqual({
|
|
991
|
+
expect(e).toEqual({
|
|
992
|
+
restricted: false,
|
|
993
|
+
principals: [{ name: 'everyone', kind: 'role' }],
|
|
994
|
+
roles: ['everyone'],
|
|
995
|
+
users: [],
|
|
996
|
+
publicVia: [],
|
|
997
|
+
});
|
|
982
998
|
});
|
|
983
999
|
|
|
984
1000
|
it('restricted=true when a same-scope by-name deny carves someone out of read: everyone', async () => {
|