@bevel-software/platform-core-backend 0.20.0 → 0.22.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/dist/core/catalog-cache-invalidation.d.ts +7 -2
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
- package/dist/core/catalog-cache-invalidation.js +8 -9
- package/dist/core/catalog-cache-invalidation.js.map +1 -1
- package/dist/core/core-ports.d.ts +19 -0
- 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 +2 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +55 -107
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +41 -4
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +183 -82
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +74 -28
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +157 -20
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/public-config.d.ts +5 -1
- package/dist/core/public-config.d.ts.map +1 -1
- package/dist/core/public-config.js +7 -5
- package/dist/core/public-config.js.map +1 -1
- package/dist/core-config.d.ts +95 -1
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +24 -8
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +18 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +19 -8
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-requests.contract.d.ts +75 -0
- package/dist/modules/access/access-requests.contract.d.ts.map +1 -0
- package/dist/modules/access/access-requests.contract.js +20 -0
- package/dist/modules/access/access-requests.contract.js.map +1 -0
- package/dist/modules/access/access-requests.routes.d.ts +35 -0
- package/dist/modules/access/access-requests.routes.d.ts.map +1 -0
- package/dist/modules/access/access-requests.routes.js +237 -0
- package/dist/modules/access/access-requests.routes.js.map +1 -0
- package/dist/modules/access/access-requests.service.d.ts +123 -0
- package/dist/modules/access/access-requests.service.d.ts.map +1 -0
- package/dist/modules/access/access-requests.service.js +337 -0
- package/dist/modules/access/access-requests.service.js.map +1 -0
- package/dist/modules/access/access.routes.d.ts +2 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +10 -10
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/change-read-gate.d.ts +6 -3
- package/dist/modules/access/change-read-gate.d.ts.map +1 -1
- package/dist/modules/access/change-read-gate.js +9 -6
- package/dist/modules/access/change-read-gate.js.map +1 -1
- package/dist/modules/access/creator-access.d.ts +4 -2
- package/dist/modules/access/creator-access.d.ts.map +1 -1
- package/dist/modules/access/creator-access.js +7 -4
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +12 -0
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +24 -0
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/agent-instructions/read-preamble.d.ts +2 -1
- package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -1
- package/dist/modules/agent-instructions/read-preamble.js +6 -9
- package/dist/modules/agent-instructions/read-preamble.js.map +1 -1
- package/dist/modules/auth/account-admission.d.ts +40 -0
- package/dist/modules/auth/account-admission.d.ts.map +1 -0
- package/dist/modules/auth/account-admission.js +17 -0
- package/dist/modules/auth/account-admission.js.map +1 -0
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +4 -1
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +45 -2
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +44 -3
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +9 -0
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +10 -2
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/advisory-lock.d.ts +17 -0
- package/dist/modules/database/advisory-lock.d.ts.map +1 -1
- package/dist/modules/database/advisory-lock.js +24 -3
- package/dist/modules/database/advisory-lock.js.map +1 -1
- package/dist/modules/database/connection.d.ts +67 -2
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +118 -12
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/migrate.d.ts +1 -1
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +27 -3
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/github-app/github-app.client.d.ts +101 -0
- package/dist/modules/github-app/github-app.client.d.ts.map +1 -0
- package/dist/modules/github-app/github-app.client.js +222 -0
- package/dist/modules/github-app/github-app.client.js.map +1 -0
- package/dist/modules/github-app/github-app.connection.d.ts +102 -0
- package/dist/modules/github-app/github-app.connection.d.ts.map +1 -0
- package/dist/modules/github-app/github-app.connection.js +185 -0
- package/dist/modules/github-app/github-app.connection.js.map +1 -0
- package/dist/modules/github-app/github-app.routes.d.ts +59 -0
- package/dist/modules/github-app/github-app.routes.d.ts.map +1 -0
- package/dist/modules/github-app/github-app.routes.js +323 -0
- package/dist/modules/github-app/github-app.routes.js.map +1 -0
- package/dist/modules/github-app/index.d.ts +4 -0
- package/dist/modules/github-app/index.d.ts.map +1 -0
- package/dist/modules/github-app/index.js +4 -0
- package/dist/modules/github-app/index.js.map +1 -0
- package/dist/modules/kb-fs/branch-name.d.ts +0 -1
- package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
- package/dist/modules/kb-fs/branch-name.js +3 -3
- package/dist/modules/kb-fs/branch-name.js.map +1 -1
- package/dist/modules/kb-fs/clone-config.d.ts +9 -8
- package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
- package/dist/modules/kb-fs/clone-config.js +14 -12
- package/dist/modules/kb-fs/clone-config.js.map +1 -1
- package/dist/modules/kb-fs/remote-url.d.ts +22 -0
- package/dist/modules/kb-fs/remote-url.d.ts.map +1 -0
- package/dist/modules/kb-fs/remote-url.js +35 -0
- package/dist/modules/kb-fs/remote-url.js.map +1 -0
- package/dist/modules/mcp/mcp.service.d.ts +7 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +4 -4
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
- package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/native.source.js +4 -2
- package/dist/modules/plugins/discovery/native.source.js.map +1 -1
- package/dist/modules/plugins/index.d.ts +1 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/join-proposals.d.ts +17 -5
- package/dist/modules/plugins/join-proposals.d.ts.map +1 -1
- package/dist/modules/plugins/join-proposals.js +76 -22
- package/dist/modules/plugins/join-proposals.js.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.js +6 -6
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
- package/dist/modules/plugins/join-requests.service.d.ts +113 -18
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +178 -33
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +65 -10
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.js +136 -31
- package/dist/modules/plugins/plugin-links.js.map +1 -1
- package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.service.js +151 -21
- package/dist/modules/plugins/plugin-links.service.js.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +33 -26
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.js +10 -12
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +3 -0
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +48 -17
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +4 -4
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +7 -10
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/plugins/teams.routes.d.ts +4 -1
- package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
- package/dist/modules/plugins/teams.routes.js +11 -9
- package/dist/modules/plugins/teams.routes.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +1 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +29 -11
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
- package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +34 -9
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +113 -26
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/managed-repository.d.ts +43 -0
- package/dist/modules/settings/managed-repository.d.ts.map +1 -0
- package/dist/modules/settings/managed-repository.js +60 -0
- package/dist/modules/settings/managed-repository.js.map +1 -0
- package/dist/modules/settings/repository-source.d.ts +128 -0
- package/dist/modules/settings/repository-source.d.ts.map +1 -0
- package/dist/modules/settings/repository-source.js +150 -0
- package/dist/modules/settings/repository-source.js.map +1 -0
- package/dist/modules/settings/setup.routes.d.ts +34 -4
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +198 -18
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
- package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
- package/dist/modules/skills/allowed-tools-check.js +8 -8
- package/dist/modules/skills/allowed-tools-check.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +3 -1
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +9 -6
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +8 -9
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.js +26 -65
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +4 -1
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +4 -4
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +4 -2
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +13 -11
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +15 -0
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
- package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
- package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
- package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +47 -11
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +14 -6
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +40 -18
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +40 -5
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts +45 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +99 -0
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
- package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
- package/dist/modules/workflow/session-ontology.service.js +6 -2
- package/dist/modules/workflow/session-ontology.service.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +3 -1
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +42 -10
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +181 -71
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.js +3 -3
- package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
- package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-git.js +15 -13
- package/dist/modules/workspace/startup/kb-git.js.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +70 -7
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +183 -38
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +10 -6
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts +2 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +8 -7
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +18 -12
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +49 -29
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +2 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +48 -20
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +3 -2
- package/dist/modules/workspace/write-denial.d.ts.map +1 -1
- package/dist/modules/workspace/write-denial.js +6 -5
- package/dist/modules/workspace/write-denial.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +39 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +55 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +62 -7
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +30 -8
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/kb-context.d.ts +76 -0
- package/dist/shared/kb-context.d.ts.map +1 -0
- package/dist/shared/kb-context.js +108 -0
- package/dist/shared/kb-context.js.map +1 -0
- package/dist/shared/kb-layout.d.ts +3 -2
- package/dist/shared/kb-layout.d.ts.map +1 -1
- package/dist/shared/kb-layout.js +4 -3
- package/dist/shared/kb-layout.js.map +1 -1
- package/dist/shared/kb-layout.test.js +35 -27
- package/dist/shared/kb-layout.test.js.map +1 -1
- package/dist/shared/pending-proposals.d.ts +4 -1
- package/dist/shared/pending-proposals.d.ts.map +1 -1
- package/dist/shared/pending-proposals.js +3 -4
- package/dist/shared/pending-proposals.js.map +1 -1
- package/dist/shared/redact-secret.d.ts +5 -5
- package/dist/shared/redact-secret.js +5 -5
- package/dist/tenancy/index.d.ts +13 -0
- package/dist/tenancy/index.d.ts.map +1 -0
- package/dist/tenancy/index.js +13 -0
- package/dist/tenancy/index.js.map +1 -0
- package/dist/tenancy/static-tenant-source.d.ts +98 -0
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
- package/dist/tenancy/static-tenant-source.js +220 -0
- package/dist/tenancy/static-tenant-source.js.map +1 -0
- package/dist/tenancy/tenant-host.d.ts +80 -0
- package/dist/tenancy/tenant-host.d.ts.map +1 -0
- package/dist/tenancy/tenant-host.js +217 -0
- package/dist/tenancy/tenant-host.js.map +1 -0
- package/dist/tenancy/tenant-runtime.d.ts +91 -0
- package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
- package/dist/tenancy/tenant-runtime.js +174 -0
- package/dist/tenancy/tenant-runtime.js.map +1 -0
- package/dist/tenancy/tenant-secrets.d.ts +24 -0
- package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
- package/dist/tenancy/tenant-secrets.js +30 -0
- package/dist/tenancy/tenant-secrets.js.map +1 -0
- package/dist/tenancy/tenant-source.contract.d.ts +45 -0
- package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
- package/dist/tenancy/tenant-source.contract.js +21 -0
- package/dist/tenancy/tenant-source.contract.js.map +1 -0
- package/migrations/0000_core_init.sql +14 -14
- package/migrations/0003_deployment_settings.sql +1 -1
- package/migrations/0005_github_facade.sql +1 -1
- package/migrations/0012_agent_audit.sql +6 -6
- package/package.json +3 -3
- package/src/__tests__/branch-model.test.ts +7 -6
- package/src/__tests__/kb-context.ts +30 -0
- package/src/__tests__/kb-layout-config.test.ts +35 -30
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
- package/src/core/__tests__/lifecycle.test.ts +78 -1
- package/src/core/__tests__/public-config.test.ts +5 -1
- package/src/core/catalog-cache-invalidation.ts +16 -11
- package/src/core/core-ports.ts +19 -0
- package/src/core/create-core-server.ts +64 -107
- package/src/core/create-core-services.ts +238 -96
- package/src/core/lifecycle.ts +214 -29
- package/src/core/public-config.ts +8 -5
- package/src/core-config.ts +107 -8
- package/src/index.ts +25 -2
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
- package/src/modules/access/__tests__/access-requests.recut.test.ts +134 -0
- package/src/modules/access/__tests__/access-requests.routes.test.ts +610 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
- package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
- package/src/modules/access/__tests__/creator-access.test.ts +3 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
- package/src/modules/access/access-control.service.ts +20 -6
- package/src/modules/access/access-requests.contract.ts +93 -0
- package/src/modules/access/access-requests.routes.ts +286 -0
- package/src/modules/access/access-requests.service.ts +420 -0
- package/src/modules/access/access.routes.ts +10 -13
- package/src/modules/access/change-read-gate.ts +10 -4
- package/src/modules/access/creator-access.ts +7 -2
- package/src/modules/access-model/access-grammar.ts +22 -0
- package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
- package/src/modules/agent-instructions/read-preamble.ts +10 -12
- package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +98 -0
- package/src/modules/auth/account-admission.ts +44 -0
- package/src/modules/auth/account.routes.ts +4 -1
- package/src/modules/auth/auth.service.ts +61 -2
- package/src/modules/auth/oidc-auth-provider.ts +9 -0
- package/src/modules/connection-probe/connection-probe.service.ts +7 -1
- package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
- package/src/modules/database/__tests__/connection.test.ts +98 -0
- package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
- package/src/modules/database/advisory-lock.ts +28 -4
- package/src/modules/database/connection.ts +143 -13
- package/src/modules/database/migrate.ts +48 -14
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
- package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
- package/src/modules/github-app/__tests__/github-app.test.ts +848 -0
- package/src/modules/github-app/github-app.client.ts +268 -0
- package/src/modules/github-app/github-app.connection.ts +204 -0
- package/src/modules/github-app/github-app.routes.ts +359 -0
- package/src/modules/github-app/index.ts +19 -0
- package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
- package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
- package/src/modules/kb-fs/__tests__/remote-url.test.ts +38 -0
- package/src/modules/kb-fs/branch-name.ts +3 -3
- package/src/modules/kb-fs/clone-config.ts +15 -12
- package/src/modules/kb-fs/remote-url.ts +35 -0
- package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
- package/src/modules/mcp/mcp.service.ts +11 -4
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
- package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
- package/src/modules/plugins/__tests__/join-proposals.test.ts +100 -19
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
- package/src/modules/plugins/__tests__/join-requests.service.test.ts +28 -11
- package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +371 -0
- package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +12 -3
- package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
- package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
- package/src/modules/plugins/discovery/native.source.ts +4 -1
- package/src/modules/plugins/index.ts +1 -1
- package/src/modules/plugins/join-proposals.ts +87 -19
- package/src/modules/plugins/join-request-jobs.service.ts +8 -7
- package/src/modules/plugins/join-requests.service.ts +203 -43
- package/src/modules/plugins/plugin-links.service.ts +192 -19
- package/src/modules/plugins/plugin-links.ts +147 -27
- package/src/modules/plugins/plugin-provision.service.ts +54 -27
- package/src/modules/plugins/plugin-rename.service.ts +10 -10
- package/src/modules/plugins/plugins.routes.ts +48 -17
- package/src/modules/plugins/plugins.service.ts +7 -11
- package/src/modules/plugins/teams.routes.ts +16 -8
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
- package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
- package/src/modules/secrets-vault/index.ts +2 -0
- package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
- package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +24 -11
- package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
- package/src/modules/settings/__tests__/repository-source.test.ts +191 -0
- package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
- package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +353 -0
- package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +282 -0
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
- package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +233 -0
- package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
- package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
- package/src/modules/settings/deployment-settings.service.ts +130 -23
- package/src/modules/settings/managed-repository.ts +68 -0
- package/src/modules/settings/repository-source.ts +197 -0
- package/src/modules/settings/setup.routes.ts +233 -19
- package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +7 -2
- package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
- package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
- package/src/modules/skills/allowed-tools-check.ts +7 -6
- package/src/modules/skills/pending-skills.service.ts +9 -8
- package/src/modules/skills/skill-access-requests.routes.ts +29 -79
- package/src/modules/skills/skills.routes.ts +4 -3
- package/src/modules/skills/skills.service.ts +13 -9
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
- package/src/modules/tool-helpers/tool-context.ts +15 -0
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
- package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
- package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
- package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
- package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
- package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
- package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
- package/src/modules/workflow/__tests__/apply-failure.test.ts +11 -2
- package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
- package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
- package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +187 -35
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +148 -41
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +143 -2
- package/src/modules/workflow/agent-tools/workflow.tools.ts +60 -12
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
- package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
- package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
- package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
- package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
- package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +130 -8
- package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
- package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
- package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +145 -2
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-delete.test.ts +166 -0
- package/src/modules/workflow/git/git.service.ts +49 -17
- package/src/modules/workflow/git/node-git-runner.ts +43 -4
- package/src/modules/workflow/git/pull-request.service.ts +119 -0
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
- package/src/modules/workflow/session-ontology.service.ts +7 -2
- package/src/modules/workflow/workflow.routes.ts +3 -1
- package/src/modules/workflow/workflow.service.ts +201 -76
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +10 -9
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
- package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
- package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +45 -1
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +237 -10
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +44 -11
- package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
- package/src/modules/workspace/session-ontology.gate.ts +6 -4
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +240 -21
- package/src/modules/workspace/startup/kb-git.ts +15 -22
- package/src/modules/workspace/startup/kb-startup-runner.ts +198 -45
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
- package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
- package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
- package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
- package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
- package/src/modules/workspace/startup/steps/template-source.ts +8 -5
- package/src/modules/workspace/workspace.routes.ts +8 -7
- package/src/modules/workspace/workspace.service.ts +47 -28
- package/src/modules/workspace/workspace.tools.ts +56 -20
- package/src/modules/workspace/write-denial.ts +7 -5
- package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
- package/src/shared/domain-errors.ts +58 -0
- package/src/shared/git.contract.ts +80 -8
- package/src/shared/kb-context.ts +130 -0
- package/src/shared/kb-layout.test.ts +37 -27
- package/src/shared/kb-layout.ts +4 -3
- package/src/shared/pending-proposals.ts +7 -4
- package/src/shared/redact-secret.ts +5 -5
- package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
- package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
- package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
- package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
- package/src/tenancy/index.ts +35 -0
- package/src/tenancy/static-tenant-source.ts +296 -0
- package/src/tenancy/tenant-host.ts +261 -0
- package/src/tenancy/tenant-runtime.ts +208 -0
- package/src/tenancy/tenant-secrets.ts +41 -0
- package/src/tenancy/tenant-source.contract.ts +59 -0
|
@@ -16,6 +16,7 @@ import { usersDbDouble } from './users-db-double.js';
|
|
|
16
16
|
import { AccessControlService } from '../access-control.service.js';
|
|
17
17
|
import { createAccessRoutes } from '../access.routes.js';
|
|
18
18
|
import { branchForWorkspaceId, workspaceIdForBranch } from '../../../shared/workspace-id.js';
|
|
19
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Manage access on a file that exists ONLY on a change request's branch.
|
|
@@ -136,7 +137,7 @@ describe('access routes on a proposal branch (real git)', () => {
|
|
|
136
137
|
workflowService,
|
|
137
138
|
{ emit: () => {} } as unknown as WorkflowEventBus,
|
|
138
139
|
usersDbDouble(),
|
|
139
|
-
KB,
|
|
140
|
+
testKbContext({ kbDirName: KB }),
|
|
140
141
|
),
|
|
141
142
|
);
|
|
142
143
|
server = await new Promise<Server>((resolve) => {
|
|
@@ -11,6 +11,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
|
11
11
|
import type { Database } from '../../database/connection.js';
|
|
12
12
|
import { createAccessRoutes } from '../access.routes.js';
|
|
13
13
|
import { WorkflowDomainError } from '../../../shared/domain-errors.js';
|
|
14
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* HTTP contract for `GET /access/prospective` — what the move confirmation
|
|
@@ -67,7 +68,7 @@ async function makeHarness(opts: { canRead?: boolean } = {}): Promise<Harness> {
|
|
|
67
68
|
{} as unknown as WorkflowService,
|
|
68
69
|
{ emit: vi.fn() } as unknown as WorkflowEventBus,
|
|
69
70
|
{} as unknown as Database,
|
|
70
|
-
KB,
|
|
71
|
+
testKbContext({ kbDirName: KB }),
|
|
71
72
|
),
|
|
72
73
|
);
|
|
73
74
|
|
|
@@ -10,6 +10,7 @@ import type { WorkflowService } from '../../workflow/workflow.service.js';
|
|
|
10
10
|
import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
11
11
|
import { createAccessRoutes } from '../access.routes.js';
|
|
12
12
|
import { usersDbDouble } from './users-db-double.js';
|
|
13
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* HTTP-level contract tests for the revoke route's CLASSIFICATION + MODE logic —
|
|
@@ -133,7 +134,7 @@ async function makeHarness(opts: {
|
|
|
133
134
|
(req as unknown as { userId: string }).userId = USER.id;
|
|
134
135
|
next();
|
|
135
136
|
});
|
|
136
|
-
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, KB));
|
|
137
|
+
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB })));
|
|
137
138
|
|
|
138
139
|
const server = await new Promise<Server>((resolve) => {
|
|
139
140
|
const s = app.listen(0, () => resolve(s));
|
|
@@ -11,6 +11,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
|
11
11
|
import { createAccessRoutes } from '../access.routes.js';
|
|
12
12
|
import type { Database } from '../../database/connection.js';
|
|
13
13
|
import { usersDbDouble } from './users-db-double.js';
|
|
14
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* A grant to an email with NO ACCOUNT says so, and stays allowed.
|
|
@@ -107,7 +108,7 @@ async function makeHarness(opts: {
|
|
|
107
108
|
workflowService,
|
|
108
109
|
{ emit: vi.fn() } as unknown as WorkflowEventBus,
|
|
109
110
|
opts.db ?? usersDbDouble(opts.accounts ?? []),
|
|
110
|
-
KB,
|
|
111
|
+
testKbContext({ kbDirName: KB }),
|
|
111
112
|
),
|
|
112
113
|
);
|
|
113
114
|
|
|
@@ -8,6 +8,8 @@ import { AccessControlService } from '../access-control.service.js';
|
|
|
8
8
|
import { ChangeReadGate, isNewTopLevelFolderPath } from '../change-read-gate.js';
|
|
9
9
|
import { AccessDeniedError } from '../../access-model/access-errors.js';
|
|
10
10
|
import { SYNCED_GROUPS_YAML } from '../../access-model/group-files.js';
|
|
11
|
+
import { DEFAULT_KB_LAYOUT } from '@bevel-software/platform-shared';
|
|
12
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
11
13
|
|
|
12
14
|
const KB = 'knowledge-base';
|
|
13
15
|
const WS = 'ws-gate';
|
|
@@ -56,7 +58,7 @@ describe('ChangeReadGate', () => {
|
|
|
56
58
|
await write(repo, 'roles.yaml', ROLES_YAML);
|
|
57
59
|
const ws = stubWorkspaceService(workspaceDir);
|
|
58
60
|
access = new AccessControlService(ws, KB, new NodeFs());
|
|
59
|
-
gate = new ChangeReadGate(ws, access, KB, new NodeFs());
|
|
61
|
+
gate = new ChangeReadGate(ws, access, testKbContext({ kbDirName: KB }), new NodeFs());
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
afterEach(async () => {
|
|
@@ -255,18 +257,18 @@ describe('isNewTopLevelFolderPath', () => {
|
|
|
255
257
|
const exists = async (p: string) => onDisk.has(p);
|
|
256
258
|
|
|
257
259
|
it('is true for a path inside a folder directly under a creatable root that is not on disk', async () => {
|
|
258
|
-
expect(await isNewTopLevelFolderPath('KnowledgeBase/New/a.md', 'file', exists)).toBe(true);
|
|
259
|
-
expect(await isNewTopLevelFolderPath('Skills/fresh/SKILL.md', 'file', exists)).toBe(true);
|
|
260
|
-
expect(await isNewTopLevelFolderPath('Plugins/team/deep/er/x', 'file', exists)).toBe(true);
|
|
261
|
-
expect(await isNewTopLevelFolderPath('KnowledgeBase/New', 'dir', exists)).toBe(true);
|
|
260
|
+
expect(await isNewTopLevelFolderPath('KnowledgeBase/New/a.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(true);
|
|
261
|
+
expect(await isNewTopLevelFolderPath('Skills/fresh/SKILL.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(true);
|
|
262
|
+
expect(await isNewTopLevelFolderPath('Plugins/team/deep/er/x', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(true);
|
|
263
|
+
expect(await isNewTopLevelFolderPath('KnowledgeBase/New', 'dir', exists, DEFAULT_KB_LAYOUT)).toBe(true);
|
|
262
264
|
});
|
|
263
265
|
|
|
264
266
|
it('is false when the top-level folder exists, for a loose file at the root, and outside the three roots', async () => {
|
|
265
|
-
expect(await isNewTopLevelFolderPath('KnowledgeBase/Existing/a.md', 'file', exists)).toBe(false);
|
|
266
|
-
expect(await isNewTopLevelFolderPath('Skills/known', 'dir', exists)).toBe(false);
|
|
267
|
-
expect(await isNewTopLevelFolderPath('KnowledgeBase/loose.md', 'file', exists)).toBe(false);
|
|
268
|
-
expect(await isNewTopLevelFolderPath('KnowledgeBase', 'dir', exists)).toBe(false);
|
|
269
|
-
expect(await isNewTopLevelFolderPath('Data/New/a.md', 'file', exists)).toBe(false);
|
|
270
|
-
expect(await isNewTopLevelFolderPath('roles.yaml', 'file', exists)).toBe(false);
|
|
267
|
+
expect(await isNewTopLevelFolderPath('KnowledgeBase/Existing/a.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
|
|
268
|
+
expect(await isNewTopLevelFolderPath('Skills/known', 'dir', exists, DEFAULT_KB_LAYOUT)).toBe(false);
|
|
269
|
+
expect(await isNewTopLevelFolderPath('KnowledgeBase/loose.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
|
|
270
|
+
expect(await isNewTopLevelFolderPath('KnowledgeBase', 'dir', exists, DEFAULT_KB_LAYOUT)).toBe(false);
|
|
271
|
+
expect(await isNewTopLevelFolderPath('Data/New/a.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
|
|
272
|
+
expect(await isNewTopLevelFolderPath('roles.yaml', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
|
|
271
273
|
});
|
|
272
274
|
});
|
|
@@ -7,6 +7,7 @@ import os from 'node:os';
|
|
|
7
7
|
import type { WorkspaceService } from '../../workspace/workspace.service.js';
|
|
8
8
|
import { AccessControlService } from '../access-control.service.js';
|
|
9
9
|
import { CreatorAccessService } from '../creator-access.js';
|
|
10
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
10
11
|
|
|
11
12
|
const KB = 'knowledge-base';
|
|
12
13
|
const WS = 'ws-creator';
|
|
@@ -61,7 +62,7 @@ describe('CreatorAccessService.planForCreate', () => {
|
|
|
61
62
|
await fs.mkdir(path.join(repo, 'Plugins'), { recursive: true });
|
|
62
63
|
await write(repo, 'roles.yaml', ROLES_YAML);
|
|
63
64
|
const ws = stubWorkspaceService(workspaceDir);
|
|
64
|
-
svc = new CreatorAccessService(ws, new AccessControlService(ws, KB, new NodeFs()), KB, new NodeFs());
|
|
65
|
+
svc = new CreatorAccessService(ws, new AccessControlService(ws, KB, new NodeFs()), testKbContext({ kbDirName: KB }), new NodeFs());
|
|
65
66
|
});
|
|
66
67
|
|
|
67
68
|
afterEach(async () => {
|
|
@@ -175,7 +176,7 @@ describe('CreatorAccessService.planForCreate', () => {
|
|
|
175
176
|
},
|
|
176
177
|
});
|
|
177
178
|
const ws = stubWorkspaceService(path.join(root, WS));
|
|
178
|
-
const withFailingDisk = new CreatorAccessService(ws, new AccessControlService(ws, KB, real), KB, failing);
|
|
179
|
+
const withFailingDisk = new CreatorAccessService(ws, new AccessControlService(ws, KB, real), testKbContext({ kbDirName: KB }), failing);
|
|
179
180
|
expect(await withFailingDisk.planForCreate(WS, ALICE, `${KB}/KnowledgeBase/Projects`, 'dir')).toBeNull();
|
|
180
181
|
expect(await withFailingDisk.planForCreate(WS, ALICE, `${KB}/KnowledgeBase/Projects/a.md`, 'file')).toBeNull();
|
|
181
182
|
// The same creation on a disk that answers is planned, so it was the
|
|
@@ -15,6 +15,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
|
|
|
15
15
|
import type { FileTreeEntry } from '@bevel-software/platform-shared';
|
|
16
16
|
import { createAccessRoutes } from '../access.routes.js';
|
|
17
17
|
import { usersDbDouble } from './users-db-double.js';
|
|
18
|
+
import { testKbContext } from '../../../__tests__/kb-context.js';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* HTTP-level contract tests for the /api/access/roles routes: the admin gate
|
|
@@ -137,7 +138,7 @@ async function makeHarness(opts: { isAdmin?: boolean } = {}): Promise<{ server:
|
|
|
137
138
|
(req as unknown as { userId: string }).userId = ADMIN.id;
|
|
138
139
|
next();
|
|
139
140
|
});
|
|
140
|
-
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, KB));
|
|
141
|
+
app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB })));
|
|
141
142
|
|
|
142
143
|
const server = await new Promise<Server>((resolve) => {
|
|
143
144
|
const s = app.listen(0, () => resolve(s));
|
|
@@ -22,10 +22,14 @@ import type {
|
|
|
22
22
|
ProspectiveHolders,
|
|
23
23
|
ResolvedPrincipal,
|
|
24
24
|
} from './access-control.interface.js';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
DEFAULT_KB_LAYOUT,
|
|
27
|
+
PLUGIN_MANIFEST_FILE,
|
|
28
|
+
isPersonalPluginDir,
|
|
26
29
|
pluginIdentityOf,
|
|
27
30
|
platformRestoreDestination,
|
|
28
31
|
} from '@bevel-software/platform-shared';
|
|
32
|
+
import type { KbContext } from '../../shared/kb-context.js';
|
|
29
33
|
import { AccessConfigError, AccessUnreadableError } from '../access-model/access-errors.js';
|
|
30
34
|
import { WorkflowDomainError } from '../../shared/domain-errors.js';
|
|
31
35
|
import { synthesizePluginPrincipals } from '../access-model/plugin-principals.js';
|
|
@@ -291,13 +295,14 @@ interface AccessScope {
|
|
|
291
295
|
*/
|
|
292
296
|
function accessFileListener(
|
|
293
297
|
repoDir: string,
|
|
298
|
+
pluginsDir: string,
|
|
294
299
|
out: Map<string, AccessFile>,
|
|
295
300
|
pluginDirs: Map<string, string>,
|
|
296
301
|
): WalkListener {
|
|
297
302
|
return {
|
|
298
303
|
async onFile(dir, name) {
|
|
299
304
|
const rel = dir ? `${dir}/${name}` : name;
|
|
300
|
-
if (name === PLUGIN_MANIFEST_FILE && dir.startsWith(`${
|
|
305
|
+
if (name === PLUGIN_MANIFEST_FILE && dir.startsWith(`${pluginsDir}/`)) {
|
|
301
306
|
const text = await fs.readFile(path.join(repoDir, rel), 'utf-8').catch(() => null);
|
|
302
307
|
pluginDirs.set(dir, pluginIdentityOf(parseManifestText(text), path.posix.basename(dir)));
|
|
303
308
|
return;
|
|
@@ -1301,6 +1306,14 @@ export class AccessControlService implements IAccessControl {
|
|
|
1301
1306
|
* many fixtures construct this service directly.
|
|
1302
1307
|
*/
|
|
1303
1308
|
private readonly gitRunner: IGitRunner = new NodeGitRunner(),
|
|
1309
|
+
/**
|
|
1310
|
+
* The knowledge base's layout — which folder is the plugins root, what the
|
|
1311
|
+
* platform files are called. Read per use, never captured: the roots are
|
|
1312
|
+
* renameable from the setup screen. Defaulted for the same reason as
|
|
1313
|
+
* `deploymentOwners`: the fixtures that construct this service directly
|
|
1314
|
+
* run on the default layout.
|
|
1315
|
+
*/
|
|
1316
|
+
private readonly kb: Pick<KbContext, 'layout'> = { layout: DEFAULT_KB_LAYOUT },
|
|
1304
1317
|
) {
|
|
1305
1318
|
this.deploymentOwners = new Set(
|
|
1306
1319
|
deploymentOwners.filter(Boolean).map((e) => canonicalEmail(e)),
|
|
@@ -1806,7 +1819,7 @@ export class AccessControlService implements IAccessControl {
|
|
|
1806
1819
|
principal.kind === 'plugin' &&
|
|
1807
1820
|
principal.pluginName &&
|
|
1808
1821
|
principal.pluginDir &&
|
|
1809
|
-
!isPersonalPluginDir(principal.pluginDir)
|
|
1822
|
+
!isPersonalPluginDir(principal.pluginDir, this.kb.layout)
|
|
1810
1823
|
) {
|
|
1811
1824
|
plugins.set(principal.pluginName, principal.pluginDir);
|
|
1812
1825
|
}
|
|
@@ -1982,7 +1995,7 @@ export class AccessControlService implements IAccessControl {
|
|
|
1982
1995
|
userEmail: string,
|
|
1983
1996
|
destinationRelativePath: string,
|
|
1984
1997
|
): Promise<boolean> {
|
|
1985
|
-
const target = platformRestoreDestination(destinationRelativePath);
|
|
1998
|
+
const target = platformRestoreDestination(destinationRelativePath, this.kb.layout);
|
|
1986
1999
|
if (target === null) return false;
|
|
1987
2000
|
const email = canonicalEmail(userEmail);
|
|
1988
2001
|
|
|
@@ -2143,7 +2156,7 @@ export class AccessControlService implements IAccessControl {
|
|
|
2143
2156
|
// / `roles.yaml` because `hasPermissionResolved` admin-rescues those
|
|
2144
2157
|
// paths, so the bad config remains fixable from inside the app.
|
|
2145
2158
|
const pluginDirs = new Map<string, string>();
|
|
2146
|
-
await this.disk.walkKb(repoDir, [accessFileListener(repoDir, accessFiles, pluginDirs)]);
|
|
2159
|
+
await this.disk.walkKb(repoDir, [accessFileListener(repoDir, this.kb.layout.pluginsDir, accessFiles, pluginDirs)]);
|
|
2147
2160
|
|
|
2148
2161
|
// Plugin principals (`plugin/<Name>/<verb>`), derived from each plugin
|
|
2149
2162
|
// folder's own access.md — a plugin being a folder with a manifest, at
|
|
@@ -2320,11 +2333,12 @@ export class AccessControlService implements IAccessControl {
|
|
|
2320
2333
|
const { stdout } = await this.gitRunner.run(repoDir, ['ls-tree', '-r', '--name-only', ref]);
|
|
2321
2334
|
const out: string[] = [];
|
|
2322
2335
|
const pluginDirs: string[] = [];
|
|
2336
|
+
const { pluginsDir } = this.kb.layout;
|
|
2323
2337
|
for (const line of stdout.split('\n')) {
|
|
2324
2338
|
const p = line.trim();
|
|
2325
2339
|
if (!p) continue;
|
|
2326
2340
|
if (p === 'access.md' || p.endsWith('/access.md')) out.push(p);
|
|
2327
|
-
else if (p.startsWith(`${
|
|
2341
|
+
else if (p.startsWith(`${pluginsDir}/`) && p.endsWith(`/${PLUGIN_MANIFEST_FILE}`)) {
|
|
2328
2342
|
pluginDirs.push(p.slice(0, -(PLUGIN_MANIFEST_FILE.length + 1)));
|
|
2329
2343
|
}
|
|
2330
2344
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { AuthUser, ChangeRequest } from '@bevel-software/platform-shared';
|
|
2
|
+
import type { Verb } from '../access-model/access-grammar.js';
|
|
3
|
+
import { accessMdPathForFolder, type TargetKind } from './access-mutation.service.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The ITEM a request is about — a folder, or a file that carries its own
|
|
7
|
+
* rules. Everything downstream (the branch name, the file spliced, the
|
|
8
|
+
* permission checked) is derived from this pair, so a request on a file can
|
|
9
|
+
* never reach beyond that file.
|
|
10
|
+
*/
|
|
11
|
+
export interface AccessRequestTarget {
|
|
12
|
+
/** Repo-relative path of the item itself — NOT of its rules file. */
|
|
13
|
+
path: string;
|
|
14
|
+
kind: TargetKind;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A folder target, spelled once. */
|
|
18
|
+
export function folderTarget(repoRelFolder: string): AccessRequestTarget {
|
|
19
|
+
return { path: repoRelFolder, kind: 'folder' };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The file whose rules a request on this item edits: a folder's `access.md`,
|
|
24
|
+
* or the file itself (its own frontmatter). The one place the two cases part,
|
|
25
|
+
* so a file request writes nothing a sibling can see.
|
|
26
|
+
*/
|
|
27
|
+
export function rulesFileFor(target: AccessRequestTarget): string {
|
|
28
|
+
return target.kind === 'folder' ? accessMdPathForFolder(target.path) : target.path;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The levels the Manage access dialog can ask for. `owner` covers `write`. */
|
|
32
|
+
export type RequestLevel = Extract<Verb, 'write' | 'owner'>;
|
|
33
|
+
export const REQUEST_LEVELS: readonly RequestLevel[] = ['write', 'owner'];
|
|
34
|
+
export function isRequestLevel(value: unknown): value is RequestLevel {
|
|
35
|
+
return typeof value === 'string' && (REQUEST_LEVELS as readonly string[]).includes(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The optional note a requester may attach, in characters. */
|
|
39
|
+
export const REQUEST_NOTE_MAX = 500;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One grant a request's branch still proposes. Structurally the plugin
|
|
43
|
+
* module's `JoinProposal` — stated here so the access module can consume the
|
|
44
|
+
* request lifecycle without importing the module that implements it (the
|
|
45
|
+
* dependency runs library → access, and this keeps it that way).
|
|
46
|
+
*/
|
|
47
|
+
export interface AccessProposal {
|
|
48
|
+
verb: Verb;
|
|
49
|
+
id: string;
|
|
50
|
+
principal:
|
|
51
|
+
| { kind: 'user'; email: string; displayName: string }
|
|
52
|
+
| { kind: 'role'; role: string };
|
|
53
|
+
label: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** One open request, as the editors' surfaces render it. */
|
|
57
|
+
export interface AccessRequestSummary {
|
|
58
|
+
number: number;
|
|
59
|
+
branch: string;
|
|
60
|
+
requesterName: string;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
proposals: AccessProposal[];
|
|
63
|
+
/** The requester's note, as plain text, when they gave one. */
|
|
64
|
+
note?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The request lifecycle, as the access module needs it: what a branch still
|
|
69
|
+
* proposes, the open requests for an item, and the settle-if-nothing-is-left
|
|
70
|
+
* check. Implemented by `JoinRequestsService`.
|
|
71
|
+
*
|
|
72
|
+
* `key` is what `joinBranchFor` was given when the branch was named — a
|
|
73
|
+
* plugin's name for a Subscribe request, and the ITEM'S OWN PATH for every
|
|
74
|
+
* request that comes from the Manage access dialog. Keying a skill folder's
|
|
75
|
+
* request on its path is what makes the skill page's "Request write access"
|
|
76
|
+
* and a Can edit request on that same folder one request rather than two.
|
|
77
|
+
*/
|
|
78
|
+
export interface IAccessRequestLifecycle {
|
|
79
|
+
/** Null ⇒ the branch could not be read; that is not "proposes nothing". */
|
|
80
|
+
proposalsOn(branch: string, target: AccessRequestTarget): Promise<AccessProposal[] | null>;
|
|
81
|
+
list(
|
|
82
|
+
key: string,
|
|
83
|
+
target: AccessRequestTarget,
|
|
84
|
+
crs: ChangeRequest[],
|
|
85
|
+
actor: AuthUser,
|
|
86
|
+
): Promise<AccessRequestSummary[]>;
|
|
87
|
+
reconcile(
|
|
88
|
+
key: string,
|
|
89
|
+
target: AccessRequestTarget,
|
|
90
|
+
cr: ChangeRequest,
|
|
91
|
+
actor: AuthUser,
|
|
92
|
+
): Promise<boolean>;
|
|
93
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import type { AuthUser } from '@bevel-software/platform-shared';
|
|
3
|
+
import type { IWorkflowService } from '@bevel-software/platform-shared';
|
|
4
|
+
import { logger } from '../../shared/logging.js';
|
|
5
|
+
import type { KbContext } from '../../shared/kb-context.js';
|
|
6
|
+
import { branchForWorkspaceId } from '../../shared/workspace-id.js';
|
|
7
|
+
import type { AuthService } from '../auth/auth.service.js';
|
|
8
|
+
import type { WorkspaceService } from '../workspace/workspace.service.js';
|
|
9
|
+
import type { IAccessControl } from './access-control.interface.js';
|
|
10
|
+
import {
|
|
11
|
+
AccessMutationError,
|
|
12
|
+
AccessMutationService,
|
|
13
|
+
fileCarriesAccessRules,
|
|
14
|
+
governingFolderOf,
|
|
15
|
+
type TargetKind,
|
|
16
|
+
} from './access-mutation.service.js';
|
|
17
|
+
import { toHttpError as sharedToHttpError } from './admin-route-helpers.js';
|
|
18
|
+
import {
|
|
19
|
+
isRequestLevel,
|
|
20
|
+
rulesFileFor,
|
|
21
|
+
REQUEST_NOTE_MAX,
|
|
22
|
+
type AccessRequestTarget,
|
|
23
|
+
type IAccessRequestLifecycle,
|
|
24
|
+
} from './access-requests.contract.js';
|
|
25
|
+
import {
|
|
26
|
+
AccessRequestsService,
|
|
27
|
+
extractRequestNote,
|
|
28
|
+
itemNameOf,
|
|
29
|
+
} from './access-requests.service.js';
|
|
30
|
+
import '../auth/auth.middleware.js'; // Express Request.userId augmentation
|
|
31
|
+
|
|
32
|
+
const log = logger('access.request');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Asking for Can edit or Owner on one item, and the editors' answer.
|
|
36
|
+
*
|
|
37
|
+
* POST /api/workspace/:id/access/request → { ok, number, state, level }
|
|
38
|
+
* GET /api/workspace/:id/access/request?path=&kind= → { state, level?, number? }
|
|
39
|
+
* GET /api/workspace/:id/access/requests?path=&kind= → { requests } (editors)
|
|
40
|
+
* POST /api/workspace/:id/access/requests/:n/reconcile → { closed }
|
|
41
|
+
*
|
|
42
|
+
* LIVE WORKSPACE ONLY. A draft branch's rules may differ from the ones the
|
|
43
|
+
* person is asking about, and a request opened against a draft would propose a
|
|
44
|
+
* grant into a branch nobody merges — so every route here refuses any
|
|
45
|
+
* workspace but the default one, rather than quietly retargeting.
|
|
46
|
+
*
|
|
47
|
+
* Fail-closed like the rest of the access family: an item the caller cannot
|
|
48
|
+
* read answers as unknown, and the editors' listing answers `[]` to everyone
|
|
49
|
+
* else rather than 403 — "am I an editor here" stays a question only the
|
|
50
|
+
* server answers.
|
|
51
|
+
*/
|
|
52
|
+
export function createAccessRequestRoutes(deps: {
|
|
53
|
+
accessControl: IAccessControl;
|
|
54
|
+
workspaceService: WorkspaceService;
|
|
55
|
+
authService: AuthService;
|
|
56
|
+
workflow: IWorkflowService;
|
|
57
|
+
lifecycle: IAccessRequestLifecycle;
|
|
58
|
+
kb: KbContext;
|
|
59
|
+
}): express.Router {
|
|
60
|
+
const { accessControl, workspaceService, authService, workflow, lifecycle, kb } = deps;
|
|
61
|
+
const { kbDirName } = kb;
|
|
62
|
+
const router = express.Router({ mergeParams: true });
|
|
63
|
+
const mutation = new AccessMutationService(workspaceService, accessControl, kbDirName);
|
|
64
|
+
const requests = new AccessRequestsService({ workflow, workspaceService, lifecycle, kb });
|
|
65
|
+
|
|
66
|
+
async function requireUser(
|
|
67
|
+
req: express.Request,
|
|
68
|
+
res: express.Response,
|
|
69
|
+
): Promise<AuthUser | null> {
|
|
70
|
+
if (!req.userId) {
|
|
71
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const user = await authService.getUserById(req.userId);
|
|
75
|
+
if (!user) {
|
|
76
|
+
res.status(401).json({ error: 'User not found' });
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return user;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Workspace-relative (what the dialog holds) → repo-relative (what the model speaks). */
|
|
83
|
+
function toRepoRelative(p: string): string {
|
|
84
|
+
const prefix = `${kbDirName}/`;
|
|
85
|
+
if (p === kbDirName) return '';
|
|
86
|
+
return p.startsWith(prefix) ? p.slice(prefix.length) : p;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Refuse a target that could escape the KB repo, or that is spelled in more
|
|
91
|
+
* than one way. Mirrors the grant route's guard — the branch name and the
|
|
92
|
+
* spliced path are both derived from this string, so two spellings of one
|
|
93
|
+
* item would be two different requests.
|
|
94
|
+
*/
|
|
95
|
+
function assertSafeTarget(repoRelTarget: string, kind: TargetKind): void {
|
|
96
|
+
if (kind === 'file' && !repoRelTarget) {
|
|
97
|
+
throw new AccessMutationError('file path is required');
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
repoRelTarget.startsWith('/') ||
|
|
101
|
+
repoRelTarget.includes('\\') ||
|
|
102
|
+
repoRelTarget.includes('\0') ||
|
|
103
|
+
repoRelTarget.split('/').some((s) => s === '..' || s === '.')
|
|
104
|
+
) {
|
|
105
|
+
throw new AccessMutationError('path must stay inside the KB repo');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The (live workspace, readable item) pair every route here needs, or null
|
|
111
|
+
* after the refusal has been answered.
|
|
112
|
+
*
|
|
113
|
+
* The two refusals are deliberately different shapes: a non-default
|
|
114
|
+
* workspace is the CALLER asking the wrong question (400), while an item the
|
|
115
|
+
* caller cannot read answers exactly as one that does not exist (404) — a
|
|
116
|
+
* 403 would confirm the item is there.
|
|
117
|
+
*/
|
|
118
|
+
async function resolveTarget(
|
|
119
|
+
req: express.Request,
|
|
120
|
+
res: express.Response,
|
|
121
|
+
rawPath: unknown,
|
|
122
|
+
rawKind: unknown,
|
|
123
|
+
): Promise<{ user: AuthUser; target: AccessRequestTarget; wsId: string } | null> {
|
|
124
|
+
const user = await requireUser(req, res);
|
|
125
|
+
if (!user) return null;
|
|
126
|
+
const wsId = kb.defaultWorkspaceId();
|
|
127
|
+
if (branchForWorkspaceId(String(req.params.id)) !== kb.defaultBranch) {
|
|
128
|
+
res.status(400).json({
|
|
129
|
+
error: 'Access requests are only made on the live workspace.',
|
|
130
|
+
kind: 'not-live-workspace',
|
|
131
|
+
});
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (typeof rawPath !== 'string') {
|
|
135
|
+
res.status(400).json({ error: 'path is required', kind: 'access-error' });
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const kind: TargetKind = rawKind === 'folder' ? 'folder' : 'file';
|
|
139
|
+
const target: AccessRequestTarget = { path: toRepoRelative(rawPath), kind };
|
|
140
|
+
try {
|
|
141
|
+
assertSafeTarget(target.path, kind);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
const { status, body } = sharedToHttpError(err, 'access');
|
|
144
|
+
res.status(status).json(body);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
if (!(await accessControl.canRead(wsId, user.email, target.path))) {
|
|
148
|
+
res.status(404).json({ error: 'Not found', kind: 'unknown-target' });
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return { user, target, wsId };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Can the caller change this item's rules — the gate an Accept runs through. */
|
|
155
|
+
const isEditor = (wsId: string, email: string, target: AccessRequestTarget) =>
|
|
156
|
+
accessControl.canWrite(wsId, email, rulesFileFor(target));
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* A FILE that cannot hold rules of its own is governed by its folder, and
|
|
160
|
+
* there is nothing here to request. The dialog already points at the folder
|
|
161
|
+
* for these; the route says the same thing rather than splicing YAML into a
|
|
162
|
+
* PDF. Content counts as much as the name — bytes that are not text refuse
|
|
163
|
+
* too, exactly as the grant route refuses them.
|
|
164
|
+
*/
|
|
165
|
+
async function assertCanCarryRules(wsId: string, target: AccessRequestTarget): Promise<void> {
|
|
166
|
+
if (target.kind !== 'file') return;
|
|
167
|
+
if (fileCarriesAccessRules(target.path) && (await mutation.targetHoldsText(wsId, target.path))) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
throw new AccessMutationError(
|
|
171
|
+
`Access for this file is managed on its folder. Ask there instead.`,
|
|
172
|
+
409,
|
|
173
|
+
{ kind: 'folder-governs', folder: governingFolderOf(target.path) },
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
router.post('/workspace/:id/access/request', async (req, res) => {
|
|
178
|
+
const body = (req.body ?? {}) as Record<string, unknown>;
|
|
179
|
+
const ctx = await resolveTarget(req, res, body.path, body.kind);
|
|
180
|
+
if (!ctx) return;
|
|
181
|
+
const { user, target, wsId } = ctx;
|
|
182
|
+
try {
|
|
183
|
+
if (!isRequestLevel(body.level)) {
|
|
184
|
+
throw new AccessMutationError('level must be "write" or "owner"');
|
|
185
|
+
}
|
|
186
|
+
const note = typeof body.note === 'string' ? body.note : '';
|
|
187
|
+
if (typeof body.note !== 'undefined' && typeof body.note !== 'string') {
|
|
188
|
+
throw new AccessMutationError('note must be text');
|
|
189
|
+
}
|
|
190
|
+
if (note.length > REQUEST_NOTE_MAX) {
|
|
191
|
+
throw new AccessMutationError(`A note can be at most ${REQUEST_NOTE_MAX} characters.`);
|
|
192
|
+
}
|
|
193
|
+
await assertCanCarryRules(wsId, target);
|
|
194
|
+
if (await isEditor(wsId, user.email, target)) {
|
|
195
|
+
throw new AccessMutationError(
|
|
196
|
+
`You can already change who can use this ${target.kind}.`,
|
|
197
|
+
409,
|
|
198
|
+
{ kind: 'already-writable' },
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
const opened = await requests.open({
|
|
202
|
+
user,
|
|
203
|
+
target,
|
|
204
|
+
itemName: itemNameOf(target.path),
|
|
205
|
+
level: body.level,
|
|
206
|
+
note,
|
|
207
|
+
});
|
|
208
|
+
res.json({ ok: true, state: 'pending', number: opened.number, level: opened.level });
|
|
209
|
+
} catch (err) {
|
|
210
|
+
const { status, body: errBody } = sharedToHttpError(err, 'access');
|
|
211
|
+
if (status >= 500) log.error('failed to open an access request:', { err });
|
|
212
|
+
res.status(status).json(errBody);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
router.get('/workspace/:id/access/request', async (req, res) => {
|
|
217
|
+
const ctx = await resolveTarget(req, res, req.query.path, req.query.kind);
|
|
218
|
+
if (!ctx) return;
|
|
219
|
+
try {
|
|
220
|
+
res.json(await requests.status(ctx.user, ctx.target));
|
|
221
|
+
} catch (err) {
|
|
222
|
+
log.error('failed to read an access request:', { err });
|
|
223
|
+
res.status(500).json({ error: 'Failed to read the request' });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
router.get('/workspace/:id/access/requests', async (req, res) => {
|
|
228
|
+
const ctx = await resolveTarget(req, res, req.query.path, req.query.kind);
|
|
229
|
+
if (!ctx) return;
|
|
230
|
+
const { user, target, wsId } = ctx;
|
|
231
|
+
try {
|
|
232
|
+
if (!(await isEditor(wsId, user.email, target))) {
|
|
233
|
+
res.json({ requests: [] });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const crs = await workflow.listChangeRequests();
|
|
237
|
+
const rows = await lifecycle.list(target.path, target, crs, user);
|
|
238
|
+
// The note lives in the request's description and nowhere else, so it
|
|
239
|
+
// is read back per request. Bounded by the open requests on ONE item —
|
|
240
|
+
// a handful at most — and a detail that cannot be fetched costs the
|
|
241
|
+
// note, never the line it belongs to.
|
|
242
|
+
const withNotes = await Promise.all(
|
|
243
|
+
rows.map(async (row) => {
|
|
244
|
+
const detail = await workflow
|
|
245
|
+
.getChangeRequestDetail(row.number, { patches: false })
|
|
246
|
+
.catch(() => null);
|
|
247
|
+
const note = extractRequestNote(detail?.body);
|
|
248
|
+
return note ? { ...row, note } : row;
|
|
249
|
+
}),
|
|
250
|
+
);
|
|
251
|
+
res.json({ requests: withNotes });
|
|
252
|
+
} catch (err) {
|
|
253
|
+
log.error('failed to list access requests:', { err });
|
|
254
|
+
res.status(500).json({ error: 'Failed to list access requests' });
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
router.post('/workspace/:id/access/requests/:number/reconcile', async (req, res) => {
|
|
259
|
+
const body = (req.body ?? {}) as Record<string, unknown>;
|
|
260
|
+
const ctx = await resolveTarget(req, res, body.path, body.kind);
|
|
261
|
+
if (!ctx) return;
|
|
262
|
+
const { user, target, wsId } = ctx;
|
|
263
|
+
try {
|
|
264
|
+
if (!(await isEditor(wsId, user.email, target))) {
|
|
265
|
+
res.status(404).json({ error: 'Not found' });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const number = Number(req.params.number);
|
|
269
|
+
if (!Number.isSafeInteger(number) || number <= 0) {
|
|
270
|
+
res.status(400).json({ error: 'Invalid change request number' });
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const cr = await workflow.getChangeRequest(number);
|
|
274
|
+
if (!cr) {
|
|
275
|
+
res.status(404).json({ error: 'Not found' });
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
res.json({ closed: await lifecycle.reconcile(target.path, target, cr, user) });
|
|
279
|
+
} catch (err) {
|
|
280
|
+
log.error('failed to reconcile an access request:', { err });
|
|
281
|
+
res.status(500).json({ error: 'Failed to reconcile the request' });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return router;
|
|
286
|
+
}
|