@bevel-software/platform-core-backend 0.21.0 → 0.23.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/core-ports.d.ts +11 -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 +3 -3
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +62 -13
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +22 -3
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +158 -23
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +34 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +89 -13
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +0 -12
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +11 -13
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +42 -12
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +67 -10
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +214 -35
- 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.map +1 -1
- package/dist/modules/access/access.routes.js +13 -15
- package/dist/modules/access/access.routes.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/auth/account-admission.d.ts +58 -7
- package/dist/modules/auth/account-admission.d.ts.map +1 -1
- package/dist/modules/auth/account-admission.js +44 -1
- package/dist/modules/auth/account-admission.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts +1 -1
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +61 -5
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +1 -1
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +18 -7
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +8 -0
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +96 -3
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +187 -9
- 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 +7 -2
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +41 -24
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +35 -27
- package/dist/modules/database/core-schema.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.map +1 -1
- package/dist/modules/kb-fs/branch-name.js +12 -2
- package/dist/modules/kb-fs/branch-name.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/kb-fs/repo-path.d.ts +11 -16
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +79 -0
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.d.ts +2 -2
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.js +14 -3
- package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -1
- package/dist/modules/kb-sync/sync-auth.d.ts +3 -1
- package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -1
- package/dist/modules/kb-sync/sync-auth.js +1 -1
- package/dist/modules/kb-sync/sync-auth.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +21 -6
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +3 -2
- package/dist/modules/mcp/oauth/bevel-oauth-provider.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-requests.service.d.ts +111 -18
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +173 -29
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +3 -2
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +22 -1
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +101 -7
- 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 +78 -4
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +379 -17
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +6 -8
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.js +24 -63
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +90 -18
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.contract.js +4 -1
- package/dist/modules/skills/skills.contract.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +98 -9
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +239 -37
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts +7 -0
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +71 -7
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +4 -2
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +3 -3
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +16 -7
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +34 -12
- package/dist/modules/tool-auth/tool-auth.middleware.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-helpers/tool-handler.d.ts +2 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +25 -4
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +3 -2
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-helpers/validate-token.js +1 -1
- package/dist/modules/tool-helpers/validate-token.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +98 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.js +81 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -0
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +113 -37
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +24 -0
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +30 -0
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +72 -1
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +170 -6
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +5 -0
- 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/pending-commits.service.d.ts +38 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +55 -0
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/workflow-hooks.d.ts +54 -32
- package/dist/modules/workflow/workflow-hooks.d.ts.map +1 -1
- package/dist/modules/workflow/workflow-hooks.js +16 -1
- package/dist/modules/workflow/workflow-hooks.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 +94 -13
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +258 -51
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts +94 -0
- package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -0
- package/dist/modules/workspace/agent-access.gate.js +123 -0
- package/dist/modules/workspace/agent-access.gate.js.map +1 -0
- package/dist/modules/workspace/routine-write-policy.d.ts +5 -6
- package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
- package/dist/modules/workspace/routine-write-policy.js +5 -6
- package/dist/modules/workspace/routine-write-policy.js.map +1 -1
- package/dist/modules/workspace/session-sink.d.ts +5 -5
- package/dist/modules/workspace/set-aside-clone.d.ts +46 -0
- package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -0
- package/dist/modules/workspace/set-aside-clone.js +92 -0
- package/dist/modules/workspace/set-aside-clone.js.map +1 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +117 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +190 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +92 -3
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +115 -5
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +272 -26
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +2 -2
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +337 -117
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/write-access/write-access.d.ts +60 -0
- package/dist/modules/write-access/write-access.d.ts.map +1 -0
- package/dist/modules/write-access/write-access.js +129 -0
- package/dist/modules/write-access/write-access.js.map +1 -0
- package/dist/shared/domain-errors.d.ts +64 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +83 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +30 -0
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +26 -0
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/tenancy/static-tenant-source.d.ts +0 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +0 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/migrations/0014_change_request_closed_reason.sql +1 -0
- package/migrations/0015_account_deactivation.sql +3 -0
- package/migrations/meta/0014_snapshot.json +2265 -0
- package/migrations/meta/0015_snapshot.json +2271 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +3 -3
- package/src/__tests__/kb-layout-config.test.ts +0 -2
- package/src/__tests__/retired-settings.test.ts +96 -0
- package/src/core/__tests__/gated-boot.test.ts +132 -0
- package/src/core/__tests__/lifecycle.test.ts +170 -1
- package/src/core/__tests__/set-aside-root-is-one-place.test.ts +63 -0
- package/src/core/core-ports.ts +11 -1
- package/src/core/create-core-server.ts +69 -15
- package/src/core/create-core-services.ts +190 -27
- package/src/core/lifecycle.ts +98 -13
- package/src/core-config.ts +13 -14
- package/src/index.ts +12 -1
- package/src/modules/access/__tests__/access-control.preview-relocation.test.ts +385 -0
- package/src/modules/access/__tests__/access-control.prospective.test.ts +94 -18
- 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.prospective.test.ts +6 -7
- package/src/modules/access/access-control.interface.ts +43 -12
- package/src/modules/access/access-control.service.ts +234 -36
- 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 +13 -15
- package/src/modules/access-model/access-grammar.ts +22 -0
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +3 -1
- package/src/modules/auth/__tests__/account-deactivation.test.ts +252 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +81 -3
- package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
- package/src/modules/auth/__tests__/auth.service.test.ts +39 -1
- package/src/modules/auth/account-admission.ts +78 -9
- package/src/modules/auth/account.routes.ts +63 -7
- package/src/modules/auth/auth.middleware.ts +19 -8
- package/src/modules/auth/auth.routes.ts +8 -0
- package/src/modules/auth/auth.service.ts +207 -7
- package/src/modules/auth/oidc-auth-provider.ts +7 -2
- package/src/modules/database/core-schema.ts +35 -27
- 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 +10 -0
- package/src/modules/kb-fs/__tests__/remote-url.test.ts +38 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +123 -0
- package/src/modules/kb-fs/branch-name.ts +14 -1
- package/src/modules/kb-fs/remote-url.ts +35 -0
- package/src/modules/kb-fs/repo-path.ts +85 -0
- package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +26 -1
- package/src/modules/kb-sync/kb-sync.routes.ts +14 -4
- package/src/modules/kb-sync/sync-auth.ts +2 -2
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +60 -3
- package/src/modules/mcp/__tests__/mcp.service.test.ts +68 -6
- package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
- package/src/modules/plugins/__tests__/join-proposals.test.ts +100 -19
- package/src/modules/plugins/__tests__/join-requests.service.test.ts +27 -11
- package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +371 -0
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +1 -1
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
- package/src/modules/plugins/join-proposals.ts +87 -19
- package/src/modules/plugins/join-requests.service.ts +199 -39
- package/src/modules/plugins/plugins.routes.ts +3 -2
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
- package/src/modules/settings/__tests__/repository-source.test.ts +191 -0
- package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +386 -0
- package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +299 -0
- package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +236 -0
- package/src/modules/settings/__tests__/setup.routes.repository-change.test.ts +390 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +3 -0
- package/src/modules/settings/deployment-settings.service.ts +117 -6
- 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 +463 -15
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +2 -1
- package/src/modules/skills/__tests__/branch-skills.tools.test.ts +218 -0
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +5 -1
- package/src/modules/skills/__tests__/skills.service.test.ts +229 -5
- package/src/modules/skills/skill-access-requests.routes.ts +25 -75
- package/src/modules/skills/skills.contract.ts +91 -18
- package/src/modules/skills/skills.service.ts +278 -41
- package/src/modules/skills/skills.tools.ts +80 -8
- package/src/modules/tool-auth/__tests__/manual-auth.middleware.test.ts +14 -1
- package/src/modules/tool-auth/external-api-key.service.ts +4 -2
- package/src/modules/tool-auth/internal-token.service.ts +3 -3
- package/src/modules/tool-auth/tool-auth.middleware.ts +34 -11
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +5 -3
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -3
- package/src/modules/tool-helpers/__tests__/validate-token.test.ts +11 -1
- package/src/modules/tool-helpers/tool-context.ts +15 -0
- package/src/modules/tool-helpers/tool-handler.ts +24 -4
- package/src/modules/tool-helpers/tool.contract.ts +3 -2
- package/src/modules/tool-helpers/validate-token.ts +1 -1
- package/src/modules/workflow/__tests__/apply-failure.test.ts +9 -1
- package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +111 -0
- package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +25 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +159 -8
- package/src/modules/workflow/__tests__/workflow.service.repository-replaced.test.ts +249 -0
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +146 -40
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +405 -7
- package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
- package/src/modules/workflow/agent-tools/workflow.tools.ts +135 -38
- package/src/modules/workflow/file-lock.service.ts +31 -0
- package/src/modules/workflow/git/__tests__/git.service.fileBytesAtCommit.test.ts +260 -0
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +121 -0
- 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 +193 -4
- package/src/modules/workflow/git/node-git-runner.ts +6 -0
- package/src/modules/workflow/git/pull-request.service.ts +119 -0
- package/src/modules/workflow/pending-commits.service.ts +59 -1
- package/src/modules/workflow/workflow-hooks.ts +64 -26
- package/src/modules/workflow/workflow.routes.ts +3 -1
- package/src/modules/workflow/workflow.service.ts +290 -55
- package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
- package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +384 -0
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +2 -1
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +2 -1
- package/src/modules/workspace/__tests__/set-aside-clone.test.ts +52 -0
- package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +305 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -0
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +147 -0
- package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +326 -0
- package/src/modules/workspace/__tests__/workspace.service.test.ts +20 -9
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +43 -0
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +2 -3
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +235 -10
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +720 -17
- package/src/modules/workspace/agent-access.gate.ts +164 -0
- package/src/modules/workspace/routine-write-policy.ts +5 -6
- package/src/modules/workspace/session-sink.ts +5 -5
- package/src/modules/workspace/set-aside-clone.ts +96 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +297 -0
- package/src/modules/workspace/startup/kb-startup-runner.ts +242 -2
- package/src/modules/workspace/workspace.routes.ts +95 -3
- package/src/modules/workspace/workspace.service.ts +273 -25
- package/src/modules/workspace/workspace.tools.ts +373 -119
- package/src/modules/write-access/__tests__/write-access.test.ts +248 -0
- package/src/modules/write-access/write-access.ts +153 -0
- package/src/shared/domain-errors.ts +89 -0
- package/src/shared/git.contract.ts +37 -0
- package/src/tenancy/__tests__/static-tenant-source.test.ts +0 -1
- package/src/tenancy/static-tenant-source.ts +0 -3
- package/dist/modules/workflow/session-ontology.policy.d.ts +0 -52
- package/dist/modules/workflow/session-ontology.policy.d.ts.map +0 -1
- package/dist/modules/workflow/session-ontology.policy.js +0 -62
- package/dist/modules/workflow/session-ontology.policy.js.map +0 -1
- package/dist/modules/workflow/session-ontology.service.d.ts +0 -105
- package/dist/modules/workflow/session-ontology.service.d.ts.map +0 -1
- package/dist/modules/workflow/session-ontology.service.js +0 -147
- package/dist/modules/workflow/session-ontology.service.js.map +0 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts +0 -114
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +0 -1
- package/dist/modules/workspace/session-ontology.gate.js +0 -161
- package/dist/modules/workspace/session-ontology.gate.js.map +0 -1
- package/dist/shared/kb-layout.d.ts +0 -39
- package/dist/shared/kb-layout.d.ts.map +0 -1
- package/dist/shared/kb-layout.js +0 -103
- package/dist/shared/kb-layout.js.map +0 -1
- package/dist/shared/kb-layout.test.d.ts +0 -2
- package/dist/shared/kb-layout.test.d.ts.map +0 -1
- package/dist/shared/kb-layout.test.js +0 -75
- package/dist/shared/kb-layout.test.js.map +0 -1
- package/src/modules/workflow/__tests__/session-ontology.policy.test.ts +0 -62
- package/src/modules/workflow/__tests__/session-ontology.service.test.ts +0 -201
- package/src/modules/workflow/session-ontology.policy.ts +0 -70
- package/src/modules/workflow/session-ontology.service.ts +0 -183
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +0 -239
- package/src/modules/workspace/session-ontology.gate.ts +0 -191
- package/src/shared/kb-layout.test.ts +0 -98
- package/src/shared/kb-layout.ts +0 -102
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { AuthUser, ChangeRequest } from '@bevel-software/platform-shared';
|
|
2
|
+
import type { Verb } from '../access-model/access-grammar.js';
|
|
3
|
+
import { type TargetKind } from './access-mutation.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* The ITEM a request is about — a folder, or a file that carries its own
|
|
6
|
+
* rules. Everything downstream (the branch name, the file spliced, the
|
|
7
|
+
* permission checked) is derived from this pair, so a request on a file can
|
|
8
|
+
* never reach beyond that file.
|
|
9
|
+
*/
|
|
10
|
+
export interface AccessRequestTarget {
|
|
11
|
+
/** Repo-relative path of the item itself — NOT of its rules file. */
|
|
12
|
+
path: string;
|
|
13
|
+
kind: TargetKind;
|
|
14
|
+
}
|
|
15
|
+
/** A folder target, spelled once. */
|
|
16
|
+
export declare function folderTarget(repoRelFolder: string): AccessRequestTarget;
|
|
17
|
+
/**
|
|
18
|
+
* The file whose rules a request on this item edits: a folder's `access.md`,
|
|
19
|
+
* or the file itself (its own frontmatter). The one place the two cases part,
|
|
20
|
+
* so a file request writes nothing a sibling can see.
|
|
21
|
+
*/
|
|
22
|
+
export declare function rulesFileFor(target: AccessRequestTarget): string;
|
|
23
|
+
/** The levels the Manage access dialog can ask for. `owner` covers `write`. */
|
|
24
|
+
export type RequestLevel = Extract<Verb, 'write' | 'owner'>;
|
|
25
|
+
export declare const REQUEST_LEVELS: readonly RequestLevel[];
|
|
26
|
+
export declare function isRequestLevel(value: unknown): value is RequestLevel;
|
|
27
|
+
/** The optional note a requester may attach, in characters. */
|
|
28
|
+
export declare const REQUEST_NOTE_MAX = 500;
|
|
29
|
+
/**
|
|
30
|
+
* One grant a request's branch still proposes. Structurally the plugin
|
|
31
|
+
* module's `JoinProposal` — stated here so the access module can consume the
|
|
32
|
+
* request lifecycle without importing the module that implements it (the
|
|
33
|
+
* dependency runs library → access, and this keeps it that way).
|
|
34
|
+
*/
|
|
35
|
+
export interface AccessProposal {
|
|
36
|
+
verb: Verb;
|
|
37
|
+
id: string;
|
|
38
|
+
principal: {
|
|
39
|
+
kind: 'user';
|
|
40
|
+
email: string;
|
|
41
|
+
displayName: string;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'role';
|
|
44
|
+
role: string;
|
|
45
|
+
};
|
|
46
|
+
label: string;
|
|
47
|
+
}
|
|
48
|
+
/** One open request, as the editors' surfaces render it. */
|
|
49
|
+
export interface AccessRequestSummary {
|
|
50
|
+
number: number;
|
|
51
|
+
branch: string;
|
|
52
|
+
requesterName: string;
|
|
53
|
+
createdAt: string;
|
|
54
|
+
proposals: AccessProposal[];
|
|
55
|
+
/** The requester's note, as plain text, when they gave one. */
|
|
56
|
+
note?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The request lifecycle, as the access module needs it: what a branch still
|
|
60
|
+
* proposes, the open requests for an item, and the settle-if-nothing-is-left
|
|
61
|
+
* check. Implemented by `JoinRequestsService`.
|
|
62
|
+
*
|
|
63
|
+
* `key` is what `joinBranchFor` was given when the branch was named — a
|
|
64
|
+
* plugin's name for a Subscribe request, and the ITEM'S OWN PATH for every
|
|
65
|
+
* request that comes from the Manage access dialog. Keying a skill folder's
|
|
66
|
+
* request on its path is what makes the skill page's "Request write access"
|
|
67
|
+
* and a Can edit request on that same folder one request rather than two.
|
|
68
|
+
*/
|
|
69
|
+
export interface IAccessRequestLifecycle {
|
|
70
|
+
/** Null ⇒ the branch could not be read; that is not "proposes nothing". */
|
|
71
|
+
proposalsOn(branch: string, target: AccessRequestTarget): Promise<AccessProposal[] | null>;
|
|
72
|
+
list(key: string, target: AccessRequestTarget, crs: ChangeRequest[], actor: AuthUser): Promise<AccessRequestSummary[]>;
|
|
73
|
+
reconcile(key: string, target: AccessRequestTarget, cr: ChangeRequest, actor: AuthUser): Promise<boolean>;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=access-requests.contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-requests.contract.d.ts","sourceRoot":"","sources":["../../../src/modules/access/access-requests.contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAEtF;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,mBAAmB,CAEvE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAEhE;AAED,+EAA+E;AAC/E,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC;AAC5D,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAAuB,CAAC;AAC1E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EACL;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GACpD;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3F,IAAI,CACF,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,aAAa,EAAE,EACpB,KAAK,EAAE,QAAQ,GACd,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACnC,SAAS,CACP,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,mBAAmB,EAC3B,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE,QAAQ,GACd,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { accessMdPathForFolder } from './access-mutation.service.js';
|
|
2
|
+
/** A folder target, spelled once. */
|
|
3
|
+
export function folderTarget(repoRelFolder) {
|
|
4
|
+
return { path: repoRelFolder, kind: 'folder' };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The file whose rules a request on this item edits: a folder's `access.md`,
|
|
8
|
+
* or the file itself (its own frontmatter). The one place the two cases part,
|
|
9
|
+
* so a file request writes nothing a sibling can see.
|
|
10
|
+
*/
|
|
11
|
+
export function rulesFileFor(target) {
|
|
12
|
+
return target.kind === 'folder' ? accessMdPathForFolder(target.path) : target.path;
|
|
13
|
+
}
|
|
14
|
+
export const REQUEST_LEVELS = ['write', 'owner'];
|
|
15
|
+
export function isRequestLevel(value) {
|
|
16
|
+
return typeof value === 'string' && REQUEST_LEVELS.includes(value);
|
|
17
|
+
}
|
|
18
|
+
/** The optional note a requester may attach, in characters. */
|
|
19
|
+
export const REQUEST_NOTE_MAX = 500;
|
|
20
|
+
//# sourceMappingURL=access-requests.contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-requests.contract.js","sourceRoot":"","sources":["../../../src/modules/access/access-requests.contract.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAmB,MAAM,8BAA8B,CAAC;AActF,qCAAqC;AACrC,MAAM,UAAU,YAAY,CAAC,aAAqB;IAChD,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAA2B;IACtD,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;AACrF,CAAC;AAID,MAAM,CAAC,MAAM,cAAc,GAA4B,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1E,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,cAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC5F,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import type { IWorkflowService } from '@bevel-software/platform-shared';
|
|
3
|
+
import type { KbContext } from '../../shared/kb-context.js';
|
|
4
|
+
import type { AuthService } from '../auth/auth.service.js';
|
|
5
|
+
import type { WorkspaceService } from '../workspace/workspace.service.js';
|
|
6
|
+
import type { IAccessControl } from './access-control.interface.js';
|
|
7
|
+
import { type IAccessRequestLifecycle } from './access-requests.contract.js';
|
|
8
|
+
import '../auth/auth.middleware.js';
|
|
9
|
+
/**
|
|
10
|
+
* Asking for Can edit or Owner on one item, and the editors' answer.
|
|
11
|
+
*
|
|
12
|
+
* POST /api/workspace/:id/access/request → { ok, number, state, level }
|
|
13
|
+
* GET /api/workspace/:id/access/request?path=&kind= → { state, level?, number? }
|
|
14
|
+
* GET /api/workspace/:id/access/requests?path=&kind= → { requests } (editors)
|
|
15
|
+
* POST /api/workspace/:id/access/requests/:n/reconcile → { closed }
|
|
16
|
+
*
|
|
17
|
+
* LIVE WORKSPACE ONLY. A draft branch's rules may differ from the ones the
|
|
18
|
+
* person is asking about, and a request opened against a draft would propose a
|
|
19
|
+
* grant into a branch nobody merges — so every route here refuses any
|
|
20
|
+
* workspace but the default one, rather than quietly retargeting.
|
|
21
|
+
*
|
|
22
|
+
* Fail-closed like the rest of the access family: an item the caller cannot
|
|
23
|
+
* read answers as unknown, and the editors' listing answers `[]` to everyone
|
|
24
|
+
* else rather than 403 — "am I an editor here" stays a question only the
|
|
25
|
+
* server answers.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createAccessRequestRoutes(deps: {
|
|
28
|
+
accessControl: IAccessControl;
|
|
29
|
+
workspaceService: WorkspaceService;
|
|
30
|
+
authService: AuthService;
|
|
31
|
+
workflow: IWorkflowService;
|
|
32
|
+
lifecycle: IAccessRequestLifecycle;
|
|
33
|
+
kb: KbContext;
|
|
34
|
+
}): express.Router;
|
|
35
|
+
//# sourceMappingURL=access-requests.routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-requests.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/access/access-requests.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AASpE,OAAO,EAKL,KAAK,uBAAuB,EAC7B,MAAM,+BAA+B,CAAC;AAMvC,OAAO,4BAA4B,CAAC;AAIpC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,aAAa,EAAE,cAAc,CAAC;IAC9B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,SAAS,EAAE,uBAAuB,CAAC;IACnC,EAAE,EAAE,SAAS,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAmOjB"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { logger } from '../../shared/logging.js';
|
|
3
|
+
import { branchForWorkspaceId } from '../../shared/workspace-id.js';
|
|
4
|
+
import { AccessMutationError, AccessMutationService, fileCarriesAccessRules, governingFolderOf, } from './access-mutation.service.js';
|
|
5
|
+
import { toHttpError as sharedToHttpError } from './admin-route-helpers.js';
|
|
6
|
+
import { isRequestLevel, rulesFileFor, REQUEST_NOTE_MAX, } from './access-requests.contract.js';
|
|
7
|
+
import { AccessRequestsService, extractRequestNote, itemNameOf, } from './access-requests.service.js';
|
|
8
|
+
import '../auth/auth.middleware.js'; // Express Request.userId augmentation
|
|
9
|
+
const log = logger('access.request');
|
|
10
|
+
/**
|
|
11
|
+
* Asking for Can edit or Owner on one item, and the editors' answer.
|
|
12
|
+
*
|
|
13
|
+
* POST /api/workspace/:id/access/request → { ok, number, state, level }
|
|
14
|
+
* GET /api/workspace/:id/access/request?path=&kind= → { state, level?, number? }
|
|
15
|
+
* GET /api/workspace/:id/access/requests?path=&kind= → { requests } (editors)
|
|
16
|
+
* POST /api/workspace/:id/access/requests/:n/reconcile → { closed }
|
|
17
|
+
*
|
|
18
|
+
* LIVE WORKSPACE ONLY. A draft branch's rules may differ from the ones the
|
|
19
|
+
* person is asking about, and a request opened against a draft would propose a
|
|
20
|
+
* grant into a branch nobody merges — so every route here refuses any
|
|
21
|
+
* workspace but the default one, rather than quietly retargeting.
|
|
22
|
+
*
|
|
23
|
+
* Fail-closed like the rest of the access family: an item the caller cannot
|
|
24
|
+
* read answers as unknown, and the editors' listing answers `[]` to everyone
|
|
25
|
+
* else rather than 403 — "am I an editor here" stays a question only the
|
|
26
|
+
* server answers.
|
|
27
|
+
*/
|
|
28
|
+
export function createAccessRequestRoutes(deps) {
|
|
29
|
+
const { accessControl, workspaceService, authService, workflow, lifecycle, kb } = deps;
|
|
30
|
+
const { kbDirName } = kb;
|
|
31
|
+
const router = express.Router({ mergeParams: true });
|
|
32
|
+
const mutation = new AccessMutationService(workspaceService, accessControl, kbDirName);
|
|
33
|
+
const requests = new AccessRequestsService({ workflow, workspaceService, lifecycle, kb });
|
|
34
|
+
async function requireUser(req, res) {
|
|
35
|
+
if (!req.userId) {
|
|
36
|
+
res.status(401).json({ error: 'Unauthenticated' });
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const user = await authService.getUserById(req.userId);
|
|
40
|
+
if (!user) {
|
|
41
|
+
res.status(401).json({ error: 'User not found' });
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return user;
|
|
45
|
+
}
|
|
46
|
+
/** Workspace-relative (what the dialog holds) → repo-relative (what the model speaks). */
|
|
47
|
+
function toRepoRelative(p) {
|
|
48
|
+
const prefix = `${kbDirName}/`;
|
|
49
|
+
if (p === kbDirName)
|
|
50
|
+
return '';
|
|
51
|
+
return p.startsWith(prefix) ? p.slice(prefix.length) : p;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Refuse a target that could escape the KB repo, or that is spelled in more
|
|
55
|
+
* than one way. Mirrors the grant route's guard — the branch name and the
|
|
56
|
+
* spliced path are both derived from this string, so two spellings of one
|
|
57
|
+
* item would be two different requests.
|
|
58
|
+
*/
|
|
59
|
+
function assertSafeTarget(repoRelTarget, kind) {
|
|
60
|
+
if (kind === 'file' && !repoRelTarget) {
|
|
61
|
+
throw new AccessMutationError('file path is required');
|
|
62
|
+
}
|
|
63
|
+
if (repoRelTarget.startsWith('/') ||
|
|
64
|
+
repoRelTarget.includes('\\') ||
|
|
65
|
+
repoRelTarget.includes('\0') ||
|
|
66
|
+
repoRelTarget.split('/').some((s) => s === '..' || s === '.')) {
|
|
67
|
+
throw new AccessMutationError('path must stay inside the KB repo');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The (live workspace, readable item) pair every route here needs, or null
|
|
72
|
+
* after the refusal has been answered.
|
|
73
|
+
*
|
|
74
|
+
* The two refusals are deliberately different shapes: a non-default
|
|
75
|
+
* workspace is the CALLER asking the wrong question (400), while an item the
|
|
76
|
+
* caller cannot read answers exactly as one that does not exist (404) — a
|
|
77
|
+
* 403 would confirm the item is there.
|
|
78
|
+
*/
|
|
79
|
+
async function resolveTarget(req, res, rawPath, rawKind) {
|
|
80
|
+
const user = await requireUser(req, res);
|
|
81
|
+
if (!user)
|
|
82
|
+
return null;
|
|
83
|
+
const wsId = kb.defaultWorkspaceId();
|
|
84
|
+
if (branchForWorkspaceId(String(req.params.id)) !== kb.defaultBranch) {
|
|
85
|
+
res.status(400).json({
|
|
86
|
+
error: 'Access requests are only made on the live workspace.',
|
|
87
|
+
kind: 'not-live-workspace',
|
|
88
|
+
});
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
if (typeof rawPath !== 'string') {
|
|
92
|
+
res.status(400).json({ error: 'path is required', kind: 'access-error' });
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const kind = rawKind === 'folder' ? 'folder' : 'file';
|
|
96
|
+
const target = { path: toRepoRelative(rawPath), kind };
|
|
97
|
+
try {
|
|
98
|
+
assertSafeTarget(target.path, kind);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
const { status, body } = sharedToHttpError(err, 'access');
|
|
102
|
+
res.status(status).json(body);
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (!(await accessControl.canRead(wsId, user.email, target.path))) {
|
|
106
|
+
res.status(404).json({ error: 'Not found', kind: 'unknown-target' });
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return { user, target, wsId };
|
|
110
|
+
}
|
|
111
|
+
/** Can the caller change this item's rules — the gate an Accept runs through. */
|
|
112
|
+
const isEditor = (wsId, email, target) => accessControl.canWrite(wsId, email, rulesFileFor(target));
|
|
113
|
+
/**
|
|
114
|
+
* A FILE that cannot hold rules of its own is governed by its folder, and
|
|
115
|
+
* there is nothing here to request. The dialog already points at the folder
|
|
116
|
+
* for these; the route says the same thing rather than splicing YAML into a
|
|
117
|
+
* PDF. Content counts as much as the name — bytes that are not text refuse
|
|
118
|
+
* too, exactly as the grant route refuses them.
|
|
119
|
+
*/
|
|
120
|
+
async function assertCanCarryRules(wsId, target) {
|
|
121
|
+
if (target.kind !== 'file')
|
|
122
|
+
return;
|
|
123
|
+
if (fileCarriesAccessRules(target.path) && (await mutation.targetHoldsText(wsId, target.path))) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
throw new AccessMutationError(`Access for this file is managed on its folder. Ask there instead.`, 409, { kind: 'folder-governs', folder: governingFolderOf(target.path) });
|
|
127
|
+
}
|
|
128
|
+
router.post('/workspace/:id/access/request', async (req, res) => {
|
|
129
|
+
const body = (req.body ?? {});
|
|
130
|
+
const ctx = await resolveTarget(req, res, body.path, body.kind);
|
|
131
|
+
if (!ctx)
|
|
132
|
+
return;
|
|
133
|
+
const { user, target, wsId } = ctx;
|
|
134
|
+
try {
|
|
135
|
+
if (!isRequestLevel(body.level)) {
|
|
136
|
+
throw new AccessMutationError('level must be "write" or "owner"');
|
|
137
|
+
}
|
|
138
|
+
const note = typeof body.note === 'string' ? body.note : '';
|
|
139
|
+
if (typeof body.note !== 'undefined' && typeof body.note !== 'string') {
|
|
140
|
+
throw new AccessMutationError('note must be text');
|
|
141
|
+
}
|
|
142
|
+
if (note.length > REQUEST_NOTE_MAX) {
|
|
143
|
+
throw new AccessMutationError(`A note can be at most ${REQUEST_NOTE_MAX} characters.`);
|
|
144
|
+
}
|
|
145
|
+
await assertCanCarryRules(wsId, target);
|
|
146
|
+
if (await isEditor(wsId, user.email, target)) {
|
|
147
|
+
throw new AccessMutationError(`You can already change who can use this ${target.kind}.`, 409, { kind: 'already-writable' });
|
|
148
|
+
}
|
|
149
|
+
const opened = await requests.open({
|
|
150
|
+
user,
|
|
151
|
+
target,
|
|
152
|
+
itemName: itemNameOf(target.path),
|
|
153
|
+
level: body.level,
|
|
154
|
+
note,
|
|
155
|
+
});
|
|
156
|
+
res.json({ ok: true, state: 'pending', number: opened.number, level: opened.level });
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
const { status, body: errBody } = sharedToHttpError(err, 'access');
|
|
160
|
+
if (status >= 500)
|
|
161
|
+
log.error('failed to open an access request:', { err });
|
|
162
|
+
res.status(status).json(errBody);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
router.get('/workspace/:id/access/request', async (req, res) => {
|
|
166
|
+
const ctx = await resolveTarget(req, res, req.query.path, req.query.kind);
|
|
167
|
+
if (!ctx)
|
|
168
|
+
return;
|
|
169
|
+
try {
|
|
170
|
+
res.json(await requests.status(ctx.user, ctx.target));
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
log.error('failed to read an access request:', { err });
|
|
174
|
+
res.status(500).json({ error: 'Failed to read the request' });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
router.get('/workspace/:id/access/requests', async (req, res) => {
|
|
178
|
+
const ctx = await resolveTarget(req, res, req.query.path, req.query.kind);
|
|
179
|
+
if (!ctx)
|
|
180
|
+
return;
|
|
181
|
+
const { user, target, wsId } = ctx;
|
|
182
|
+
try {
|
|
183
|
+
if (!(await isEditor(wsId, user.email, target))) {
|
|
184
|
+
res.json({ requests: [] });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const crs = await workflow.listChangeRequests();
|
|
188
|
+
const rows = await lifecycle.list(target.path, target, crs, user);
|
|
189
|
+
// The note lives in the request's description and nowhere else, so it
|
|
190
|
+
// is read back per request. Bounded by the open requests on ONE item —
|
|
191
|
+
// a handful at most — and a detail that cannot be fetched costs the
|
|
192
|
+
// note, never the line it belongs to.
|
|
193
|
+
const withNotes = await Promise.all(rows.map(async (row) => {
|
|
194
|
+
const detail = await workflow
|
|
195
|
+
.getChangeRequestDetail(row.number, { patches: false })
|
|
196
|
+
.catch(() => null);
|
|
197
|
+
const note = extractRequestNote(detail?.body);
|
|
198
|
+
return note ? { ...row, note } : row;
|
|
199
|
+
}));
|
|
200
|
+
res.json({ requests: withNotes });
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
log.error('failed to list access requests:', { err });
|
|
204
|
+
res.status(500).json({ error: 'Failed to list access requests' });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
router.post('/workspace/:id/access/requests/:number/reconcile', async (req, res) => {
|
|
208
|
+
const body = (req.body ?? {});
|
|
209
|
+
const ctx = await resolveTarget(req, res, body.path, body.kind);
|
|
210
|
+
if (!ctx)
|
|
211
|
+
return;
|
|
212
|
+
const { user, target, wsId } = ctx;
|
|
213
|
+
try {
|
|
214
|
+
if (!(await isEditor(wsId, user.email, target))) {
|
|
215
|
+
res.status(404).json({ error: 'Not found' });
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const number = Number(req.params.number);
|
|
219
|
+
if (!Number.isSafeInteger(number) || number <= 0) {
|
|
220
|
+
res.status(400).json({ error: 'Invalid change request number' });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const cr = await workflow.getChangeRequest(number);
|
|
224
|
+
if (!cr) {
|
|
225
|
+
res.status(404).json({ error: 'Not found' });
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
res.json({ closed: await lifecycle.reconcile(target.path, target, cr, user) });
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
log.error('failed to reconcile an access request:', { err });
|
|
232
|
+
res.status(500).json({ error: 'Failed to reconcile the request' });
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
return router;
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=access-requests.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-requests.routes.js","sourceRoot":"","sources":["../../../src/modules/access/access-requests.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAIpE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,GAElB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,GAGjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,UAAU,GACX,MAAM,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC,CAAC,sCAAsC;AAE3E,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAErC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAOzC;IACC,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;IACvF,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,qBAAqB,CAAC,gBAAgB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,IAAI,qBAAqB,CAAC,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;IAE1F,KAAK,UAAU,WAAW,CACxB,GAAoB,EACpB,GAAqB;QAErB,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YAChB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0FAA0F;IAC1F,SAAS,cAAc,CAAC,CAAS;QAC/B,MAAM,MAAM,GAAG,GAAG,SAAS,GAAG,CAAC;QAC/B,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAC/B,OAAO,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED;;;;;OAKG;IACH,SAAS,gBAAgB,CAAC,aAAqB,EAAE,IAAgB;QAC/D,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;YACtC,MAAM,IAAI,mBAAmB,CAAC,uBAAuB,CAAC,CAAC;QACzD,CAAC;QACD,IACE,aAAa,CAAC,UAAU,CAAC,GAAG,CAAC;YAC7B,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC5B,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC5B,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,EAC7D,CAAC;YACD,MAAM,IAAI,mBAAmB,CAAC,mCAAmC,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,UAAU,aAAa,CAC1B,GAAoB,EACpB,GAAqB,EACrB,OAAgB,EAChB,OAAgB;QAEhB,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,MAAM,IAAI,GAAG,EAAE,CAAC,kBAAkB,EAAE,CAAC;QACrC,IAAI,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,aAAa,EAAE,CAAC;YACrE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,sDAAsD;gBAC7D,IAAI,EAAE,oBAAoB;aAC3B,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAe,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QAClE,MAAM,MAAM,GAAwB,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;QAC5E,IAAI,CAAC;YACH,gBAAgB,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC1D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACrE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAChC,CAAC;IAED,iFAAiF;IACjF,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,MAA2B,EAAE,EAAE,CAC5E,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAE5D;;;;;;OAMG;IACH,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,MAA2B;QAC1E,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO;QACnC,IAAI,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC/F,OAAO;QACT,CAAC;QACD,MAAM,IAAI,mBAAmB,CAC3B,mEAAmE,EACnE,GAAG,EACH,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CACnE,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9D,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;QACzD,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,mBAAmB,CAAC,kCAAkC,CAAC,CAAC;YACpE,CAAC;YACD,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtE,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;YACrD,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;gBACnC,MAAM,IAAI,mBAAmB,CAAC,yBAAyB,gBAAgB,cAAc,CAAC,CAAC;YACzF,CAAC;YACD,MAAM,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;gBAC7C,MAAM,IAAI,mBAAmB,CAC3B,2CAA2C,MAAM,CAAC,IAAI,GAAG,EACzD,GAAG,EACH,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAC7B,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;gBACjC,IAAI;gBACJ,MAAM;gBACN,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC;gBACjC,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI;aACL,CAAC,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACvF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YACnE,IAAI,MAAM,IAAI,GAAG;gBAAE,GAAG,CAAC,KAAK,CAAC,mCAAmC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3E,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,+BAA+B,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC7D,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1E,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,mCAAmC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YACxD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,gCAAgC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9D,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1E,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;gBAChD,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC3B,OAAO;YACT,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,kBAAkB,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAClE,sEAAsE;YACtE,uEAAuE;YACvE,oEAAoE;YACpE,sCAAsC;YACtC,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBACrB,MAAM,MAAM,GAAG,MAAM,QAAQ;qBAC1B,sBAAsB,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;qBACtD,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;gBACrB,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC9C,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YACvC,CAAC,CAAC,CACH,CAAC;YACF,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,iCAAiC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YACtD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,kDAAkD,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACjF,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;QACzD,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;gBAChD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;gBACjD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;gBACjE,OAAO;YACT,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;YACnD,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACjF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,wCAAwC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAC7D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { type AuthUser, type IWorkflowService } from '@bevel-software/platform-shared';
|
|
2
|
+
import type { KbContext } from '../../shared/kb-context.js';
|
|
3
|
+
import type { WorkspaceService } from '../workspace/workspace.service.js';
|
|
4
|
+
import { type AccessRequestTarget, type IAccessRequestLifecycle, type RequestLevel } from './access-requests.contract.js';
|
|
5
|
+
export declare const LEVEL_LABEL: Record<RequestLevel, string>;
|
|
6
|
+
/** What the requester's own dialog shows in place of the control. */
|
|
7
|
+
export interface AccessRequestStatus {
|
|
8
|
+
/**
|
|
9
|
+
* - `none` nothing outstanding; show the control.
|
|
10
|
+
* - `pending` a request is open and still asking for something.
|
|
11
|
+
* - `not-accepted` their last request closed without the access landing.
|
|
12
|
+
*/
|
|
13
|
+
state: 'none' | 'pending' | 'not-accepted';
|
|
14
|
+
level?: RequestLevel;
|
|
15
|
+
number?: number;
|
|
16
|
+
}
|
|
17
|
+
/** The name an item is called by in a title — the last path segment. */
|
|
18
|
+
export declare function itemNameOf(repoRelPath: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Neutralise every character a markdown renderer would read as structure.
|
|
21
|
+
*
|
|
22
|
+
* The note is somebody's free text and it lands in a change request's
|
|
23
|
+
* description, which IS rendered. Escaping it is what makes "plain text" true:
|
|
24
|
+
* without this a note can carry a heading, a link, or an image pointing at a
|
|
25
|
+
* URL that logs whoever opens the request.
|
|
26
|
+
*/
|
|
27
|
+
export declare function escapeMarkdown(text: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The requester's own words, back out of a request's description, or
|
|
30
|
+
* undefined when they wrote none. Unescaped exactly as they were escaped, so
|
|
31
|
+
* what the banner shows is what was typed.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* The level a request asks for, off its own description. Undefined for a
|
|
35
|
+
* request opened before this marker existed — those are the skill route's,
|
|
36
|
+
* and the skill route only ever asked for `write`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractRequestLevel(body: string | null | undefined): RequestLevel | undefined;
|
|
39
|
+
export declare function extractRequestNote(body: string | null | undefined): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Asking for Can edit or Owner on ONE item, and reading back what came of it.
|
|
42
|
+
*
|
|
43
|
+
* A request is a change request whose branch proposes exactly one grant in the
|
|
44
|
+
* item's own rules — a folder's `access.md`, or a file's own frontmatter — and
|
|
45
|
+
* nothing else. There is no request table: the branch IS the request, named
|
|
46
|
+
* deterministically from (person, item), so a second click finds the first
|
|
47
|
+
* click's request instead of opening a rival. Editors answer it by granting
|
|
48
|
+
* through the ordinary access path, and it retires itself
|
|
49
|
+
* ({@link IAccessRequestLifecycle}) once the access has landed however it
|
|
50
|
+
* landed.
|
|
51
|
+
*
|
|
52
|
+
* Keyed on the ITEM'S PATH, which is what the skill page's "Request write
|
|
53
|
+
* access" keys on too — so a Can edit request on a skill's folder and that
|
|
54
|
+
* button open one request, shown on both surfaces.
|
|
55
|
+
*/
|
|
56
|
+
export declare class AccessRequestsService {
|
|
57
|
+
private readonly deps;
|
|
58
|
+
constructor(deps: {
|
|
59
|
+
workflow: IWorkflowService;
|
|
60
|
+
workspaceService: WorkspaceService;
|
|
61
|
+
lifecycle: IAccessRequestLifecycle;
|
|
62
|
+
kb: Pick<KbContext, 'defaultBranch' | 'defaultWorkspaceId' | 'kbDirName'>;
|
|
63
|
+
});
|
|
64
|
+
/** The branch a request from `email` about `target` always lives on. */
|
|
65
|
+
branchFor(email: string, target: AccessRequestTarget): string;
|
|
66
|
+
/**
|
|
67
|
+
* Open `user`'s request for `level` on `target`, or answer with the one they
|
|
68
|
+
* already have open. Never opens a second request for the same pair.
|
|
69
|
+
*/
|
|
70
|
+
open(input: {
|
|
71
|
+
user: AuthUser;
|
|
72
|
+
target: AccessRequestTarget;
|
|
73
|
+
itemName: string;
|
|
74
|
+
level: RequestLevel;
|
|
75
|
+
note?: string;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
number: number;
|
|
78
|
+
level: RequestLevel;
|
|
79
|
+
}>;
|
|
80
|
+
/**
|
|
81
|
+
* What `user`'s dialog should say about `target`.
|
|
82
|
+
*
|
|
83
|
+
* The two states are answered from DIFFERENT sources, because different
|
|
84
|
+
* things prove them:
|
|
85
|
+
*
|
|
86
|
+
* pending an open request on this branch. That is the whole proof,
|
|
87
|
+
* and the level comes off the request itself — asking the
|
|
88
|
+
* branch would make "Requested: …" depend on whether this
|
|
89
|
+
* clone has fetched a ref that may be seconds old.
|
|
90
|
+
* not-accepted a closed request AND a branch that still carries an unmet
|
|
91
|
+
* proposal naming this person. A settled request has no
|
|
92
|
+
* branch left, so an accepted one can never read as
|
|
93
|
+
* declined; a branch that cannot be read proves nothing and
|
|
94
|
+
* reads as `none`, which shows the control rather than
|
|
95
|
+
* claiming a refusal nobody made.
|
|
96
|
+
*/
|
|
97
|
+
status(user: AuthUser, target: AccessRequestTarget): Promise<AccessRequestStatus>;
|
|
98
|
+
/**
|
|
99
|
+
* The level change request `number` asks for, off its own description.
|
|
100
|
+
*
|
|
101
|
+
* `write` when the description does not say: every request that predates the
|
|
102
|
+
* marker came from the skill page's "Request write access", which asks for
|
|
103
|
+
* exactly that. A detail that cannot be fetched falls there too — the line
|
|
104
|
+
* it feeds says which level was asked for, and Can edit is the lesser claim.
|
|
105
|
+
*/
|
|
106
|
+
private levelOf;
|
|
107
|
+
/**
|
|
108
|
+
* The level `email`'s branch still asks for on `target`, or null when it
|
|
109
|
+
* asks for nothing they do not already hold. Owner wins when a branch
|
|
110
|
+
* somehow carries both — it is the larger of the two, and it is the one the
|
|
111
|
+
* person is still waiting on.
|
|
112
|
+
*/
|
|
113
|
+
private levelAskedOn;
|
|
114
|
+
/**
|
|
115
|
+
* The live rules text to splice onto. A folder with no `access.md` yet is
|
|
116
|
+
* normal and reads as empty; a FILE that is not there is not — a request
|
|
117
|
+
* must not conjure the file it asks about.
|
|
118
|
+
*/
|
|
119
|
+
private readLive;
|
|
120
|
+
/** The description an editor reads: who, what, where, the note, and how to answer. */
|
|
121
|
+
private describe;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=access-requests.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-requests.service.d.ts","sourceRoot":"","sources":["../../../src/modules/access/access-requests.service.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACtB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EAClB,MAAM,+BAA+B,CAAC;AAKvC,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAGpD,CAAC;AAEF,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAAC;IAC3C,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AA6BD;;;;GAIG;AACH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAG7F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CActF;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,qBAAqB;IAE9B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE;QACrB,QAAQ,EAAE,gBAAgB,CAAC;QAC3B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,SAAS,EAAE,uBAAuB,CAAC;QACnC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,eAAe,GAAG,oBAAoB,GAAG,WAAW,CAAC,CAAC;KAC3E;IAGH,wEAAwE;IACxE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,MAAM;IAI7D;;;OAGG;IACG,IAAI,CAAC,KAAK,EAAE;QAChB,IAAI,EAAE,QAAQ,CAAC;QACf,MAAM,EAAE,mBAAmB,CAAC;QAC5B,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,YAAY,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC;IA0IpD;;;;;;;;;;;;;;;;OAgBG;IACG,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAiBvF;;;;;;;OAOG;YACW,OAAO;IAOrB;;;;;OAKG;YACW,YAAY;IAkB1B;;;;OAIG;YACW,QAAQ;IAgBtB,sFAAsF;IACtF,OAAO,CAAC,QAAQ;CA0BjB"}
|