@bevel-software/platform-core-backend 0.7.5 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -202
- package/THIRD-PARTY-NOTICES.md +428 -454
- package/dist/core/core-ports.d.ts +8 -1
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +75 -18
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +12 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +69 -36
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +8 -7
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +1 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +6 -6
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-declarations.d.ts +5 -5
- package/dist/modules/access/access-declarations.js +3 -3
- package/dist/modules/access/access-mutation.service.d.ts +3 -3
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +3 -3
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-splice.js +4 -4
- package/dist/modules/access/access-splice.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +3 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +22 -20
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/creator-access.d.ts +2 -2
- package/dist/modules/access/creator-access.js +5 -5
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/access/render-roles-yaml.d.ts +22 -0
- package/dist/modules/access/render-roles-yaml.d.ts.map +1 -0
- package/dist/modules/access/render-roles-yaml.js +56 -0
- package/dist/modules/access/render-roles-yaml.js.map +1 -0
- package/dist/modules/access/roles-admin.service.d.ts +33 -5
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +44 -19
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/code-mode/code-mode-names.d.ts +5 -13
- package/dist/modules/code-mode/code-mode-names.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode-names.js +5 -27
- package/dist/modules/code-mode/code-mode-names.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +29 -7
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +11 -52
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +33 -395
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/plugins/index.d.ts +7 -0
- package/dist/modules/plugins/index.d.ts.map +1 -0
- package/dist/modules/plugins/index.js +6 -0
- package/dist/modules/plugins/index.js.map +1 -0
- package/dist/modules/plugins/join-proposals.d.ts +53 -0
- package/dist/modules/plugins/join-proposals.d.ts.map +1 -0
- package/dist/modules/plugins/join-proposals.js +67 -0
- package/dist/modules/plugins/join-proposals.js.map +1 -0
- package/dist/modules/plugins/join-requests.service.d.ts +81 -0
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -0
- package/dist/modules/plugins/join-requests.service.js +135 -0
- package/dist/modules/plugins/join-requests.service.js.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts +134 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.js +344 -0
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -0
- package/dist/modules/plugins/plugins.contract.d.ts +106 -0
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.contract.js +36 -0
- package/dist/modules/plugins/plugins.contract.js.map +1 -0
- package/dist/modules/plugins/plugins.routes.d.ts +42 -0
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.routes.js +379 -0
- package/dist/modules/plugins/plugins.routes.js.map +1 -0
- package/dist/modules/plugins/plugins.service.d.ts +60 -0
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.service.js +172 -0
- package/dist/modules/plugins/plugins.service.js.map +1 -0
- package/dist/modules/settings/setup.routes.d.ts +10 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +111 -6
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +2 -2
- package/dist/modules/skills/pending-skills.service.js +7 -7
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +4 -4
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.contract.js +1 -1
- package/dist/modules/skills/skills.service.js +5 -5
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +65 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.js +276 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +92 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +328 -0
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +38 -12
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.js +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +13 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +233 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +74 -37
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +6 -3
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.js +2 -2
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workspace/kb-seed.service.d.ts +2 -2
- package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -1
- package/dist/modules/workspace/kb-seed.service.js +43 -10
- package/dist/modules/workspace/kb-seed.service.js.map +1 -1
- package/dist/modules/workspace/plugins-migration.d.ts +50 -0
- package/dist/modules/workspace/plugins-migration.d.ts.map +1 -0
- package/dist/modules/workspace/plugins-migration.js +379 -0
- package/dist/modules/workspace/plugins-migration.js.map +1 -0
- package/dist/modules/workspace/startup/kb-git.d.ts +23 -0
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -0
- package/dist/modules/workspace/startup/kb-git.js +86 -0
- package/dist/modules/workspace/startup/kb-git.js.map +1 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +74 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -0
- package/dist/modules/workspace/startup/kb-startup-runner.js +528 -0
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -0
- package/dist/modules/workspace/startup/on-server-start.d.ts +105 -0
- package/dist/modules/workspace/startup/on-server-start.d.ts.map +1 -0
- package/dist/modules/workspace/startup/on-server-start.js +21 -0
- package/dist/modules/workspace/startup/on-server-start.js.map +1 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +46 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +492 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +23 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js +69 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +17 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.js +109 -0
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -0
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +103 -0
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/template-files.step.js +337 -0
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.js +3 -3
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +0 -9
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +2 -30
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/shared/kb-layout.test.js +3 -3
- package/dist/shared/kb-layout.test.js.map +1 -1
- package/dist/shared/utcp-namespace.d.ts +6 -27
- package/dist/shared/utcp-namespace.d.ts.map +1 -1
- package/dist/shared/utcp-namespace.js +6 -63
- package/dist/shared/utcp-namespace.js.map +1 -1
- package/dist/shared/variable-refs.d.ts +42 -0
- package/dist/shared/variable-refs.d.ts.map +1 -0
- package/dist/shared/variable-refs.js +60 -0
- package/dist/shared/variable-refs.js.map +1 -0
- package/kb-template/.bevelignore +1 -1
- package/kb-template/AGENTS.md +88 -35
- package/kb-template/KnowledgeBase/How to get started.md +10 -10
- package/kb-template/access.md +36 -36
- package/kb-template/gitignore.template +16 -0
- package/migrations/meta/0000_snapshot.json +1479 -1479
- package/package.json +5 -4
- package/src/assets.ts +25 -25
- package/src/core/core-ports.ts +113 -106
- package/src/core/create-core-server.ts +85 -20
- package/src/core/create-core-services.ts +83 -42
- package/src/index.ts +69 -69
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +98 -98
- package/src/modules/access/__tests__/access-declarations.test.ts +28 -28
- package/src/modules/access/__tests__/access-md-format.test.ts +18 -18
- package/src/modules/access/__tests__/access-mutation.service.test.ts +5 -5
- package/src/modules/access/__tests__/access-splice.test.ts +2 -2
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +16 -16
- package/src/modules/access/__tests__/grant-sources.test.ts +12 -12
- package/src/modules/access/__tests__/roles-admin.service.test.ts +36 -2
- package/src/modules/access/access-control.interface.ts +8 -7
- package/src/modules/access/access-control.service.ts +7 -7
- package/src/modules/access/access-declarations.ts +5 -5
- package/src/modules/access/access-mutation.service.ts +3 -3
- package/src/modules/access/access-splice.ts +4 -4
- package/src/modules/access/access.routes.ts +23 -20
- package/src/modules/access/creator-access.ts +5 -5
- package/src/modules/access/render-roles-yaml.ts +65 -0
- package/src/modules/access/roles-admin.service.ts +45 -16
- package/src/modules/admin/admin-access.routes.ts +29 -29
- package/src/modules/auth/__tests__/auth.routes.test.ts +91 -91
- package/src/modules/auth/__tests__/rate-limit.test.ts +36 -36
- package/src/modules/auth/rate-limit.ts +45 -45
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +67 -0
- package/src/modules/code-mode/code-mode-names.ts +10 -36
- package/src/modules/code-mode/code-mode.tool.ts +27 -7
- package/src/modules/database/connection.ts +15 -15
- package/src/modules/database/schema.ts +11 -11
- package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +150 -150
- package/src/modules/mcp/mcp.service.ts +57 -435
- package/src/modules/{groups → plugins}/__tests__/join-proposals.test.ts +1 -1
- package/src/modules/{groups → plugins}/__tests__/join-requests.service.test.ts +7 -7
- package/src/modules/{groups/__tests__/group-index.service.test.ts → plugins/__tests__/plugin-index.service.test.ts} +41 -41
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +312 -0
- package/src/modules/{groups/__tests__/groups.routes.test.ts → plugins/__tests__/plugins.routes.test.ts} +100 -100
- package/src/modules/plugins/index.ts +17 -0
- package/src/modules/{groups → plugins}/join-proposals.ts +2 -2
- package/src/modules/{groups → plugins}/join-requests.service.ts +8 -8
- package/src/modules/{groups/group-provision.service.ts → plugins/plugin-provision.service.ts} +139 -69
- package/src/modules/{groups/groups.contract.ts → plugins/plugins.contract.ts} +26 -26
- package/src/modules/{groups/groups.routes.ts → plugins/plugins.routes.ts} +102 -102
- package/src/modules/{groups/groups.service.ts → plugins/plugins.service.ts} +43 -43
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +143 -143
- package/src/modules/settings/__tests__/setup.routes.test.ts +124 -3
- package/src/modules/settings/setup.routes.ts +112 -5
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +14 -14
- package/src/modules/skills/__tests__/skills.service.test.ts +13 -13
- package/src/modules/skills/pending-skills.service.ts +7 -7
- package/src/modules/skills/skills.contract.ts +4 -4
- package/src/modules/skills/skills.service.ts +5 -5
- package/src/modules/tool-auth/llm-usage-meter.ts +19 -19
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +198 -0
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +346 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +101 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +3 -3
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +27 -27
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +3 -3
- package/src/modules/tool-manuals/mcp-json-discovery.ts +328 -0
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +434 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +35 -12
- package/src/modules/tool-manuals/tool-manuals.routes.ts +222 -1
- package/src/modules/tool-manuals/tool-manuals.service.ts +82 -42
- package/src/modules/tool-manuals/tool-manuals.tools.ts +6 -3
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/branch-name.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +13 -8
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
- package/src/modules/workflow/git/git.service.ts +2 -2
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +1 -1
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
- package/src/modules/workflow/workflow-hooks.ts +101 -101
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +237 -237
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +236 -236
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +179 -179
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +320 -320
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +337 -337
- package/src/modules/workspace/__tests__/workspace.service.test.ts +27 -0
- package/src/modules/workspace/bevel-ignore.ts +66 -66
- package/src/modules/workspace/session-sink.ts +25 -25
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +495 -0
- package/src/modules/workspace/startup/kb-git.ts +94 -0
- package/src/modules/workspace/startup/kb-startup-runner.ts +597 -0
- package/src/modules/workspace/startup/on-server-start.ts +97 -0
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +636 -0
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +561 -0
- package/src/modules/workspace/startup/steps/roles-yaml.step.ts +71 -0
- package/src/modules/workspace/startup/steps/seed-tree.ts +115 -0
- package/src/modules/workspace/startup/steps/template-files.step.ts +360 -0
- package/src/modules/workspace/workspace.routes.ts +3 -3
- package/src/modules/workspace/workspace.service.ts +2 -35
- package/src/modules/workspace/workspace.tools.ts +922 -922
- package/src/shared/__tests__/join-request.test.ts +13 -13
- package/src/shared/__tests__/kb-layout.plugin.test.ts +45 -0
- package/src/shared/kb-layout.test.ts +3 -3
- package/src/shared/utcp-namespace.ts +10 -68
- package/src/shared/variable-refs.ts +64 -0
- package/src/modules/groups/__tests__/group-provision.service.test.ts +0 -247
- package/src/modules/groups/index.ts +0 -17
- package/src/modules/workspace/__tests__/kb-seed.service.test.ts +0 -444
- package/src/modules/workspace/kb-seed.interface.ts +0 -36
- package/src/modules/workspace/kb-seed.service.ts +0 -555
- package/src/shared/__tests__/kb-layout.group.test.ts +0 -45
- /package/kb-template/{Groups → Plugins}/.gitkeep +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
configureBranchModel,
|
|
6
6
|
validateBranchModel,
|
|
7
7
|
PROTECTED_BRANCHES,
|
|
8
|
-
|
|
8
|
+
PLUGINS_DIR,
|
|
9
9
|
} from '@bevel-software/platform-shared';
|
|
10
10
|
import { CoreConfig } from '../core-config.js';
|
|
11
11
|
import { getDb, type Database } from '../modules/database/connection.js';
|
|
@@ -13,7 +13,11 @@ import { runCoreMigrations } from '../modules/database/migrate.js';
|
|
|
13
13
|
import { coreMigrationsDir } from '../assets.js';
|
|
14
14
|
import { WorkspaceService } from '../modules/workspace/workspace.service.js';
|
|
15
15
|
import { RoutineWritePolicyService } from '../modules/workspace/routine-write-policy.js';
|
|
16
|
-
import {
|
|
16
|
+
import { KbStartupRunner } from '../modules/workspace/startup/kb-startup-runner.js';
|
|
17
|
+
import { GroupsToPluginsStep } from '../modules/workspace/startup/steps/groups-to-plugins.step.js';
|
|
18
|
+
import { TemplateFilesStep } from '../modules/workspace/startup/steps/template-files.step.js';
|
|
19
|
+
import { RolesYamlStep } from '../modules/workspace/startup/steps/roles-yaml.step.js';
|
|
20
|
+
import { buildSeedTree } from '../modules/workspace/startup/steps/seed-tree.js';
|
|
17
21
|
import { DeploymentSettingsService } from '../modules/settings/deployment-settings.service.js';
|
|
18
22
|
|
|
19
23
|
/** `a.com, b.com` → `['a.com','b.com']`, tolerating a leading `@` or `.`. */
|
|
@@ -33,7 +37,8 @@ import { AccessControlService } from '../modules/access/access-control.service.j
|
|
|
33
37
|
import { CreatorAccessService } from '../modules/access/creator-access.js';
|
|
34
38
|
import { PendingSkillsService, SkillService } from '../modules/skills/index.js';
|
|
35
39
|
import { ToolManualService } from '../modules/tool-manuals/index.js';
|
|
36
|
-
import {
|
|
40
|
+
import { McpServerEditService } from '../modules/tool-manuals/mcp-server-edit.service.js';
|
|
41
|
+
import { PluginIndexService, PluginProvisionService, JoinRequestsService } from '../modules/plugins/index.js';
|
|
37
42
|
import {
|
|
38
43
|
DbSecretsVaultService,
|
|
39
44
|
McpOAuthDiscoveryService,
|
|
@@ -93,7 +98,12 @@ export interface CoreServices {
|
|
|
93
98
|
config: CoreConfig;
|
|
94
99
|
db: Database;
|
|
95
100
|
workspaceService: WorkspaceService;
|
|
96
|
-
|
|
101
|
+
/**
|
|
102
|
+
* The KB startup phase (see `startup/on-server-start.ts`): run at the
|
|
103
|
+
* deployment's two quiet moments — boot, before routes mount, and
|
|
104
|
+
* first-time setup completion — and never again while the process serves.
|
|
105
|
+
*/
|
|
106
|
+
kbStartupRunner: KbStartupRunner;
|
|
97
107
|
spillStore: SpillStore;
|
|
98
108
|
accessControl: AccessControlService;
|
|
99
109
|
creatorAccess: CreatorAccessService;
|
|
@@ -102,8 +112,9 @@ export interface CoreServices {
|
|
|
102
112
|
skillService: SkillService;
|
|
103
113
|
pendingSkillsService: PendingSkillsService;
|
|
104
114
|
toolManualService: ToolManualService;
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
mcpServerEditService: McpServerEditService;
|
|
116
|
+
pluginIndexService: PluginIndexService;
|
|
117
|
+
pluginProvisionService: PluginProvisionService;
|
|
107
118
|
joinRequestsService: JoinRequestsService;
|
|
108
119
|
authService: AuthService;
|
|
109
120
|
authMiddleware: ReturnType<typeof createAuthMiddleware>;
|
|
@@ -211,27 +222,39 @@ export async function createCoreServices(
|
|
|
211
222
|
kbDirName,
|
|
212
223
|
() => settings.resolve('gitUsername') || 'x-access-token',
|
|
213
224
|
);
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
// The KB startup phase: every seeding, scaffolding and migration concern,
|
|
226
|
+
// run through one runner at the deployment's quiet moments (boot + setup
|
|
227
|
+
// completion) instead of lazily on branch loads. Root folders this
|
|
228
|
+
// distribution reserves on top of core's two are a plain value, not a
|
|
229
|
+
// getter: they are named in the composition root rather than collected on
|
|
230
|
+
// the setup screen, so there is nothing to re-read.
|
|
231
|
+
const extraDirs = ports.kbExtraRootDirs ?? [];
|
|
232
|
+
// Core's steps first, in dependency order — the Groups→Plugins migration
|
|
233
|
+
// reshapes trees the template top-up would otherwise re-scaffold — then
|
|
234
|
+
// whatever the distribution appends.
|
|
235
|
+
const kbStartupSteps = [
|
|
236
|
+
new GroupsToPluginsStep(),
|
|
237
|
+
new TemplateFilesStep(extraDirs),
|
|
238
|
+
new RolesYamlStep([config.adminEmail]),
|
|
239
|
+
...(ports.kbStartupSteps ?? []),
|
|
240
|
+
];
|
|
241
|
+
const kbStartupRunner = new KbStartupRunner({
|
|
242
|
+
// Getters, not values: the remote and the branch model can arrive from the
|
|
243
|
+
// setup screen after this object exists, and the setup-completion run is
|
|
244
|
+
// the first thing that needs them.
|
|
245
|
+
kbRepoUrl: () => settings.resolve('kbRepoUrl'),
|
|
246
|
+
gitUsername: () => settings.resolve('gitUsername') || 'x-access-token',
|
|
247
|
+
workspacesRoot: config.workspacesRoot,
|
|
248
|
+
kbDirName,
|
|
249
|
+
templateDir: config.kbTemplateDir,
|
|
250
|
+
defaultBranch: () => DEFAULT_BRANCH,
|
|
251
|
+
protectedBranches: () => [...PROTECTED_BRANCHES],
|
|
225
252
|
// The deployment owner is the initial Admin of a freshly seeded KB — the
|
|
226
253
|
// same answer `SEED_ADMIN_EMAILS` used to ask for a second time.
|
|
227
|
-
[config.adminEmail],
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// collected on the setup screen, so there is nothing to re-read.
|
|
232
|
-
ports.kbExtraRootDirs ?? [],
|
|
233
|
-
);
|
|
234
|
-
workspaceService.setSeedService(kbSeedService);
|
|
254
|
+
seedAdminEmails: [config.adminEmail],
|
|
255
|
+
steps: kbStartupSteps,
|
|
256
|
+
buildSeedTree: buildSeedTree(config.kbTemplateDir, extraDirs, [config.adminEmail]),
|
|
257
|
+
});
|
|
235
258
|
// Shared, workspace-independent store for oversized `call_tool_chain` results,
|
|
236
259
|
// read back via `read_file`. Sibling of `workspacesRoot`, never committed.
|
|
237
260
|
const spillStore = new SpillStore(config.spillRoot);
|
|
@@ -260,15 +283,15 @@ export async function createCoreServices(
|
|
|
260
283
|
const routineWritePolicy = new RoutineWritePolicyService();
|
|
261
284
|
// Skills: discovered from the default-branch workspace only (global catalog).
|
|
262
285
|
const skillService = new SkillService(workspaceService, accessControl, kbDirName);
|
|
263
|
-
// Tool manuals: user-authored `*.tool` files under `
|
|
286
|
+
// Tool manuals: user-authored `*.tool` files under `Plugins/` in the default
|
|
264
287
|
// branch — access-controlled like Skills, served to external agents via
|
|
265
288
|
// `GET /api/agent/all-tools` and registered on the MCP proxy's UTCP client.
|
|
266
289
|
const toolManualService = new ToolManualService(workspaceService, accessControl, kbDirName);
|
|
267
|
-
//
|
|
290
|
+
// Plugins: the folders under `Plugins/` that carry a
|
|
268
291
|
// team's skills AND the tools they need. Enumerated for EVERY authenticated
|
|
269
|
-
// caller — a
|
|
292
|
+
// caller — a plugin they cannot read still exists for them, as a locked one —
|
|
270
293
|
// with the counts read off the two catalogs above rather than a second scan.
|
|
271
|
-
const
|
|
294
|
+
const pluginIndexService = new PluginIndexService(
|
|
272
295
|
workspaceService,
|
|
273
296
|
accessControl,
|
|
274
297
|
skillService,
|
|
@@ -384,16 +407,28 @@ export async function createCoreServices(
|
|
|
384
407
|
workflowHooks,
|
|
385
408
|
);
|
|
386
409
|
|
|
387
|
-
// Join requests: derived entirely from two copies of a
|
|
410
|
+
// Join requests: derived entirely from two copies of a plugin's `access.md`
|
|
388
411
|
// (the request's branch vs the default branch), so it holds no state — it
|
|
389
412
|
// only needs to read files at refs and to close a request whose proposals
|
|
390
413
|
// have all landed.
|
|
391
414
|
const joinRequestsService = new JoinRequestsService(workspaceService, workflowService);
|
|
392
|
-
//
|
|
393
|
-
//
|
|
415
|
+
// Server-scoped MCP editing — the tool page's edit form. One server's truth
|
|
416
|
+
// spans a plugin's mcp.json AND plugin.json extensions block, and this is
|
|
417
|
+
// the ONE writer that rewrites both entries and commits them together (see
|
|
418
|
+
// McpServerEditService).
|
|
419
|
+
const mcpServerEditService = new McpServerEditService(
|
|
420
|
+
workspaceService,
|
|
421
|
+
workflowService,
|
|
422
|
+
accessControl,
|
|
423
|
+
toolManualService,
|
|
424
|
+
kbDirName,
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
// Plugin provisioning — the one privileged door that brings `Plugins/<name>/`
|
|
428
|
+
// folders into existence (named plugins and personal folders alike). Commits
|
|
394
429
|
// INLINE through the same pipeline the pending-commits worker uses, so the
|
|
395
430
|
// folder's rules are at HEAD before the endpoint answers.
|
|
396
|
-
const
|
|
431
|
+
const pluginProvisionService = new PluginProvisionService(
|
|
397
432
|
workspaceService,
|
|
398
433
|
workflowService,
|
|
399
434
|
accessControl,
|
|
@@ -422,16 +457,16 @@ export async function createCoreServices(
|
|
|
422
457
|
// caches are independent, so the split preserves behavior.)
|
|
423
458
|
fileChangeNotifier.onFilesChanged(({ branch, paths }) => {
|
|
424
459
|
if (branch !== DEFAULT_BRANCH) return;
|
|
425
|
-
// Skills, tools and the
|
|
460
|
+
// Skills, tools and the plugin index all live under `Plugins/`, so one
|
|
426
461
|
// touch check drives all three caches. An access grant lands as a
|
|
427
|
-
// default-branch change to `
|
|
428
|
-
// what makes a newly-granted
|
|
462
|
+
// default-branch change to `Plugins/<plugin>/access.md`, so this is also
|
|
463
|
+
// what makes a newly-granted plugin unlock within one round-trip instead
|
|
429
464
|
// of one TTL.
|
|
430
|
-
const touched = paths.some((p) => p.startsWith(`${kbDirName}/${
|
|
465
|
+
const touched = paths.some((p) => p.startsWith(`${kbDirName}/${PLUGINS_DIR}/`));
|
|
431
466
|
if (touched) {
|
|
432
467
|
toolManualService.invalidate();
|
|
433
468
|
skillService.invalidate();
|
|
434
|
-
|
|
469
|
+
pluginIndexService.invalidate();
|
|
435
470
|
}
|
|
436
471
|
});
|
|
437
472
|
|
|
@@ -448,7 +483,7 @@ export async function createCoreServices(
|
|
|
448
483
|
if (!('branch' in event) || event.branch !== DEFAULT_BRANCH) return;
|
|
449
484
|
toolManualService.invalidate();
|
|
450
485
|
skillService.invalidate();
|
|
451
|
-
|
|
486
|
+
pluginIndexService.invalidate();
|
|
452
487
|
});
|
|
453
488
|
|
|
454
489
|
// Admin = `Admin` role in roles.yaml, resolved through the access model on the
|
|
@@ -567,7 +602,12 @@ export async function createCoreServices(
|
|
|
567
602
|
});
|
|
568
603
|
// RFC 9728 pointer carried on every MCP 401 challenge so OAuth-capable
|
|
569
604
|
// clients discover the AS. Single source of truth for the resource id.
|
|
605
|
+
// Userinfo is stripped for the same reason the server's own copy strips it
|
|
606
|
+
// (see create-core-server.ts): the pointer rides an unauthenticated 401
|
|
607
|
+
// challenge, and a `user:pass@` from PUBLIC_BACKEND_URL must not ride along.
|
|
570
608
|
const mcpResourceUrl = new URL('/api/mcp', config.publicBackendUrl);
|
|
609
|
+
mcpResourceUrl.username = '';
|
|
610
|
+
mcpResourceUrl.password = '';
|
|
571
611
|
const mcpResourceMetadataUrl = getOAuthProtectedResourceMetadataUrl(mcpResourceUrl);
|
|
572
612
|
const mcpAuthMiddleware = createMcpAuthMiddleware(
|
|
573
613
|
authService,
|
|
@@ -675,8 +715,9 @@ export async function createCoreServices(
|
|
|
675
715
|
return {
|
|
676
716
|
config,
|
|
677
717
|
db,
|
|
718
|
+
mcpServerEditService,
|
|
678
719
|
workspaceService,
|
|
679
|
-
|
|
720
|
+
kbStartupRunner,
|
|
680
721
|
settings,
|
|
681
722
|
kbDirName,
|
|
682
723
|
spillStore,
|
|
@@ -687,8 +728,8 @@ export async function createCoreServices(
|
|
|
687
728
|
skillService,
|
|
688
729
|
pendingSkillsService,
|
|
689
730
|
toolManualService,
|
|
690
|
-
|
|
691
|
-
|
|
731
|
+
pluginIndexService,
|
|
732
|
+
pluginProvisionService,
|
|
692
733
|
joinRequestsService,
|
|
693
734
|
authService,
|
|
694
735
|
authMiddleware,
|
package/src/index.ts
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @bevel-software/platform-core-backend — the open-source core of the Bevel platform:
|
|
3
|
-
* git-backed knowledge workspace, workflow (branches / change requests /
|
|
4
|
-
* locks / SSE), skills, tool manuals + registry + auth, secrets vault, access
|
|
5
|
-
* control, diff/backup ledger, code-mode and the remote MCP surface.
|
|
6
|
-
*
|
|
7
|
-
* Composition entry points:
|
|
8
|
-
* - `new CoreConfig()` — env-driven core configuration
|
|
9
|
-
* - `createCoreServices(config, ports)` — construct every core service
|
|
10
|
-
* - `createCoreServer(core, ext, opts)` — the Express app (fixed mount order)
|
|
11
|
-
*
|
|
12
|
-
* Importing this package ALSO applies the global Express `Request`
|
|
13
|
-
* augmentation (`req.userId` / `req.userEmail` / token payloads) — the
|
|
14
|
-
* middleware modules re-exported below carry the `declare global` blocks, so
|
|
15
|
-
* consumers get the augmented types without a side-effect import of their own.
|
|
16
|
-
*
|
|
17
|
-
* Finer-grained internals are available via the `./modules/*` subpath exports.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
export { CoreConfig } from './core-config.js';
|
|
21
|
-
export { createCoreServices, type CoreServices } from './core/create-core-services.js';
|
|
22
|
-
export {
|
|
23
|
-
createCoreServer,
|
|
24
|
-
type ServerExtensions,
|
|
25
|
-
type ToolSurfaceCtx,
|
|
26
|
-
} from './core/create-core-server.js';
|
|
27
|
-
export { noopRecoveryAgent, type CorePorts } from './core/core-ports.js';
|
|
28
|
-
|
|
29
|
-
// Packaged assets (migrations/, kb-template/) + the migration runners.
|
|
30
|
-
export { coreMigrationsDir, defaultKbTemplateDir } from './assets.js';
|
|
31
|
-
export {
|
|
32
|
-
runCoreMigrations,
|
|
33
|
-
runEnterpriseMigrations,
|
|
34
|
-
} from './modules/database/migrate.js';
|
|
35
|
-
export { getDb, type Database } from './modules/database/connection.js';
|
|
36
|
-
|
|
37
|
-
// Build identity surfaced by GET /api/health.
|
|
38
|
-
export { GIT_SHA, resolveGitSha } from './version.js';
|
|
39
|
-
|
|
40
|
-
// Express `Request` augmentation carriers (userId/userEmail — auth.middleware;
|
|
41
|
-
// token payloads — tool-auth + external-api-key interface). Re-exported by
|
|
42
|
-
// VALUE/type so the `declare global` blocks land in this package's public
|
|
43
|
-
// declaration graph.
|
|
44
|
-
export { createAuthMiddleware, AUTH_COOKIE_NAME } from './modules/auth/auth.middleware.js';
|
|
45
|
-
export {
|
|
46
|
-
createToolAuthMiddleware,
|
|
47
|
-
createManualAuthMiddleware,
|
|
48
|
-
} from './modules/tool-auth/tool-auth.middleware.js';
|
|
49
|
-
export type {
|
|
50
|
-
IExternalApiKeyService,
|
|
51
|
-
ExternalApiKeySummary,
|
|
52
|
-
} from './modules/tool-auth/external-api-key.interface.js';
|
|
53
|
-
|
|
54
|
-
// Key port/seam types an overlay implements.
|
|
55
|
-
export type { ISessionSink } from './modules/workspace/session-sink.js';
|
|
56
|
-
export type {
|
|
57
|
-
ISystemNoticeSink,
|
|
58
|
-
RecoveryAgentRunner,
|
|
59
|
-
} from './modules/workflow/pending-commits.worker.js';
|
|
60
|
-
export type { ILlmUsageMeter } from './modules/tool-auth/llm-usage-meter.js';
|
|
61
|
-
export type { AuthProviderPlugin } from './modules/auth/auth.routes.js';
|
|
62
|
-
export type { IErasureParticipant } from './modules/auth/account-erasure.service.js';
|
|
63
|
-
export {
|
|
64
|
-
WorkflowHooks,
|
|
65
|
-
type CommitValidationHook,
|
|
66
|
-
type CommitValidationContext,
|
|
67
|
-
type PreWriteHook,
|
|
68
|
-
type PreWriteContext,
|
|
69
|
-
} from './modules/workflow/workflow-hooks.js';
|
|
1
|
+
/**
|
|
2
|
+
* @bevel-software/platform-core-backend — the open-source core of the Bevel platform:
|
|
3
|
+
* git-backed knowledge workspace, workflow (branches / change requests /
|
|
4
|
+
* locks / SSE), skills, tool manuals + registry + auth, secrets vault, access
|
|
5
|
+
* control, diff/backup ledger, code-mode and the remote MCP surface.
|
|
6
|
+
*
|
|
7
|
+
* Composition entry points:
|
|
8
|
+
* - `new CoreConfig()` — env-driven core configuration
|
|
9
|
+
* - `createCoreServices(config, ports)` — construct every core service
|
|
10
|
+
* - `createCoreServer(core, ext, opts)` — the Express app (fixed mount order)
|
|
11
|
+
*
|
|
12
|
+
* Importing this package ALSO applies the global Express `Request`
|
|
13
|
+
* augmentation (`req.userId` / `req.userEmail` / token payloads) — the
|
|
14
|
+
* middleware modules re-exported below carry the `declare global` blocks, so
|
|
15
|
+
* consumers get the augmented types without a side-effect import of their own.
|
|
16
|
+
*
|
|
17
|
+
* Finer-grained internals are available via the `./modules/*` subpath exports.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export { CoreConfig } from './core-config.js';
|
|
21
|
+
export { createCoreServices, type CoreServices } from './core/create-core-services.js';
|
|
22
|
+
export {
|
|
23
|
+
createCoreServer,
|
|
24
|
+
type ServerExtensions,
|
|
25
|
+
type ToolSurfaceCtx,
|
|
26
|
+
} from './core/create-core-server.js';
|
|
27
|
+
export { noopRecoveryAgent, type CorePorts } from './core/core-ports.js';
|
|
28
|
+
|
|
29
|
+
// Packaged assets (migrations/, kb-template/) + the migration runners.
|
|
30
|
+
export { coreMigrationsDir, defaultKbTemplateDir } from './assets.js';
|
|
31
|
+
export {
|
|
32
|
+
runCoreMigrations,
|
|
33
|
+
runEnterpriseMigrations,
|
|
34
|
+
} from './modules/database/migrate.js';
|
|
35
|
+
export { getDb, type Database } from './modules/database/connection.js';
|
|
36
|
+
|
|
37
|
+
// Build identity surfaced by GET /api/health.
|
|
38
|
+
export { GIT_SHA, resolveGitSha } from './version.js';
|
|
39
|
+
|
|
40
|
+
// Express `Request` augmentation carriers (userId/userEmail — auth.middleware;
|
|
41
|
+
// token payloads — tool-auth + external-api-key interface). Re-exported by
|
|
42
|
+
// VALUE/type so the `declare global` blocks land in this package's public
|
|
43
|
+
// declaration graph.
|
|
44
|
+
export { createAuthMiddleware, AUTH_COOKIE_NAME } from './modules/auth/auth.middleware.js';
|
|
45
|
+
export {
|
|
46
|
+
createToolAuthMiddleware,
|
|
47
|
+
createManualAuthMiddleware,
|
|
48
|
+
} from './modules/tool-auth/tool-auth.middleware.js';
|
|
49
|
+
export type {
|
|
50
|
+
IExternalApiKeyService,
|
|
51
|
+
ExternalApiKeySummary,
|
|
52
|
+
} from './modules/tool-auth/external-api-key.interface.js';
|
|
53
|
+
|
|
54
|
+
// Key port/seam types an overlay implements.
|
|
55
|
+
export type { ISessionSink } from './modules/workspace/session-sink.js';
|
|
56
|
+
export type {
|
|
57
|
+
ISystemNoticeSink,
|
|
58
|
+
RecoveryAgentRunner,
|
|
59
|
+
} from './modules/workflow/pending-commits.worker.js';
|
|
60
|
+
export type { ILlmUsageMeter } from './modules/tool-auth/llm-usage-meter.js';
|
|
61
|
+
export type { AuthProviderPlugin } from './modules/auth/auth.routes.js';
|
|
62
|
+
export type { IErasureParticipant } from './modules/auth/account-erasure.service.js';
|
|
63
|
+
export {
|
|
64
|
+
WorkflowHooks,
|
|
65
|
+
type CommitValidationHook,
|
|
66
|
+
type CommitValidationContext,
|
|
67
|
+
type PreWriteHook,
|
|
68
|
+
type PreWriteContext,
|
|
69
|
+
} from './modules/workflow/workflow-hooks.js';
|
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import { execFile } from 'node:child_process';
|
|
3
|
-
import { promisify } from 'node:util';
|
|
4
|
-
import fs from 'node:fs/promises';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import os from 'node:os';
|
|
7
|
-
|
|
8
|
-
import type { WorkspaceService } from '../../workspace/workspace.service.js';
|
|
9
|
-
import { AccessControlService } from '../access-control.service.js';
|
|
10
|
-
|
|
11
|
-
const execFileAsync = promisify(execFile);
|
|
12
|
-
const PROCESS_MAP_DIR = 'knowledge-base';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* `canWriteBatchAtRef` / the batched at-ref own-entries read — backed by ONE
|
|
16
|
-
* `git cat-file --batch` process whose length-prefixed output we parse by
|
|
17
|
-
* hand, so exercise it against a REAL git repo: found blobs, a path missing
|
|
18
|
-
* at the ref (the `<spec> missing` line), a path with spaces, and frontmatter
|
|
19
|
-
* deny resolution — all in one batch, order-aligned.
|
|
20
|
-
*/
|
|
21
|
-
describe('AccessControlService — at-ref batch reads (git cat-file --batch)', () => {
|
|
22
|
-
let root: string;
|
|
23
|
-
let repo: string;
|
|
24
|
-
let svc: AccessControlService;
|
|
25
|
-
const workspaceId = 'ws-atref-1';
|
|
26
|
-
const admin = 'razvan@bevel.software';
|
|
27
|
-
|
|
28
|
-
async function git(...args: string[]): Promise<void> {
|
|
29
|
-
await execFileAsync('git', ['-C', repo, ...args]);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
beforeAll(async () => {
|
|
33
|
-
root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-atref-'));
|
|
34
|
-
const workspaceDir = path.join(root, workspaceId);
|
|
35
|
-
repo = path.join(workspaceDir, PROCESS_MAP_DIR);
|
|
36
|
-
await fs.mkdir(path.join(repo, 'Knowledge'), { recursive: true });
|
|
37
|
-
|
|
38
|
-
await fs.writeFile(path.join(repo, 'roles.yaml'), 'roles:\n Admin:\n - razvan@bevel.software\n');
|
|
39
|
-
await fs.writeFile(path.join(repo, 'access.md'), '---\nwrite:\n - Admin\n---\n');
|
|
40
|
-
await fs.writeFile(path.join(repo, 'Knowledge/plain.md'), '# plain\n');
|
|
41
|
-
await fs.writeFile(
|
|
42
|
-
path.join(repo, 'Knowledge/denied.md'),
|
|
43
|
-
'---\nwrite:\n - deny razvan <razvan@bevel.software>\n---\n# denied\n',
|
|
44
|
-
);
|
|
45
|
-
await fs.writeFile(path.join(repo, 'Knowledge/with space.md'), '# spaced\n');
|
|
46
|
-
|
|
47
|
-
await execFileAsync('git', ['init', '-b', 'main', repo]);
|
|
48
|
-
await git('config', 'user.email', 'test@example.com');
|
|
49
|
-
await git('config', 'user.name', 'Test');
|
|
50
|
-
await git('add', '-A');
|
|
51
|
-
await git('commit', '-m', 'seed');
|
|
52
|
-
|
|
53
|
-
const stub = {
|
|
54
|
-
getWorkspacePath: async () => workspaceDir,
|
|
55
|
-
// Fixture repo has no remote; at-ref model loading fetches best-effort.
|
|
56
|
-
ensureRemotesFetched: async () => undefined,
|
|
57
|
-
} as unknown as WorkspaceService;
|
|
58
|
-
svc = new AccessControlService(stub, PROCESS_MAP_DIR);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
afterAll(async () => {
|
|
62
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('resolves a mixed batch in one pass: grants, frontmatter deny, spaces, and a missing path', async () => {
|
|
66
|
-
const paths = [
|
|
67
|
-
'Knowledge/plain.md', // dir-chain Admin grant → true
|
|
68
|
-
'Knowledge/denied.md', // own frontmatter deny → false
|
|
69
|
-
'Knowledge/with space.md', // found blob whose SPEC contains spaces → true
|
|
70
|
-
'Knowledge/not-there.md', // missing at the ref (`<spec> missing` line) → dir-chain verdict
|
|
71
|
-
];
|
|
72
|
-
const map = await svc.canWriteBatchAtRef(workspaceId, 'main', admin, paths);
|
|
73
|
-
expect(map).not.toBeNull();
|
|
74
|
-
expect(map!.get('Knowledge/plain.md')).toBe(true);
|
|
75
|
-
expect(map!.get('Knowledge/denied.md')).toBe(false);
|
|
76
|
-
expect(map!.get('Knowledge/with space.md')).toBe(true);
|
|
77
|
-
expect(map!.get('Knowledge/not-there.md')).toBe(true);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('matches the single-path at-ref resolution exactly', async () => {
|
|
81
|
-
// The batch is a pure performance change — verdicts must be identical to
|
|
82
|
-
// the per-path `git show` route the single-path helper still uses.
|
|
83
|
-
expect(await svc.canWriteAtRef(workspaceId, 'main', admin, 'Knowledge/denied.md')).toBe(false);
|
|
84
|
-
expect(await svc.canWriteAtRef(workspaceId, 'main', admin, 'Knowledge/plain.md')).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('eligibleWritersForPathsAtRef honours the same batched own-entries', async () => {
|
|
88
|
-
const map = await svc.eligibleWritersForPathsAtRef(workspaceId, 'main', [
|
|
89
|
-
'Knowledge/plain.md',
|
|
90
|
-
'Knowledge/denied.md',
|
|
91
|
-
]);
|
|
92
|
-
expect(map).not.toBeNull();
|
|
93
|
-
expect(map!.get('Knowledge/plain.md')!.roles).toContain('Admin');
|
|
94
|
-
// The per-user deny strips razvan from the expanded email set.
|
|
95
|
-
expect(map!.get('Knowledge/denied.md')!.emails.has(admin)).toBe(false);
|
|
96
|
-
expect(map!.get('Knowledge/plain.md')!.emails.has(admin)).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import fs from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
|
|
8
|
+
import type { WorkspaceService } from '../../workspace/workspace.service.js';
|
|
9
|
+
import { AccessControlService } from '../access-control.service.js';
|
|
10
|
+
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
const PROCESS_MAP_DIR = 'knowledge-base';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `canWriteBatchAtRef` / the batched at-ref own-entries read — backed by ONE
|
|
16
|
+
* `git cat-file --batch` process whose length-prefixed output we parse by
|
|
17
|
+
* hand, so exercise it against a REAL git repo: found blobs, a path missing
|
|
18
|
+
* at the ref (the `<spec> missing` line), a path with spaces, and frontmatter
|
|
19
|
+
* deny resolution — all in one batch, order-aligned.
|
|
20
|
+
*/
|
|
21
|
+
describe('AccessControlService — at-ref batch reads (git cat-file --batch)', () => {
|
|
22
|
+
let root: string;
|
|
23
|
+
let repo: string;
|
|
24
|
+
let svc: AccessControlService;
|
|
25
|
+
const workspaceId = 'ws-atref-1';
|
|
26
|
+
const admin = 'razvan@bevel.software';
|
|
27
|
+
|
|
28
|
+
async function git(...args: string[]): Promise<void> {
|
|
29
|
+
await execFileAsync('git', ['-C', repo, ...args]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
beforeAll(async () => {
|
|
33
|
+
root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-atref-'));
|
|
34
|
+
const workspaceDir = path.join(root, workspaceId);
|
|
35
|
+
repo = path.join(workspaceDir, PROCESS_MAP_DIR);
|
|
36
|
+
await fs.mkdir(path.join(repo, 'Knowledge'), { recursive: true });
|
|
37
|
+
|
|
38
|
+
await fs.writeFile(path.join(repo, 'roles.yaml'), 'roles:\n Admin:\n - razvan@bevel.software\n');
|
|
39
|
+
await fs.writeFile(path.join(repo, 'access.md'), '---\nwrite:\n - Admin\n---\n');
|
|
40
|
+
await fs.writeFile(path.join(repo, 'Knowledge/plain.md'), '# plain\n');
|
|
41
|
+
await fs.writeFile(
|
|
42
|
+
path.join(repo, 'Knowledge/denied.md'),
|
|
43
|
+
'---\nwrite:\n - deny razvan <razvan@bevel.software>\n---\n# denied\n',
|
|
44
|
+
);
|
|
45
|
+
await fs.writeFile(path.join(repo, 'Knowledge/with space.md'), '# spaced\n');
|
|
46
|
+
|
|
47
|
+
await execFileAsync('git', ['init', '-b', 'main', repo]);
|
|
48
|
+
await git('config', 'user.email', 'test@example.com');
|
|
49
|
+
await git('config', 'user.name', 'Test');
|
|
50
|
+
await git('add', '-A');
|
|
51
|
+
await git('commit', '-m', 'seed');
|
|
52
|
+
|
|
53
|
+
const stub = {
|
|
54
|
+
getWorkspacePath: async () => workspaceDir,
|
|
55
|
+
// Fixture repo has no remote; at-ref model loading fetches best-effort.
|
|
56
|
+
ensureRemotesFetched: async () => undefined,
|
|
57
|
+
} as unknown as WorkspaceService;
|
|
58
|
+
svc = new AccessControlService(stub, PROCESS_MAP_DIR);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterAll(async () => {
|
|
62
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('resolves a mixed batch in one pass: grants, frontmatter deny, spaces, and a missing path', async () => {
|
|
66
|
+
const paths = [
|
|
67
|
+
'Knowledge/plain.md', // dir-chain Admin grant → true
|
|
68
|
+
'Knowledge/denied.md', // own frontmatter deny → false
|
|
69
|
+
'Knowledge/with space.md', // found blob whose SPEC contains spaces → true
|
|
70
|
+
'Knowledge/not-there.md', // missing at the ref (`<spec> missing` line) → dir-chain verdict
|
|
71
|
+
];
|
|
72
|
+
const map = await svc.canWriteBatchAtRef(workspaceId, 'main', admin, paths);
|
|
73
|
+
expect(map).not.toBeNull();
|
|
74
|
+
expect(map!.get('Knowledge/plain.md')).toBe(true);
|
|
75
|
+
expect(map!.get('Knowledge/denied.md')).toBe(false);
|
|
76
|
+
expect(map!.get('Knowledge/with space.md')).toBe(true);
|
|
77
|
+
expect(map!.get('Knowledge/not-there.md')).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('matches the single-path at-ref resolution exactly', async () => {
|
|
81
|
+
// The batch is a pure performance change — verdicts must be identical to
|
|
82
|
+
// the per-path `git show` route the single-path helper still uses.
|
|
83
|
+
expect(await svc.canWriteAtRef(workspaceId, 'main', admin, 'Knowledge/denied.md')).toBe(false);
|
|
84
|
+
expect(await svc.canWriteAtRef(workspaceId, 'main', admin, 'Knowledge/plain.md')).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('eligibleWritersForPathsAtRef honours the same batched own-entries', async () => {
|
|
88
|
+
const map = await svc.eligibleWritersForPathsAtRef(workspaceId, 'main', [
|
|
89
|
+
'Knowledge/plain.md',
|
|
90
|
+
'Knowledge/denied.md',
|
|
91
|
+
]);
|
|
92
|
+
expect(map).not.toBeNull();
|
|
93
|
+
expect(map!.get('Knowledge/plain.md')!.roles).toContain('Admin');
|
|
94
|
+
// The per-user deny strips razvan from the expanded email set.
|
|
95
|
+
expect(map!.get('Knowledge/denied.md')!.emails.has(admin)).toBe(false);
|
|
96
|
+
expect(map!.get('Knowledge/plain.md')!.emails.has(admin)).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|