@bevel-software/platform-core-backend 0.22.0 → 0.24.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 +46 -13
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +12 -2
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +137 -24
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +46 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +99 -13
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +16 -12
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +28 -13
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- 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.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +17 -16
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +7 -3
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +8 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.js +8 -2
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +38 -6
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +39 -6
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/index.d.ts +2 -1
- package/dist/modules/agent-instructions/index.d.ts.map +1 -1
- package/dist/modules/agent-instructions/index.js +2 -1
- package/dist/modules/agent-instructions/index.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +115 -0
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -0
- package/dist/modules/agent-instructions/shared-file-rules.js +272 -0
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +4 -2
- package/dist/modules/audit/agent-audit.service.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-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +57 -16
- package/dist/modules/auth/account-erasure.service.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 +70 -2
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +197 -18
- 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/code-mode/code-mode.tool.d.ts +20 -2
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +66 -35
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/connection.d.ts +16 -0
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +117 -0
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +337 -120
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +116 -58
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts +8 -0
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +271 -1
- package/dist/modules/database/migrate.js.map +1 -1
- 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/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/mcp.service.d.ts +8 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +38 -8
- package/dist/modules/mcp/mcp.service.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-request-records.store.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-records.store.js +7 -4
- package/dist/modules/plugins/join-request-records.store.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +8 -3
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +52 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +207 -19
- package/dist/modules/settings/setup.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 +12 -4
- 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-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/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +42 -31
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +80 -0
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -0
- package/dist/modules/tool-registry/description-length.js +108 -0
- package/dist/modules/tool-registry/description-length.js.map +1 -0
- 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 +75 -35
- 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 +94 -0
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +193 -3
- package/dist/modules/workflow/git/git.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 +60 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +7 -3
- package/dist/modules/workflow/recovery-bot.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 +12 -3
- package/dist/modules/workflow/review-workflow/review-workflow.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.service.d.ts +60 -6
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +115 -5
- 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/agent-upload.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-upload.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.routes.js +210 -0
- package/dist/modules/workspace/agent-upload.routes.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +284 -0
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.store.js +553 -0
- package/dist/modules/workspace/agent-upload.store.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 +59 -9
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +65 -24
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +3 -3
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +40 -0
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +46 -4
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/upload-limits.d.ts +13 -0
- package/dist/modules/workspace/upload-limits.d.ts.map +1 -0
- package/dist/modules/workspace/upload-limits.js +13 -0
- package/dist/modules/workspace/upload-limits.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +93 -4
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +128 -5
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +316 -58
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +13 -11
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +791 -198
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +0 -6
- package/dist/modules/workspace/write-denial.d.ts.map +1 -1
- package/dist/modules/workspace/write-denial.js +0 -6
- package/dist/modules/workspace/write-denial.js.map +1 -1
- package/dist/modules/workspace/zip-entry-rules.d.ts +114 -0
- package/dist/modules/workspace/zip-entry-rules.d.ts.map +1 -0
- package/dist/modules/workspace/zip-entry-rules.js +154 -0
- package/dist/modules/workspace/zip-entry-rules.js.map +1 -0
- 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/column-crypto.d.ts +194 -0
- package/dist/shared/column-crypto.d.ts.map +1 -0
- package/dist/shared/column-crypto.js +144 -0
- package/dist/shared/column-crypto.js.map +1 -0
- package/dist/shared/domain-errors.d.ts +25 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +28 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +20 -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/shared/token-crypto.d.ts.map +1 -1
- package/dist/shared/token-crypto.js +25 -1
- package/dist/shared/token-crypto.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 +1 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/dist/tenancy/tenant-secrets.d.ts +5 -1
- package/dist/tenancy/tenant-secrets.d.ts.map +1 -1
- package/dist/tenancy/tenant-secrets.js +4 -0
- package/dist/tenancy/tenant-secrets.js.map +1 -1
- package/kb-template/AGENTS.md +2 -0
- package/migrations/0014_change_request_closed_reason.sql +1 -0
- package/migrations/0015_account_deactivation.sql +3 -0
- package/migrations/0016_pii_encryption.sql +20 -0
- package/migrations/meta/0014_snapshot.json +2265 -0
- package/migrations/meta/0015_snapshot.json +2271 -0
- package/migrations/meta/0016_snapshot.json +2327 -0
- package/migrations/meta/_journal.json +21 -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 +241 -5
- 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 +52 -16
- package/src/core/create-core-services.ts +156 -24
- package/src/core/lifecycle.ts +117 -13
- package/src/core-config.ts +31 -14
- package/src/index.ts +30 -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.routes.prospective.test.ts +6 -7
- package/src/modules/access/__tests__/users-db-double.ts +21 -12
- 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.routes.ts +17 -16
- package/src/modules/access/directory-sync-bot.ts +7 -3
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +11 -5
- package/src/modules/agent-instructions/__tests__/compose.test.ts +19 -10
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +238 -0
- package/src/modules/agent-instructions/agent-instructions.routes.ts +12 -2
- package/src/modules/agent-instructions/compose.ts +50 -7
- package/src/modules/agent-instructions/index.ts +12 -0
- package/src/modules/agent-instructions/shared-file-rules.ts +314 -0
- package/src/modules/audit/agent-audit.service.ts +4 -2
- package/src/modules/auth/__tests__/account-deactivation.test.ts +253 -0
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +6 -2
- package/src/modules/auth/__tests__/account.routes.test.ts +87 -6
- package/src/modules/auth/__tests__/auth.middleware.test.ts +45 -22
- package/src/modules/auth/__tests__/auth.service.test.ts +4 -2
- package/src/modules/auth/account-admission.ts +78 -9
- package/src/modules/auth/account-erasure.service.ts +69 -18
- 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 +206 -23
- package/src/modules/auth/oidc-auth-provider.ts +7 -2
- package/src/modules/code-mode/__tests__/chain-runtime.e2e.test.ts +335 -0
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +46 -2
- package/src/modules/code-mode/code-mode.tool.ts +80 -34
- package/src/modules/database/__tests__/connection.test.ts +12 -0
- package/src/modules/database/__tests__/pii-backfill.pg.test.ts +561 -0
- package/src/modules/database/connection.ts +117 -0
- package/src/modules/database/core-schema.ts +116 -58
- package/src/modules/database/migrate.ts +353 -1
- package/src/modules/kb-fs/__tests__/branch-name.test.ts +10 -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/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.e2e.test.ts +4 -3
- package/src/modules/mcp/__tests__/mcp.service.test.ts +115 -13
- package/src/modules/mcp/mcp-auth.middleware.ts +20 -6
- package/src/modules/mcp/mcp.service.ts +46 -7
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -1
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +2 -1
- package/src/modules/plugins/join-request-records.store.ts +7 -4
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +16 -0
- package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +91 -58
- package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +25 -8
- package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +14 -11
- 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 +8 -3
- package/src/modules/settings/setup.routes.ts +263 -19
- 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__/skills.service.test.ts +229 -5
- 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 +12 -4
- 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-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/tool-manuals/tool-manuals.tools.ts +44 -31
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +378 -0
- package/src/modules/tool-registry/description-length.ts +111 -0
- 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.repository-replaced.test.ts +249 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +264 -6
- package/src/modules/workflow/agent-tools/change-request-summary.ts +182 -0
- package/src/modules/workflow/agent-tools/workflow.tools.ts +86 -35
- 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.prFetchFailure.test.ts +170 -0
- package/src/modules/workflow/git/git.service.ts +215 -1
- package/src/modules/workflow/pending-commits.service.ts +64 -2
- package/src/modules/workflow/recovery-bot.ts +7 -3
- package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +4 -0
- package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +25 -3
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
- package/src/modules/workflow/workflow-hooks.ts +64 -26
- package/src/modules/workflow/workflow.service.ts +125 -5
- package/src/modules/workspace/__tests__/agent-access.gate.test.ts +208 -0
- package/src/modules/workspace/__tests__/agent-uploads.test.ts +1604 -0
- package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +394 -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.any-workspace-credentials.test.ts +156 -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 +402 -0
- package/src/modules/workspace/__tests__/workspace.service.test.ts +77 -9
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +41 -39
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +2 -3
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +766 -56
- package/src/modules/workspace/agent-access.gate.ts +164 -0
- package/src/modules/workspace/agent-upload.routes.ts +214 -0
- package/src/modules/workspace/agent-upload.store.ts +668 -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 +86 -0
- package/src/modules/workspace/startup/kb-startup-runner.ts +115 -34
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +9 -9
- package/src/modules/workspace/startup/steps/seed-tree.ts +3 -6
- package/src/modules/workspace/startup/steps/template-source.ts +53 -5
- package/src/modules/workspace/upload-limits.ts +12 -0
- package/src/modules/workspace/workspace.routes.ts +96 -5
- package/src/modules/workspace/workspace.service.ts +319 -61
- package/src/modules/workspace/workspace.tools.ts +894 -214
- package/src/modules/workspace/write-denial.ts +0 -8
- package/src/modules/workspace/zip-entry-rules.ts +173 -0
- 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/__tests__/column-crypto.test.ts +217 -0
- package/src/shared/column-crypto.ts +218 -0
- package/src/shared/domain-errors.ts +31 -0
- package/src/shared/git.contract.ts +27 -0
- package/src/shared/token-crypto.ts +28 -1
- package/src/tenancy/__tests__/static-tenant-source.test.ts +4 -1
- package/src/tenancy/static-tenant-source.ts +1 -3
- package/src/tenancy/tenant-secrets.ts +5 -1
- 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
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from '@utcp/sdk';
|
|
28
28
|
import { CodeModeUtcpClient } from '@utcp/code-mode';
|
|
29
29
|
import {
|
|
30
|
-
|
|
30
|
+
codeModeMetaTools,
|
|
31
31
|
META_TOOL_NAMES,
|
|
32
32
|
dispatchMetaTool,
|
|
33
33
|
dispatchToolCall,
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
type SkillSummary,
|
|
44
44
|
type LoadedSkill,
|
|
45
45
|
} from '@bevel-software/platform-mcp-core';
|
|
46
|
+
import type { KbLayout } from '@bevel-software/platform-shared';
|
|
46
47
|
import { bevelSecretsLoaderConfig } from '../secrets-vault/index.js';
|
|
47
48
|
import {
|
|
48
49
|
scopesCovered,
|
|
@@ -65,6 +66,7 @@ import {
|
|
|
65
66
|
composeAgentInstructions,
|
|
66
67
|
prefixToolDescription,
|
|
67
68
|
PREFIXED_TOOLS,
|
|
69
|
+
sharedRulesPointer,
|
|
68
70
|
type AgentPreambleReader,
|
|
69
71
|
type ComposedAgentInstructions,
|
|
70
72
|
} from '../agent-instructions/index.js';
|
|
@@ -91,6 +93,13 @@ export interface McpProxyOptions {
|
|
|
91
93
|
* carries the platform header alone.
|
|
92
94
|
*/
|
|
93
95
|
readAgentPreamble?: AgentPreambleReader;
|
|
96
|
+
/**
|
|
97
|
+
* The layout in effect, read per request: the shared file rules name the
|
|
98
|
+
* managed guide, and a deployment may rename it after boot (the setup save
|
|
99
|
+
* applies a name without a restart). A GETTER, so nothing snapshots the
|
|
100
|
+
* pre-setup default. Absent, the rules name `AGENTS.md`.
|
|
101
|
+
*/
|
|
102
|
+
kbLayout?: () => KbLayout;
|
|
94
103
|
/** Bounds of the downstream (`mcp.json`) connection pool; defaults are 4h idle / 5000 entries. */
|
|
95
104
|
downstreamPool?: Pick<DownstreamPoolOptions<unknown>, 'idleTtlMs' | 'maxEntries' | 'now'>;
|
|
96
105
|
/**
|
|
@@ -434,12 +443,26 @@ export class McpService {
|
|
|
434
443
|
const seen = new Set(META_TOOL_NAMES);
|
|
435
444
|
const dropped: string[] = [];
|
|
436
445
|
const direct: McpTool[] = [];
|
|
446
|
+
const examplePool: ProxiedTool[] = [];
|
|
437
447
|
for (const t of listed) {
|
|
438
448
|
const entry = toListedTool(t); // logs its own reason on a name/schema drop
|
|
439
449
|
if (!entry) {
|
|
440
450
|
dropped.push(t.mcpName);
|
|
441
451
|
continue;
|
|
442
452
|
}
|
|
453
|
+
// Kept for the worked example in the meta-tool descriptions: it must
|
|
454
|
+
// be derived from the tools THIS caller actually gets, not from the
|
|
455
|
+
// whole catalog, or a connection-key caller is shown an example naming
|
|
456
|
+
// a credential-gated tool the filter above just removed from its
|
|
457
|
+
// listing — a copied call that cannot work.
|
|
458
|
+
//
|
|
459
|
+
// BEFORE the duplicate drop below, on purpose. A tool dropped from the
|
|
460
|
+
// LISTING for sharing its name with another is still in the catalog a
|
|
461
|
+
// chain dispatches to, so the chain sees two tools under one name and
|
|
462
|
+
// refuses the call as ambiguous. The example has to know about both to
|
|
463
|
+
// steer clear of either (`chainExample` skips a name two tools share);
|
|
464
|
+
// shown only the survivor, it took that name for a safe one.
|
|
465
|
+
examplePool.push(t);
|
|
443
466
|
if (seen.has(entry.name)) {
|
|
444
467
|
dropped.push(`${entry.name} (duplicate)`);
|
|
445
468
|
continue;
|
|
@@ -456,18 +479,33 @@ export class McpService {
|
|
|
456
479
|
: entry,
|
|
457
480
|
);
|
|
458
481
|
}
|
|
482
|
+
// The meta-tools' examples name the namespace THIS endpoint registers the
|
|
483
|
+
// knowledge-base tools under, and a tool this caller is really served, so
|
|
484
|
+
// a chain copied out of the description runs. Built here rather than held
|
|
485
|
+
// as a constant: the local MCP server registers the same tools under a
|
|
486
|
+
// different name, and one fixed example is necessarily wrong on one of the
|
|
487
|
+
// two surfaces.
|
|
488
|
+
//
|
|
489
|
+
// The chain's description ends with the same pointer every file tool ends
|
|
490
|
+
// with: what a chain does with a failure, a large result or an image is
|
|
491
|
+
// stated once, in the shared rules, and the clients that drop
|
|
492
|
+
// `instructions` have the description and the guide to go on. Composed
|
|
493
|
+
// here because the guide's name is this deployment's setting.
|
|
494
|
+
const metaTools = codeModeMetaTools(EXTERNAL_KB_MANUAL_NAME, examplePool, {
|
|
495
|
+
sharedRulesPointer: sharedRulesPointer(this.opts.kbLayout?.()),
|
|
496
|
+
});
|
|
459
497
|
// Log only when a tool was dropped (name/schema/duplicate) — that's the
|
|
460
498
|
// anomaly worth surfacing, since a downstream client would otherwise hide
|
|
461
499
|
// it by rejecting the whole response.
|
|
462
500
|
if (dropped.length) {
|
|
463
501
|
log.warn(
|
|
464
|
-
`tools/list: serving ${
|
|
502
|
+
`tools/list: serving ${metaTools.length + direct.length} tool(s); ` +
|
|
465
503
|
`dropped ${dropped.length} non-listable: ${dropped.join(', ')}`,
|
|
466
504
|
);
|
|
467
505
|
}
|
|
468
506
|
return {
|
|
469
507
|
// Code-mode meta-tools first, then every validated direct tool.
|
|
470
|
-
tools: [...
|
|
508
|
+
tools: [...metaTools, ...direct],
|
|
471
509
|
};
|
|
472
510
|
});
|
|
473
511
|
|
|
@@ -613,13 +651,14 @@ export class McpService {
|
|
|
613
651
|
* fails over its preamble.
|
|
614
652
|
*/
|
|
615
653
|
private async composeAgentInstructions(): Promise<ComposedAgentInstructions> {
|
|
654
|
+
const layout = this.opts.kbLayout?.();
|
|
616
655
|
const read = this.opts.readAgentPreamble;
|
|
617
|
-
if (!read) return composeAgentInstructions(null);
|
|
656
|
+
if (!read) return composeAgentInstructions(null, layout);
|
|
618
657
|
try {
|
|
619
|
-
return composeAgentInstructions(await read());
|
|
658
|
+
return composeAgentInstructions(await read(), layout);
|
|
620
659
|
} catch (err) {
|
|
621
|
-
log.warn('could not read mcp-description.md; this request gets the platform
|
|
622
|
-
return composeAgentInstructions(null);
|
|
660
|
+
log.warn('could not read mcp-description.md; this request gets the platform text alone:', { err });
|
|
661
|
+
return composeAgentInstructions(null, layout);
|
|
623
662
|
}
|
|
624
663
|
}
|
|
625
664
|
|
|
@@ -309,7 +309,8 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
309
309
|
// the revoke (a refresh that raced it) is dead on arrival rather than
|
|
310
310
|
// live until its own expiry. A token from before connections existed
|
|
311
311
|
// (null `connection_id`) has no connection to be revoked and verifies on
|
|
312
|
-
// its own row alone.
|
|
312
|
+
// its own row alone. A switched-off account's tokens fail here too, and
|
|
313
|
+
// come back with it.
|
|
313
314
|
const [row] = await this.deps.db
|
|
314
315
|
.select({
|
|
315
316
|
id: oauthTokens.id,
|
|
@@ -330,6 +331,7 @@ export class BevelOAuthProvider implements OAuthServerProvider {
|
|
|
330
331
|
isNull(oauthTokens.revokedAt),
|
|
331
332
|
gt(oauthTokens.expiresAt, now),
|
|
332
333
|
or(isNull(oauthTokens.connectionId), isNull(agentConnections.revokedAt)),
|
|
334
|
+
isNull(users.deactivatedAt),
|
|
333
335
|
),
|
|
334
336
|
)
|
|
335
337
|
.limit(1);
|
|
@@ -53,7 +53,8 @@ describe('the creation endpoints behind the key-or-session gate', () => {
|
|
|
53
53
|
} as never;
|
|
54
54
|
const authService = {
|
|
55
55
|
getUserById: async (id: string) => (id === ALICE.id ? ALICE : null),
|
|
56
|
-
|
|
56
|
+
// A session is accepted only for an account that is on; Alice's is.
|
|
57
|
+
resolveSession: async (t: string) => {
|
|
57
58
|
if (t !== 'session-alice') throw new Error('bad token');
|
|
58
59
|
return { userId: ALICE.id, email: ALICE.email };
|
|
59
60
|
},
|
|
@@ -143,11 +143,14 @@ export class DbJoinRequestStore implements JoinRequestStore {
|
|
|
143
143
|
pluginKey: string;
|
|
144
144
|
}): Promise<JoinRequestRecord> {
|
|
145
145
|
const requesterEmail = input.requesterEmail.toLowerCase();
|
|
146
|
+
// Keyed by the blind index throughout: the email column is randomized
|
|
147
|
+
// ciphertext, so the uniqueness and every lookup go through its index,
|
|
148
|
+
// which is written and compared with the address.
|
|
146
149
|
const [inserted] = await this.db
|
|
147
150
|
.insert(pluginJoinRequests)
|
|
148
|
-
.values({ requesterEmail, requesterName: input.requesterName, pluginKey: input.pluginKey })
|
|
151
|
+
.values({ requesterEmail, requesterEmailBidx: requesterEmail, requesterName: input.requesterName, pluginKey: input.pluginKey })
|
|
149
152
|
.onConflictDoNothing({
|
|
150
|
-
target: [pluginJoinRequests.
|
|
153
|
+
target: [pluginJoinRequests.requesterEmailBidx, pluginJoinRequests.pluginKey],
|
|
151
154
|
})
|
|
152
155
|
.returning();
|
|
153
156
|
if (inserted) return toRecord(inserted);
|
|
@@ -170,7 +173,7 @@ export class DbJoinRequestStore implements JoinRequestStore {
|
|
|
170
173
|
})
|
|
171
174
|
.where(
|
|
172
175
|
and(
|
|
173
|
-
eq(pluginJoinRequests.
|
|
176
|
+
eq(pluginJoinRequests.requesterEmailBidx, requesterEmail),
|
|
174
177
|
eq(pluginJoinRequests.pluginKey, input.pluginKey),
|
|
175
178
|
),
|
|
176
179
|
)
|
|
@@ -191,7 +194,7 @@ export class DbJoinRequestStore implements JoinRequestStore {
|
|
|
191
194
|
const rows = await this.db
|
|
192
195
|
.select()
|
|
193
196
|
.from(pluginJoinRequests)
|
|
194
|
-
.where(eq(pluginJoinRequests.
|
|
197
|
+
.where(eq(pluginJoinRequests.requesterEmailBidx, requesterEmail));
|
|
195
198
|
return rows.map(toRecord);
|
|
196
199
|
}
|
|
197
200
|
|
|
@@ -413,6 +413,22 @@ describe('DeploymentSettingsService — validation', () => {
|
|
|
413
413
|
);
|
|
414
414
|
});
|
|
415
415
|
|
|
416
|
+
it('rejects a directory name carrying a line break, which every path is prefixed with', async () => {
|
|
417
|
+
// The name is joined onto every workspace-relative path, and a line break
|
|
418
|
+
// in a path is a separator to git's `cat-file --batch` — so a name
|
|
419
|
+
// carrying one would make the normaliser emit a path it refuses from a
|
|
420
|
+
// caller. Refused where the name is SET, which is here and at boot.
|
|
421
|
+
const { db } = makeDb();
|
|
422
|
+
const settings = new DeploymentSettingsService(db, ENC_KEY);
|
|
423
|
+
for (const bad of ['kb\nname', 'kb\rname', 'kb\u0000name', 'kb\u007Fname']) {
|
|
424
|
+
await expect(settings.save({ kbDirName: bad }, null), bad).rejects.toThrow(
|
|
425
|
+
/Invalid settings/,
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
// The ordinary name beside them, so the rule is not simply refusing both.
|
|
429
|
+
await expect(settings.save({ kbDirName: 'company-brain' }, null)).resolves.toBeTruthy();
|
|
430
|
+
});
|
|
431
|
+
|
|
416
432
|
it('rejects a username that would break out of the credential-helper snippet', async () => {
|
|
417
433
|
const { db } = makeDb();
|
|
418
434
|
const settings = new DeploymentSettingsService(db, ENC_KEY);
|
|
@@ -112,11 +112,12 @@ function listen(
|
|
|
112
112
|
server = app.listen(0);
|
|
113
113
|
const { port } = server.address() as AddressInfo;
|
|
114
114
|
const base = `http://127.0.0.1:${port}`;
|
|
115
|
-
|
|
115
|
+
/** `confirm` is the admin's answer to the question a move is asked, when they have given one. */
|
|
116
|
+
const save = (entries: Record<string, string>, confirm?: 'keep' | 'close') =>
|
|
116
117
|
fetch(`${base}/api/setup/settings`, {
|
|
117
118
|
method: 'POST',
|
|
118
119
|
headers: { 'Content-Type': 'application/json' },
|
|
119
|
-
body: JSON.stringify({ settings: entries }),
|
|
120
|
+
body: JSON.stringify({ settings: entries, ...(confirm ? { confirmRepositoryChange: confirm } : {}) }),
|
|
120
121
|
});
|
|
121
122
|
const status = async () => (await fetch(`${base}/api/setup/status`)).json() as Promise<Record<string, unknown>>;
|
|
122
123
|
return { save, status, settings, source, managed, ensured, probed, kb, phase: { runs: () => phaseRuns, saw: phaseSaw } };
|
|
@@ -199,63 +200,97 @@ describe('a deployment that is serving moves to another repository', () => {
|
|
|
199
200
|
return mounted;
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
/**
|
|
204
|
+
* A move is asked about before anything is stored: the save is refused
|
|
205
|
+
* until the answer comes back, naming the way left and the way moved to.
|
|
206
|
+
* Refused, it has changed nothing — the deployment is where it was.
|
|
207
|
+
*/
|
|
208
|
+
it('is asked first, and until it answers nothing has moved', async () => {
|
|
209
|
+
const { save, status, phase, source, settings } = await serving();
|
|
204
210
|
const runsBefore = phase.runs();
|
|
205
211
|
const res = await save({ gitMode: 'managed' });
|
|
206
|
-
|
|
207
|
-
expect(
|
|
208
|
-
expect(
|
|
209
|
-
expect(
|
|
210
|
-
|
|
212
|
+
expect(res.status).toBe(409);
|
|
213
|
+
expect(await res.json()).toMatchObject({ repositoryChange: { openChangeRequests: 0, from: 'token', to: 'managed' } });
|
|
214
|
+
expect(settings.resolve('gitMode')).toBe('');
|
|
215
|
+
expect(source.mode()).toBe('token');
|
|
216
|
+
expect(source.url()).toBe(HOSTED.kbRepoUrl);
|
|
211
217
|
expect(phase.runs()).toBe(runsBefore);
|
|
218
|
+
expect(await status()).toMatchObject({ complete: true, repository: { mode: 'token', chosen: 'token' } });
|
|
212
219
|
});
|
|
213
220
|
|
|
214
221
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
* went out with no credential, and a branch opened in between was cloned
|
|
220
|
-
* from a repository nothing had prepared.
|
|
222
|
+
* Confirmed, the move takes effect ON THE SAVE: the way chosen is the way
|
|
223
|
+
* in effect, the startup phase runs against the repository moved to, and
|
|
224
|
+
* no restart is owed. It once waited for the next start, which an admin of
|
|
225
|
+
* a hosted workspace has no way to give.
|
|
221
226
|
*/
|
|
222
|
-
it('
|
|
223
|
-
const { save, source } = await serving();
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
expect(
|
|
228
|
-
expect(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
227
|
+
it('moves on the save once confirmed, and owes no restart', async () => {
|
|
228
|
+
const { save, status, phase, ensured, managed, source } = await serving();
|
|
229
|
+
const runsBefore = phase.runs();
|
|
230
|
+
const res = await save({ gitMode: 'managed' }, 'keep');
|
|
231
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
232
|
+
expect(res.status, JSON.stringify(body)).toBe(200);
|
|
233
|
+
expect(body).toMatchObject({
|
|
234
|
+
ok: true,
|
|
235
|
+
complete: true,
|
|
236
|
+
restartRequired: false,
|
|
237
|
+
repository: { mode: 'managed', chosen: 'managed' },
|
|
238
|
+
repositoryChange: { choice: 'keep' },
|
|
239
|
+
});
|
|
240
|
+
expect(ensured).toHaveLength(1);
|
|
241
|
+
// The phase that sets the old working copies aside ran, against the repository moved to.
|
|
242
|
+
expect(phase.runs()).toBe(runsBefore + 1);
|
|
243
|
+
expect(phase.saw.at(-1)).toBe(managed.path);
|
|
244
|
+
expect(source.url()).toBe(managed.path);
|
|
245
|
+
expect(await status()).toMatchObject({ complete: true, repository: { mode: 'managed', chosen: 'managed' } });
|
|
232
246
|
});
|
|
233
247
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
expect(source.mode()).toBe('managed');
|
|
240
|
-
expect(source.url()).toBe(managed.path);
|
|
248
|
+
/** One way's credential is never presented to another way's repository. */
|
|
249
|
+
it('presents the repository moved to nothing of the one that was left', async () => {
|
|
250
|
+
const { save, source } = await serving();
|
|
251
|
+
expect(source.credentials.token()).toBe('the-token');
|
|
252
|
+
await save({ gitMode: 'managed' }, 'keep');
|
|
241
253
|
expect(source.credentials.token()).toBeNull();
|
|
242
254
|
});
|
|
243
255
|
|
|
244
|
-
it('
|
|
256
|
+
it('owes nothing through later saves about other things, and is not asked again', async () => {
|
|
245
257
|
const { save } = await serving();
|
|
246
|
-
await save({ gitMode: 'managed' });
|
|
247
|
-
const
|
|
248
|
-
expect(
|
|
258
|
+
await save({ gitMode: 'managed' }, 'keep');
|
|
259
|
+
const res = await save({ kbSyncSecret: 'a-secret-of-sixteen-or-more' });
|
|
260
|
+
expect(res.status).toBe(200);
|
|
261
|
+
expect(await res.json()).toMatchObject({ ok: true, restartRequired: false, repository: { mode: 'managed', chosen: 'managed' } });
|
|
249
262
|
});
|
|
250
263
|
|
|
251
|
-
it('
|
|
252
|
-
const { save,
|
|
253
|
-
await save({ gitMode: 'managed' });
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
expect(
|
|
257
|
-
|
|
258
|
-
expect(
|
|
264
|
+
it('is asked again to move back: that is a move too', async () => {
|
|
265
|
+
const { save, source } = await serving();
|
|
266
|
+
await save({ gitMode: 'managed' }, 'keep');
|
|
267
|
+
const back = await save({ gitMode: 'token' });
|
|
268
|
+
expect(back.status).toBe(409);
|
|
269
|
+
expect(await back.json()).toMatchObject({ repositoryChange: { from: 'managed', to: 'token' } });
|
|
270
|
+
const done = await save({ gitMode: 'token' }, 'keep');
|
|
271
|
+
expect(await done.json()).toMatchObject({ ok: true, restartRequired: false, repository: { mode: 'token', chosen: 'token' } });
|
|
272
|
+
expect(source.url()).toBe(HOSTED.kbRepoUrl);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
/** Another address under the same way is the same question, with the same answer. */
|
|
276
|
+
it('asks the same of another address under the way it is on', async () => {
|
|
277
|
+
const { save, source, phase } = await serving();
|
|
278
|
+
const moved = { kbRepoUrl: 'https://git.example.com/acme/another.git', gitToken: 'another-token' };
|
|
279
|
+
const asked = await save(moved);
|
|
280
|
+
expect(asked.status).toBe(409);
|
|
281
|
+
expect(await asked.json()).toMatchObject({ repositoryChange: { from: 'token', to: 'token' } });
|
|
282
|
+
const runsBefore = phase.runs();
|
|
283
|
+
const done = await save(moved, 'close');
|
|
284
|
+
expect(await done.json()).toMatchObject({ ok: true, restartRequired: false, repositoryChange: { choice: 'close' } });
|
|
285
|
+
expect(phase.runs()).toBe(runsBefore + 1);
|
|
286
|
+
expect(source.url()).toBe(moved.kbRepoUrl);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('asks nothing of a save that only spells the same address another way', async () => {
|
|
290
|
+
const { save } = await serving();
|
|
291
|
+
const res = await save({ kbRepoUrl: 'https://GIT.example.com/acme/kb/' });
|
|
292
|
+
expect(res.status).toBe(200);
|
|
293
|
+
expect(await res.json()).not.toHaveProperty('repositoryChange');
|
|
259
294
|
});
|
|
260
295
|
|
|
261
296
|
it('owes none for naming the way it was already on', async () => {
|
|
@@ -268,10 +303,11 @@ describe('a deployment that is serving moves to another repository', () => {
|
|
|
268
303
|
});
|
|
269
304
|
|
|
270
305
|
/**
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* not
|
|
306
|
+
* Behind a shut gate nothing is running on the way in effect, however
|
|
307
|
+
* completely the settings were answered, so a deployment there is got out of
|
|
308
|
+
* a repository that does not work by choosing one that does. One that never
|
|
309
|
+
* served is not even asked: there is no repository anyone worked on to
|
|
310
|
+
* leave. One that served before its boot failed is asked, as any move is.
|
|
275
311
|
*/
|
|
276
312
|
describe('a deployment that answered everything and serves nobody', () => {
|
|
277
313
|
const isHosted = (url: string) => url === HOSTED.kbRepoUrl;
|
|
@@ -300,19 +336,16 @@ describe('a deployment that answered everything and serves nobody', () => {
|
|
|
300
336
|
const { save, status, phase, managed } = listen({ kb: testKbContext(), bootFailed: true, unreachable: isHosted });
|
|
301
337
|
expect(await status()).toMatchObject({ complete: false, repository: { mode: 'token' } });
|
|
302
338
|
|
|
303
|
-
|
|
339
|
+
// It served before this boot, so there may be work on the repository it
|
|
340
|
+
// leaves: it is asked, and nothing moves until it answers.
|
|
341
|
+
const asked = await save({ gitMode: 'managed' });
|
|
342
|
+
expect(asked.status).toBe(409);
|
|
343
|
+
expect(phase.saw).toEqual([]);
|
|
344
|
+
|
|
345
|
+
const body = (await (await save({ gitMode: 'managed' }, 'keep')).json()) as Record<string, unknown>;
|
|
304
346
|
expect(phase.saw).toEqual([managed.path]);
|
|
305
347
|
expect(body).toMatchObject({ complete: true, restartRequired: false, repository: { mode: 'managed', chosen: 'managed' } });
|
|
306
348
|
});
|
|
307
|
-
|
|
308
|
-
it('is still asked for a restart once it IS serving: the same save, a different deployment', async () => {
|
|
309
|
-
const { save, phase } = listen({ kb: testKbContext() });
|
|
310
|
-
await save({ ...HOSTED, ...BRANCHES });
|
|
311
|
-
const runs = phase.runs();
|
|
312
|
-
const body = (await (await save({ gitMode: 'managed' })).json()) as Record<string, unknown>;
|
|
313
|
-
expect(body).toMatchObject({ complete: true, restartRequired: true, repository: { mode: 'token', chosen: 'managed' } });
|
|
314
|
-
expect(phase.runs()).toBe(runs);
|
|
315
|
-
});
|
|
316
349
|
});
|
|
317
350
|
|
|
318
351
|
describe('a deployment whose way is chosen by the environment', () => {
|
|
@@ -108,11 +108,12 @@ function listen(
|
|
|
108
108
|
);
|
|
109
109
|
server = app.listen(0);
|
|
110
110
|
const base = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
|
|
111
|
-
|
|
111
|
+
/** `confirm` is the admin's answer to the question a move is asked. */
|
|
112
|
+
const save = async (entries: Record<string, string>, confirm?: 'keep' | 'close') => {
|
|
112
113
|
const res = await fetch(`${base}/api/setup/settings`, {
|
|
113
114
|
method: 'POST',
|
|
114
115
|
headers: { 'Content-Type': 'application/json' },
|
|
115
|
-
body: JSON.stringify({ settings: entries }),
|
|
116
|
+
body: JSON.stringify({ settings: entries, ...(confirm ? { confirmRepositoryChange: confirm } : {}) }),
|
|
116
117
|
});
|
|
117
118
|
return { status: res.status, body: (await res.json()) as Record<string, unknown> & { problems?: Record<string, string> } };
|
|
118
119
|
};
|
|
@@ -271,12 +272,28 @@ describe('a deployment on GitHub that is serving', () => {
|
|
|
271
272
|
expect(probed).toHaveLength(asked);
|
|
272
273
|
});
|
|
273
274
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Another repository on GitHub is a move like any other: GitHub is asked
|
|
277
|
+
* about the new one, the admin is asked about the move, and confirmed it
|
|
278
|
+
* happens on the save.
|
|
279
|
+
*/
|
|
280
|
+
it('proves the new repository, asks about the move, and moves on the save once confirmed', async () => {
|
|
281
|
+
const { save, probed, source, phaseRuns } = await serving();
|
|
282
|
+
const before = probed.length;
|
|
283
|
+
const asked = await save({ githubRepository: 'acme/another' });
|
|
284
|
+
expect(asked.status).toBe(409);
|
|
285
|
+
expect(asked.body).toMatchObject({ repositoryChange: { from: 'github-app', to: 'github-app' } });
|
|
286
|
+
// The connection is proven before the question is put: nobody agrees to
|
|
287
|
+
// a move only to be told the repository cannot be reached.
|
|
288
|
+
expect(probed).toHaveLength(before + 1);
|
|
280
289
|
expect(probed.at(-1)!.url).toBe('https://github.com/acme/another.git');
|
|
290
|
+
expect(source.url()).toBe('https://github.com/acme/kb.git');
|
|
291
|
+
|
|
292
|
+
const runs = phaseRuns();
|
|
293
|
+
const moved = await save({ githubRepository: 'acme/another' }, 'keep');
|
|
294
|
+
expect(moved.status, JSON.stringify(moved.body)).toBe(200);
|
|
295
|
+
expect(moved.body).toMatchObject({ ok: true, restartRequired: false, repositoryChange: { choice: 'keep' } });
|
|
296
|
+
expect(source.url()).toBe('https://github.com/acme/another.git');
|
|
297
|
+
expect(phaseRuns()).toBe(runs + 1);
|
|
281
298
|
});
|
|
282
299
|
});
|
|
@@ -131,11 +131,12 @@ function boot(kb: KbContext) {
|
|
|
131
131
|
);
|
|
132
132
|
server = app.listen(0);
|
|
133
133
|
const base = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
|
|
134
|
-
|
|
134
|
+
/** `confirm` is the admin's answer to the question a move is asked. */
|
|
135
|
+
const save = async (entries: Record<string, string>, confirm?: 'keep' | 'close') => {
|
|
135
136
|
const res = await fetch(`${base}/api/setup/settings`, {
|
|
136
137
|
method: 'POST',
|
|
137
138
|
headers: { 'content-type': 'application/json' },
|
|
138
|
-
body: JSON.stringify({ settings: entries }),
|
|
139
|
+
body: JSON.stringify({ settings: entries, ...(confirm ? { confirmRepositoryChange: confirm } : {}) }),
|
|
139
140
|
});
|
|
140
141
|
return { status: res.status, body: (await res.json()) as Record<string, unknown> };
|
|
141
142
|
};
|
|
@@ -175,7 +176,7 @@ describe('the save that chooses a repository the deployment keeps', () => {
|
|
|
175
176
|
|
|
176
177
|
describe('a deployment that moves to a repository it keeps', () => {
|
|
177
178
|
it('leaves the repository it had untouched and sets its working copies aside, unpushed work included', async () => {
|
|
178
|
-
const { save, managed, hosted,
|
|
179
|
+
const { save, managed, hosted, source, workingCopy, setAsideRoot } = boot(
|
|
179
180
|
testKbContext({ branchModel: { defaultBranch: 'main', protectedBranches: ['main'] } }),
|
|
180
181
|
);
|
|
181
182
|
// A deployment on a repository of its own, set up and serving. The
|
|
@@ -203,17 +204,19 @@ describe('a deployment that moves to a repository it keeps', () => {
|
|
|
203
204
|
await git(copy, ['commit', '-m', 'never pushed']);
|
|
204
205
|
const hostedBefore = await git(hosted, ['for-each-ref']);
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
// Asked first: until the admin answers, the working copy is where it
|
|
208
|
+
// was and so is the deployment.
|
|
209
|
+
const asked = await save({ gitMode: 'managed' });
|
|
210
|
+
expect(asked.status).toBe(409);
|
|
210
211
|
expect(await git(copy, ['config', '--get', 'remote.origin.url'])).toBe(hosted);
|
|
211
212
|
expect(source.url()).toBe(hosted);
|
|
212
213
|
|
|
213
|
-
//
|
|
214
|
-
// phase brings the working copies into line.
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
// Confirmed, the move happens on the save, with no restart: the way
|
|
215
|
+
// chosen takes effect and the phase brings the working copies into line.
|
|
216
|
+
const moved = await save({ gitMode: 'managed' }, 'keep');
|
|
217
|
+
expect(moved.status, JSON.stringify(moved.body)).toBe(200);
|
|
218
|
+
expect(moved.body).toMatchObject({ ok: true, complete: true, restartRequired: false });
|
|
219
|
+
expect(source.url()).toBe(managed.path);
|
|
217
220
|
|
|
218
221
|
expect(await git(hosted, ['for-each-ref'])).toBe(hostedBefore);
|
|
219
222
|
expect(await git(copy, ['config', '--get', 'remote.origin.url'])).toBe(managed.path);
|