@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bevel-software/platform-core-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "Open-source core backend of the Bevel platform: git-backed knowledge workspace, workflow (branches/change requests/locks/SSE), skills, tools, secrets vault, access control and the remote MCP surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"@kenjiuno/msgreader": "^1.28.0",
|
|
33
33
|
"@mastra/core": "1.35.0",
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
|
-
"@utcp/cli": "^1.1.
|
|
35
|
+
"@utcp/cli": "^1.1.3",
|
|
36
36
|
"@utcp/code-mode": "^1.2.13",
|
|
37
37
|
"@utcp/direct-call": "^1.1.1",
|
|
38
|
-
"@utcp/http": "^1.1.
|
|
39
|
-
"@utcp/mcp": "^1.1.
|
|
38
|
+
"@utcp/http": "^1.1.12",
|
|
39
|
+
"@utcp/mcp": "^1.1.7",
|
|
40
40
|
"@utcp/sdk": "^1.1.1",
|
|
41
41
|
"adm-zip": "^0.6.0",
|
|
42
42
|
"cors": "^2.8.5",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
53
53
|
"yaml": "^2.9.0",
|
|
54
54
|
"zod": "^3.24.0",
|
|
55
|
-
"@bevel-software/platform-mcp-core": "0.
|
|
56
|
-
"@bevel-software/platform-shared": "0.
|
|
55
|
+
"@bevel-software/platform-mcp-core": "0.15.1",
|
|
56
|
+
"@bevel-software/platform-shared": "0.15.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/adm-zip": "^0.5.8",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, test, expect, afterEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_KB_LAYOUT,
|
|
4
|
+
KNOWLEDGE_BASE_DIR,
|
|
5
|
+
PLUGINS_DIR,
|
|
6
|
+
SKILLS_DIR,
|
|
7
|
+
configureKbLayout,
|
|
8
|
+
currentKbLayout,
|
|
9
|
+
ontologyRoots,
|
|
10
|
+
pluginOfPath,
|
|
11
|
+
renderKbLayoutPlaceholders,
|
|
12
|
+
reservedRootDirNames,
|
|
13
|
+
validateKbLayout,
|
|
14
|
+
validateKbRootName,
|
|
15
|
+
normalizeSkillRoot,
|
|
16
|
+
} from '@bevel-software/platform-shared';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The KB layout is module state shared by every test in the process, so each
|
|
20
|
+
* test that reconfigures it puts the defaults back — exactly what a deployment
|
|
21
|
+
* that names nothing runs with.
|
|
22
|
+
*/
|
|
23
|
+
afterEach(() => configureKbLayout({ ...DEFAULT_KB_LAYOUT }));
|
|
24
|
+
|
|
25
|
+
describe('KB layout — validation', () => {
|
|
26
|
+
test('accepts the defaults and any three distinct plain folder names', () => {
|
|
27
|
+
expect(validateKbLayout({ ...DEFAULT_KB_LAYOUT })).toBeNull();
|
|
28
|
+
expect(
|
|
29
|
+
validateKbLayout({ knowledgeBaseDir: 'docs', skillsDir: 'skills', pluginsDir: 'plugins' }),
|
|
30
|
+
).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('refuses a root that could escape the repository or hide from the scanners', () => {
|
|
34
|
+
expect(validateKbRootName('')).not.toBeNull();
|
|
35
|
+
expect(validateKbRootName('a/b')).not.toBeNull();
|
|
36
|
+
expect(validateKbRootName('a\\b')).not.toBeNull();
|
|
37
|
+
expect(validateKbRootName('..')).not.toBeNull();
|
|
38
|
+
expect(validateKbRootName('.git')).not.toBeNull();
|
|
39
|
+
expect(validateKbRootName('.hidden')).not.toBeNull();
|
|
40
|
+
expect(validateKbRootName('bad\nname')).not.toBeNull();
|
|
41
|
+
// The same rule every file and folder name passes: reserved Windows
|
|
42
|
+
// names, forbidden characters, trailing dots and spaces.
|
|
43
|
+
expect(validateKbRootName('CON')).not.toBeNull();
|
|
44
|
+
expect(validateKbRootName('a\u007fb')).not.toBeNull();
|
|
45
|
+
expect(validateKbRootName('a:b')).not.toBeNull();
|
|
46
|
+
expect(validateKbRootName('name.')).not.toBeNull();
|
|
47
|
+
expect(validateKbRootName('x'.repeat(300))).not.toBeNull();
|
|
48
|
+
expect(validateKbRootName('Skills')).toBeNull();
|
|
49
|
+
expect(validateKbRootName('My Skills')).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('a skill root forgives trailing slashes only — an empty segment inside is malformed', () => {
|
|
53
|
+
expect(normalizeSkillRoot('Skills/Eng/deploy/')).toBe('Skills/Eng/deploy');
|
|
54
|
+
expect(normalizeSkillRoot('Skills/Eng/deploy//')).toBe('Skills/Eng/deploy');
|
|
55
|
+
expect(normalizeSkillRoot('Skills//deploy')).toBeNull();
|
|
56
|
+
expect(normalizeSkillRoot('Skills/./deploy')).toBeNull();
|
|
57
|
+
expect(normalizeSkillRoot('/Skills/deploy')).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('refuses a configurable root that takes a fixed reserved name', () => {
|
|
61
|
+
expect(
|
|
62
|
+
validateKbLayout({ knowledgeBaseDir: 'KnowledgeBase', skillsDir: 'data', pluginsDir: 'Plugins' }),
|
|
63
|
+
).toMatch(/reserved folder name/);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('renders placeholders without interpreting $-patterns in a folder name', () => {
|
|
67
|
+
expect(
|
|
68
|
+
renderKbLayoutPlaceholders('a {{skillsDir}} b', { knowledgeBaseDir: 'K', skillsDir: 'Sales$&', pluginsDir: 'P' }),
|
|
69
|
+
).toBe('a Sales$& b');
|
|
70
|
+
expect(renderKbLayoutPlaceholders('no placeholders', DEFAULT_KB_LAYOUT)).toBe('no placeholders');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('refuses two roots sharing a name, case-insensitively — one folder on a case-insensitive disk', () => {
|
|
74
|
+
expect(
|
|
75
|
+
validateKbLayout({ knowledgeBaseDir: 'KnowledgeBase', skillsDir: 'skills', pluginsDir: 'Skills' }),
|
|
76
|
+
).toMatch(/three different names/);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('KB layout — configuration', () => {
|
|
81
|
+
test('applies the names to the live bindings every consumer reads', () => {
|
|
82
|
+
configureKbLayout({ knowledgeBaseDir: 'docs', skillsDir: 'skills', pluginsDir: 'plugins' });
|
|
83
|
+
expect(KNOWLEDGE_BASE_DIR).toBe('docs');
|
|
84
|
+
expect(SKILLS_DIR).toBe('skills');
|
|
85
|
+
expect(PLUGINS_DIR).toBe('plugins');
|
|
86
|
+
expect(currentKbLayout()).toEqual({ knowledgeBaseDir: 'docs', skillsDir: 'skills', pluginsDir: 'plugins' });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('the derived sets follow the configured names rather than snapshotting the defaults', () => {
|
|
90
|
+
configureKbLayout({ knowledgeBaseDir: 'docs', skillsDir: 'skills', pluginsDir: 'plugins' });
|
|
91
|
+
expect(ontologyRoots()).toEqual(['docs', 'Data']);
|
|
92
|
+
expect(reservedRootDirNames().has('plugins')).toBe(true);
|
|
93
|
+
expect(reservedRootDirNames().has('Plugins')).toBe(false);
|
|
94
|
+
// Path rules read the live name too.
|
|
95
|
+
expect(pluginOfPath('plugins/GTM/skills/x/SKILL.md')).toBe('GTM');
|
|
96
|
+
expect(pluginOfPath('Plugins/GTM/skills/x/SKILL.md')).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('throws on an invalid layout and leaves the current one untouched', () => {
|
|
100
|
+
expect(() =>
|
|
101
|
+
configureKbLayout({ knowledgeBaseDir: 'a', skillsDir: 'a', pluginsDir: 'b' }),
|
|
102
|
+
).toThrow(/three different names/);
|
|
103
|
+
expect(currentKbLayout()).toEqual(DEFAULT_KB_LAYOUT);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('trims what it applies', () => {
|
|
107
|
+
configureKbLayout({ knowledgeBaseDir: ' docs ', skillsDir: 'skills', pluginsDir: 'plugins' });
|
|
108
|
+
expect(KNOWLEDGE_BASE_DIR).toBe('docs');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { DEFAULT_BRANCH } from '@bevel-software/platform-shared';
|
|
3
|
+
import { FileChangeNotifier } from '../../modules/kb-fs/file-change-notifier.js';
|
|
4
|
+
import { WorkflowEventBus } from '../../modules/workflow/event-bus.js';
|
|
5
|
+
import { registerCatalogCacheInvalidation } from '../catalog-cache-invalidation.js';
|
|
6
|
+
|
|
7
|
+
const KB_DIR = 'knowledge-base';
|
|
8
|
+
|
|
9
|
+
const USER = {
|
|
10
|
+
id: 'u1',
|
|
11
|
+
email: 'someone@example.com',
|
|
12
|
+
name: 'Someone',
|
|
13
|
+
} as unknown as Parameters<Parameters<FileChangeNotifier['onFilesChanged']>[0]>[0]['byUser'];
|
|
14
|
+
|
|
15
|
+
function setup() {
|
|
16
|
+
const eventBus = new WorkflowEventBus();
|
|
17
|
+
const fileChangeNotifier = new FileChangeNotifier();
|
|
18
|
+
const catalogs = [
|
|
19
|
+
{ invalidate: vi.fn() },
|
|
20
|
+
{ invalidate: vi.fn() },
|
|
21
|
+
{ invalidate: vi.fn() },
|
|
22
|
+
];
|
|
23
|
+
const off = registerCatalogCacheInvalidation({
|
|
24
|
+
eventBus,
|
|
25
|
+
fileChangeNotifier,
|
|
26
|
+
kbDirName: KB_DIR,
|
|
27
|
+
catalogs,
|
|
28
|
+
});
|
|
29
|
+
/** How many times EVERY catalog was dropped — they are only ever dropped together. */
|
|
30
|
+
const drops = () => {
|
|
31
|
+
const counts = catalogs.map((c) => c.invalidate.mock.calls.length);
|
|
32
|
+
expect(new Set(counts).size).toBe(1);
|
|
33
|
+
return counts[0];
|
|
34
|
+
};
|
|
35
|
+
return { eventBus, fileChangeNotifier, drops, off };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('registerCatalogCacheInvalidation', () => {
|
|
39
|
+
describe('commit-time (FileChangeNotifier)', () => {
|
|
40
|
+
it('drops the catalogs when a default-branch commit touches Plugins/', () => {
|
|
41
|
+
const { fileChangeNotifier, drops } = setup();
|
|
42
|
+
fileChangeNotifier.emit({
|
|
43
|
+
workspaceId: 'ws',
|
|
44
|
+
branch: DEFAULT_BRANCH,
|
|
45
|
+
paths: [`${KB_DIR}/Plugins/Engineering/coding/SKILL.md`],
|
|
46
|
+
byUser: USER,
|
|
47
|
+
});
|
|
48
|
+
expect(drops()).toBe(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('ignores commits on other branches, and commits outside Plugins/', () => {
|
|
52
|
+
const { fileChangeNotifier, drops } = setup();
|
|
53
|
+
fileChangeNotifier.emit({
|
|
54
|
+
workspaceId: 'ws',
|
|
55
|
+
branch: 'feature/x',
|
|
56
|
+
paths: [`${KB_DIR}/Plugins/Engineering/coding/SKILL.md`],
|
|
57
|
+
byUser: USER,
|
|
58
|
+
});
|
|
59
|
+
fileChangeNotifier.emit({
|
|
60
|
+
workspaceId: 'ws',
|
|
61
|
+
branch: DEFAULT_BRANCH,
|
|
62
|
+
paths: [`${KB_DIR}/KnowledgeBase/Note.md`],
|
|
63
|
+
byUser: USER,
|
|
64
|
+
});
|
|
65
|
+
expect(drops()).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("the default branch's tree changed", () => {
|
|
70
|
+
/**
|
|
71
|
+
* The bug this exists for: approving a proposed skill closed its change
|
|
72
|
+
* request (so it left the pending shelf at once) while the released catalog
|
|
73
|
+
* kept serving its pre-merge scan, and the card vanished from the library
|
|
74
|
+
* until the TTL ran out.
|
|
75
|
+
*
|
|
76
|
+
* One test, because the subscriber sees ONE signal: the merge reaches here
|
|
77
|
+
* as the `fs-tree-changed` its post-merge pull emits, exactly as a
|
|
78
|
+
* working-tree write does — NOT as a `change-request-merged` special
|
|
79
|
+
* case. That a pull of the default workspace actually emits this event
|
|
80
|
+
* (only after it resolves, and only when it changed the tree's content) is the emitter's
|
|
81
|
+
* own contract, pinned in workflow.service.releaseLock.test.ts under
|
|
82
|
+
* "the tree-change announcement".
|
|
83
|
+
*/
|
|
84
|
+
it('drops the catalogs when the default tree changes (write or post-merge pull alike)', () => {
|
|
85
|
+
const { eventBus, drops } = setup();
|
|
86
|
+
eventBus.emit({ kind: 'fs-tree-changed', workspaceId: 'ws', branch: DEFAULT_BRANCH });
|
|
87
|
+
expect(drops()).toBe(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('drops them when a diverged workspace is reconciled', () => {
|
|
91
|
+
const { eventBus, drops } = setup();
|
|
92
|
+
eventBus.emit({ kind: 'git-sync-recovered', workspaceId: 'ws', branch: DEFAULT_BRANCH });
|
|
93
|
+
expect(drops()).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('ignores both events on another branch', () => {
|
|
97
|
+
const { eventBus, drops } = setup();
|
|
98
|
+
eventBus.emit({ kind: 'fs-tree-changed', workspaceId: 'ws', branch: 'feature/x' });
|
|
99
|
+
eventBus.emit({ kind: 'git-sync-recovered', workspaceId: 'ws', branch: 'feature/x' });
|
|
100
|
+
expect(drops()).toBe(0);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('drops them BEFORE the event reaches a browser session', () => {
|
|
104
|
+
const { eventBus, drops } = setup();
|
|
105
|
+
const seenAtPush: number[] = [];
|
|
106
|
+
eventBus.subscribe({
|
|
107
|
+
sessionId: 's1',
|
|
108
|
+
userId: 'u1',
|
|
109
|
+
// `fs-tree-changed` is workspace-scoped, so a session only receives
|
|
110
|
+
// it while focused on that workspace.
|
|
111
|
+
getFocusedWorkspaceId: () => 'ws',
|
|
112
|
+
// The browser reloads the library off this event; if the caches were
|
|
113
|
+
// still warm here, the reload would re-read the stale catalog.
|
|
114
|
+
push: () => seenAtPush.push(drops()),
|
|
115
|
+
});
|
|
116
|
+
eventBus.emit({ kind: 'fs-tree-changed', workspaceId: 'ws', branch: DEFAULT_BRANCH });
|
|
117
|
+
expect(seenAtPush).toEqual([1]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('does not drop them when a merge fails', () => {
|
|
121
|
+
const { eventBus, drops } = setup();
|
|
122
|
+
eventBus.emit({
|
|
123
|
+
kind: 'change-request-merge-failed',
|
|
124
|
+
forUserId: 'u1',
|
|
125
|
+
number: 42,
|
|
126
|
+
reason: 'conflicts',
|
|
127
|
+
conflicts: true,
|
|
128
|
+
});
|
|
129
|
+
expect(drops()).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('detaches every subscription on unsubscribe', () => {
|
|
134
|
+
const { eventBus, fileChangeNotifier, drops, off } = setup();
|
|
135
|
+
off();
|
|
136
|
+
eventBus.emit({ kind: 'change-request-merged', number: 42 });
|
|
137
|
+
eventBus.emit({ kind: 'fs-tree-changed', workspaceId: 'ws', branch: DEFAULT_BRANCH });
|
|
138
|
+
fileChangeNotifier.emit({
|
|
139
|
+
workspaceId: 'ws',
|
|
140
|
+
branch: DEFAULT_BRANCH,
|
|
141
|
+
paths: [`${KB_DIR}/Plugins/Engineering/coding/SKILL.md`],
|
|
142
|
+
byUser: USER,
|
|
143
|
+
});
|
|
144
|
+
expect(drops()).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { DEFAULT_BRANCH, PLUGINS_DIR, SKILLS_DIR } from '@bevel-software/platform-shared';
|
|
2
|
+
import type { FileChangeNotifier } from '../modules/kb-fs/file-change-notifier.js';
|
|
3
|
+
import type { WorkflowEventBus } from '../modules/workflow/event-bus.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A catalog that scans the DEFAULT branch's working tree and caches the result
|
|
7
|
+
* behind a TTL — the skill catalog, the tool-manual catalog, the plugin index.
|
|
8
|
+
* All three answer the same question ("what is released?") off the same folder,
|
|
9
|
+
* so they go stale together and are dropped together.
|
|
10
|
+
*/
|
|
11
|
+
export interface InvalidatableCatalog {
|
|
12
|
+
invalidate(): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Every way a released catalog can go stale, wired in one place.
|
|
17
|
+
*
|
|
18
|
+
* The TTL inside each catalog is a backstop, not the freshness mechanism: at a
|
|
19
|
+
* minute wide it is far longer than the gap between "the user did the thing"
|
|
20
|
+
* and "the user looks for the result". These three subscribers are what make
|
|
21
|
+
* the next read see the change instead of the previous minute's answer.
|
|
22
|
+
*
|
|
23
|
+
* Returns an unsubscribe that detaches all three.
|
|
24
|
+
*/
|
|
25
|
+
export function registerCatalogCacheInvalidation(deps: {
|
|
26
|
+
eventBus: WorkflowEventBus;
|
|
27
|
+
fileChangeNotifier: FileChangeNotifier;
|
|
28
|
+
/** The KB folder inside the workspace — `paths` are workspace-relative. */
|
|
29
|
+
kbDirName: string;
|
|
30
|
+
catalogs: InvalidatableCatalog[];
|
|
31
|
+
}): () => void {
|
|
32
|
+
const { eventBus, fileChangeNotifier, kbDirName, catalogs } = deps;
|
|
33
|
+
const invalidateAll = () => {
|
|
34
|
+
for (const c of catalogs) c.invalidate();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Subscriber A — COMMIT-time freshness: a committed change drops the affected
|
|
38
|
+
// caches immediately instead of waiting out their TTLs. The catalogs are
|
|
39
|
+
// global but read the DEFAULT branch only, so only default-branch changes
|
|
40
|
+
// under their folders matter. (The kb-graph id-index invalidation that used
|
|
41
|
+
// to live here is registered by the enterprise overlay, next to the kb-graph
|
|
42
|
+
// service it belongs to — the caches are independent, so the split preserves
|
|
43
|
+
// behavior.)
|
|
44
|
+
const offFiles = fileChangeNotifier.onFilesChanged(({ branch, paths }) => {
|
|
45
|
+
if (branch !== DEFAULT_BRANCH) return;
|
|
46
|
+
// Skills, tools, the plugin index and the link index all read `Plugins/`
|
|
47
|
+
// or `Skills/`, so one touch check drives every cache. An access grant
|
|
48
|
+
// lands as a default-branch change to `Plugins/<plugin>/access.md` or a
|
|
49
|
+
// skill folder's `access.md`, so this is also what makes a newly-granted
|
|
50
|
+
// plugin or link unlock within one round-trip instead of one TTL. Both
|
|
51
|
+
// names are live bindings (a deployment may rename the roots), hence read
|
|
52
|
+
// per event rather than captured.
|
|
53
|
+
if (
|
|
54
|
+
paths.some(
|
|
55
|
+
(p) => p.startsWith(`${kbDirName}/${PLUGINS_DIR}/`) || p.startsWith(`${kbDirName}/${SKILLS_DIR}/`),
|
|
56
|
+
)
|
|
57
|
+
) {
|
|
58
|
+
invalidateAll();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Subscriber B — the DEFAULT branch's tree changed under the catalogs.
|
|
63
|
+
//
|
|
64
|
+
// `fs-tree-changed` is emitted the moment bytes hit a working tree: by the
|
|
65
|
+
// workspace routes when someone writes a file, and by `pullWorkspace` when a
|
|
66
|
+
// pull rewrites the default workspace — a merge landing an approved skill,
|
|
67
|
+
// the non-fast-forward recovery ladder, a plain sync from the remote. That
|
|
68
|
+
// is deliberately ONE signal rather than one per occasion: the catalogs do
|
|
69
|
+
// not care which path rewrote the tree, only that it is no longer the tree
|
|
70
|
+
// they scanned. Enumerating the occasions instead (a subscriber for merges,
|
|
71
|
+
// another for recoveries) left every path nobody had thought of yet serving
|
|
72
|
+
// a stale catalog for a full TTL — which is exactly how the merge case got
|
|
73
|
+
// missed.
|
|
74
|
+
//
|
|
75
|
+
// `git-sync-recovered` is the same fact arriving by the other road: a
|
|
76
|
+
// workspace that had diverged from origin has been reconciled, so its tree
|
|
77
|
+
// moved. The pull inside that recovery announces itself, but the recovery
|
|
78
|
+
// can also complete by paths that do not run one, and a catalog left holding
|
|
79
|
+
// a scan of the diverged tree is the failure this exists to prevent.
|
|
80
|
+
const offEmit = eventBus.onEmit((event) => {
|
|
81
|
+
if (event.kind !== 'fs-tree-changed' && event.kind !== 'git-sync-recovered') return;
|
|
82
|
+
// Both events declare `branch`, so no `in` guard: the kind check above has
|
|
83
|
+
// already narrowed the union to the two that carry one.
|
|
84
|
+
if (event.branch !== DEFAULT_BRANCH) return;
|
|
85
|
+
invalidateAll();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return () => {
|
|
89
|
+
offFiles();
|
|
90
|
+
offEmit();
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { registerWorkflowTools } from '../modules/workflow/agent-tools/workflow.tools.js';
|
|
21
21
|
import { registerWorkspaceTools } from '../modules/workspace/workspace.tools.js';
|
|
22
22
|
import { RECOVERY_BOT_EMAIL } from '../modules/workflow/recovery-bot.js';
|
|
23
|
-
import { registerSkillsTools, createSkillsRoutes } from '../modules/skills/index.js';
|
|
23
|
+
import { registerSkillsTools, createSkillsRoutes, createSkillAccessRequestRoutes } from '../modules/skills/index.js';
|
|
24
24
|
import { createPluginsRoutes } from '../modules/plugins/index.js';
|
|
25
25
|
import type { SessionOntologyGate } from '../modules/workspace/session-ontology.gate.js';
|
|
26
26
|
import {
|
|
@@ -33,7 +33,17 @@ import { createGroupsAdminRoutes } from '../modules/access/groups-admin.routes.j
|
|
|
33
33
|
import { createUpdateCheckRoutes } from '../modules/update-check/update-check.routes.js';
|
|
34
34
|
import { createAccountRoutes } from '../modules/auth/account.routes.js';
|
|
35
35
|
import { createSetupRoutes } from '../modules/settings/setup.routes.js';
|
|
36
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
createKbSyncRoutes,
|
|
38
|
+
isSyncRawBodyPath,
|
|
39
|
+
SYNC_RESPONSE_HEADER,
|
|
40
|
+
} from '../modules/kb-sync/kb-sync.routes.js';
|
|
41
|
+
import { createMarketplaceGitRoutes } from '../modules/marketplace/index.js';
|
|
42
|
+
import {
|
|
43
|
+
createGitHubFacadeAdminRoutes,
|
|
44
|
+
createGitHubFacadeRoutes,
|
|
45
|
+
} from '../modules/marketplace/github-facade/index.js';
|
|
46
|
+
import { DEFAULT_BRANCH, PROTECTED_BRANCHES, currentKbLayout, type AuthUser } from '@bevel-software/platform-shared';
|
|
37
47
|
import { GIT_SHA } from '../version.js';
|
|
38
48
|
import type { CoreServices } from './create-core-services.js';
|
|
39
49
|
|
|
@@ -132,7 +142,9 @@ export async function createCoreServer(
|
|
|
132
142
|
cors({
|
|
133
143
|
origin: true,
|
|
134
144
|
credentials: true,
|
|
135
|
-
|
|
145
|
+
// `SYNC_RESPONSE_HEADER` is how the browser tells the sync endpoint's
|
|
146
|
+
// own 503 from a reverse proxy's — see `kb-sync.routes.ts`.
|
|
147
|
+
exposedHeaders: ['Mcp-Session-Id', 'Mcp-Protocol-Version', 'WWW-Authenticate', SYNC_RESPONSE_HEADER],
|
|
136
148
|
}),
|
|
137
149
|
);
|
|
138
150
|
// Global JSON body parser. Some overlay routes carry a whole document dump
|
|
@@ -141,10 +153,13 @@ export async function createCoreServer(
|
|
|
141
153
|
// parse once the first has run, so without this the 10 MB global limit would
|
|
142
154
|
// shadow the route's larger limit and 413 a large-but-valid upload before it
|
|
143
155
|
// ever reaches the route).
|
|
156
|
+
// The sync routes (`/api/sync` and `/api/sync/<branch>`) read their body as
|
|
157
|
+
// raw bytes: one of their credentials is an HMAC over exactly what arrived,
|
|
158
|
+
// which a parsed-and-reserialised body cannot reproduce.
|
|
144
159
|
const jsonExemptPaths = new Set(ext.jsonParserExemptPaths ?? []);
|
|
145
160
|
const globalJson = express.json({ limit: '10mb' });
|
|
146
161
|
app.use((req, res, next) => {
|
|
147
|
-
if (jsonExemptPaths.has(req.path)) return next();
|
|
162
|
+
if (jsonExemptPaths.has(req.path) || isSyncRawBodyPath(req.path)) return next();
|
|
148
163
|
return globalJson(req, res, next);
|
|
149
164
|
});
|
|
150
165
|
|
|
@@ -178,6 +193,13 @@ export async function createCoreServer(
|
|
|
178
193
|
const mcpResourceUrl = new URL('/api/mcp', core.config.publicBackendUrl);
|
|
179
194
|
mcpResourceUrl.username = '';
|
|
180
195
|
mcpResourceUrl.password = '';
|
|
196
|
+
/** The remote-sync address the setup status publishes for admins to paste into a hook. */
|
|
197
|
+
const syncUrl = new URL('/api/sync', core.config.publicBackendUrl);
|
|
198
|
+
syncUrl.username = '';
|
|
199
|
+
syncUrl.password = '';
|
|
200
|
+
const marketplaceGitUrl = new URL(`/git/${core.marketplaceRepo.repoName}`, core.config.publicBackendUrl);
|
|
201
|
+
marketplaceGitUrl.username = '';
|
|
202
|
+
marketplaceGitUrl.password = '';
|
|
181
203
|
|
|
182
204
|
/**
|
|
183
205
|
* The handful of facts the browser needs BEFORE it can render anything, and
|
|
@@ -199,6 +221,18 @@ export async function createCoreServer(
|
|
|
199
221
|
defaultBranch: DEFAULT_BRANCH,
|
|
200
222
|
protectedBranches: [...PROTECTED_BRANCHES],
|
|
201
223
|
},
|
|
224
|
+
/**
|
|
225
|
+
* The three renameable KB roots, for the same reason as the branch
|
|
226
|
+
* model: the file tree, the library router and every path rule read
|
|
227
|
+
* them, and they used to be compile-time constants.
|
|
228
|
+
*/
|
|
229
|
+
kbLayout: currentKbLayout(),
|
|
230
|
+
/**
|
|
231
|
+
* The per-user marketplace git remote (see modules/marketplace). Same
|
|
232
|
+
* derivation as `mcpUrl`: our address, userinfo stripped — the caller
|
|
233
|
+
* adds their own connection key.
|
|
234
|
+
*/
|
|
235
|
+
marketplaceGitUrl: marketplaceGitUrl.toString(),
|
|
202
236
|
/**
|
|
203
237
|
* The same value the OAuth metadata publishes (see `mcpResourceUrl`).
|
|
204
238
|
*
|
|
@@ -217,6 +251,36 @@ export async function createCoreServer(
|
|
|
217
251
|
});
|
|
218
252
|
});
|
|
219
253
|
|
|
254
|
+
// The per-user marketplace as a git remote. Outside `/api` and ahead of
|
|
255
|
+
// every JWT mount: it authenticates with a connection key in HTTP Basic
|
|
256
|
+
// (what `git clone https://key:<k>@…` sends), and git's own http-backend
|
|
257
|
+
// serves the protocol. See modules/marketplace.
|
|
258
|
+
app.use(
|
|
259
|
+
'/git',
|
|
260
|
+
createMarketplaceGitRoutes({
|
|
261
|
+
repo: core.marketplaceRepo,
|
|
262
|
+
keys: core.externalApiKeyService,
|
|
263
|
+
mountPath: '/git',
|
|
264
|
+
}),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// The SAME marketplace as a GitHub Enterprise consumer (claude.ai, Cowork)
|
|
268
|
+
// fetches it: the GitHub-shaped OAuth pair at /login/oauth/* and the REST
|
|
269
|
+
// calls at /api/v3/*, reading the same per-user tree by the same connection
|
|
270
|
+
// key. At the app root because those paths are GitHub's, and ahead of the
|
|
271
|
+
// /api JWT mounts because the consumer's backend arrives with a Bearer
|
|
272
|
+
// key, never a session.
|
|
273
|
+
app.use(
|
|
274
|
+
createGitHubFacadeRoutes({
|
|
275
|
+
facade: core.githubFacade,
|
|
276
|
+
keys: core.externalApiKeyService,
|
|
277
|
+
repo: core.marketplaceRepo,
|
|
278
|
+
owner: 'git',
|
|
279
|
+
repoName: core.marketplaceRepo.repoName.replace(/\.git$/, ''),
|
|
280
|
+
publicUrl: core.config.publicBackendUrl,
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
|
|
220
284
|
// Overlay boot-time side effects (startup reconciles, periodic sweeps).
|
|
221
285
|
await ext.onBoot?.(core);
|
|
222
286
|
|
|
@@ -277,6 +341,17 @@ export async function createCoreServer(
|
|
|
277
341
|
core.mcpResourceMetadataUrl,
|
|
278
342
|
));
|
|
279
343
|
|
|
344
|
+
// Remote sync — `POST /api/sync`, called by a git host's webhook or a
|
|
345
|
+
// pipeline with the deployment's sync secret (or by an admin's session).
|
|
346
|
+
// Same reason as MCP for mounting here: a non-JWT bearer must not meet a
|
|
347
|
+
// protected mount first.
|
|
348
|
+
app.use('/api', createKbSyncRoutes({
|
|
349
|
+
kbSync: core.kbSyncService,
|
|
350
|
+
syncSecret: () => core.settings.resolve('kbSyncSecret'),
|
|
351
|
+
authService: core.authService,
|
|
352
|
+
adminAccess: core.adminAccess,
|
|
353
|
+
}));
|
|
354
|
+
|
|
280
355
|
// MCP OAuth 2.1 authorization server: /authorize, /token, /register,
|
|
281
356
|
// /revoke + the /.well-known metadata documents (the SDK requires an
|
|
282
357
|
// app-root mount for those paths). Un-authed by design — these endpoints
|
|
@@ -431,7 +506,23 @@ export async function createCoreServer(
|
|
|
431
506
|
app.use(
|
|
432
507
|
'/api',
|
|
433
508
|
core.authMiddleware,
|
|
434
|
-
createSkillsRoutes(core.skillService, core.pendingSkillsService),
|
|
509
|
+
createSkillsRoutes(core.skillService, core.pendingSkillsService, core.pluginLinkIndex, core.accessControl),
|
|
510
|
+
);
|
|
511
|
+
// Asking for write on a shared skill — the join-request machinery pointed
|
|
512
|
+
// at a skill folder. Same JWT gate, same fail-closed shape.
|
|
513
|
+
app.use(
|
|
514
|
+
'/api',
|
|
515
|
+
core.authMiddleware,
|
|
516
|
+
createSkillAccessRequestRoutes({
|
|
517
|
+
skillService: core.skillService,
|
|
518
|
+
accessControl: core.accessControl,
|
|
519
|
+
workflow: core.workflowService,
|
|
520
|
+
workspaceService: core.workspaceService,
|
|
521
|
+
joinRequests: core.joinRequestsService,
|
|
522
|
+
kbDirName: core.kbDirName,
|
|
523
|
+
resolveUser: async (req) =>
|
|
524
|
+
req.userId ? ((await core.authService.getUserById(req.userId)) ?? null) : null,
|
|
525
|
+
}),
|
|
435
526
|
);
|
|
436
527
|
// Plugin enumeration + join requests. Browser-only (JWT), and fail-closed
|
|
437
528
|
// like every other read surface: plugins the caller cannot access (member,
|
|
@@ -446,6 +537,8 @@ export async function createCoreServer(
|
|
|
446
537
|
core.pluginProvisionService,
|
|
447
538
|
core.kbDirName,
|
|
448
539
|
async (req) => (req.userId ? ((await core.authService.getUserById(req.userId)) ?? null) : null),
|
|
540
|
+
core.pluginLinksService,
|
|
541
|
+
core.pluginRenameService,
|
|
449
542
|
));
|
|
450
543
|
// Admin-status resolver (CORE — see the note in admin-access.routes.ts;
|
|
451
544
|
// the full admin router is an enterprise `ext.authed` extension).
|
|
@@ -478,7 +571,12 @@ export async function createCoreServer(
|
|
|
478
571
|
app.use(
|
|
479
572
|
'/api',
|
|
480
573
|
core.authMiddleware,
|
|
481
|
-
createSetupRoutes(core.settings, core.adminAccess, core.kbStartupRunner
|
|
574
|
+
createSetupRoutes(core.settings, core.adminAccess, core.kbStartupRunner, {
|
|
575
|
+
// Same address family as the MCP endpoint above, userinfo stripped for
|
|
576
|
+
// the same reason: this string is handed to admins to paste elsewhere.
|
|
577
|
+
url: syncUrl.toString(),
|
|
578
|
+
lastSync: () => core.kbSyncService.lastSync(),
|
|
579
|
+
}),
|
|
482
580
|
);
|
|
483
581
|
app.use('/api', core.authMiddleware, createToolManualsBrowserRoutes(core.toolManualService, {
|
|
484
582
|
service: core.mcpServerEditService,
|
|
@@ -495,6 +593,20 @@ export async function createCoreServer(
|
|
|
495
593
|
app.use('/api', core.authMiddleware, createOAuthConsentRoutes({
|
|
496
594
|
provider: core.mcpOAuthProvider,
|
|
497
595
|
stateSecret: core.config.jwtSecret,
|
|
596
|
+
facade: {
|
|
597
|
+
isFacadeRequest: (st) => core.githubFacade.isFacadeRequest(st),
|
|
598
|
+
clientNameFor: (st) => core.githubFacade.clientNameFor(st),
|
|
599
|
+
completeConsent: (userId, st) => core.githubFacade.completeConsent(userId, st),
|
|
600
|
+
},
|
|
601
|
+
}));
|
|
602
|
+
|
|
603
|
+
// What an Owner pastes into Claude's admin settings to register this
|
|
604
|
+
// deployment as a GitHub Enterprise Server — admins only.
|
|
605
|
+
app.use('/api', core.authMiddleware, createGitHubFacadeAdminRoutes({
|
|
606
|
+
credentials: core.githubFacadeCredentials,
|
|
607
|
+
isAdmin: (email) => core.adminAccess.isAdmin(email),
|
|
608
|
+
publicUrl: core.config.publicBackendUrl,
|
|
609
|
+
marketplaceUrl: marketplaceGitUrl.toString(),
|
|
498
610
|
}));
|
|
499
611
|
|
|
500
612
|
// JWT-protected overlay routes.
|
|
@@ -512,7 +624,11 @@ export async function createCoreServer(
|
|
|
512
624
|
// MCP client parses as JSON → "expected object, received undefined" and
|
|
513
625
|
// the whole connection fails. A clean 404 lets discovery fall through to
|
|
514
626
|
// the variant we do serve. Same reasoning for stray `/api/*` gets.
|
|
515
|
-
if (
|
|
627
|
+
if (
|
|
628
|
+
req.path.startsWith('/.well-known/') ||
|
|
629
|
+
req.path.startsWith('/api/') ||
|
|
630
|
+
req.path.startsWith('/login/oauth/')
|
|
631
|
+
) {
|
|
516
632
|
res.status(404).json({ error: 'Not found' });
|
|
517
633
|
return;
|
|
518
634
|
}
|