@bevel-software/platform-core-backend 0.26.0 → 0.28.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/agent-guide/access-control.md +17 -0
- package/agent-guide/conventions.md +8 -0
- package/agent-guide/html-views.md +83 -0
- package/agent-guide/new-knowledge-base.md +24 -0
- package/agent-guide/tool-manuals.md +10 -0
- package/dist/assets.d.ts +19 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +26 -2
- package/dist/assets.js.map +1 -1
- package/dist/core/catalog-revision.d.ts +19 -1
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +14 -3
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/create-core-server.d.ts +5 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +56 -5
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +33 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +105 -8
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +16 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +18 -1
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +21 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +22 -1
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +17 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +2 -0
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +31 -0
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +4 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +66 -7
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +1 -1
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +35 -6
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +36 -9
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access/user-access-removal.service.js +1 -1
- package/dist/modules/access/user-access-removal.service.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +6 -0
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.js +8 -0
- package/dist/modules/agent-guide/agent-guide.js.map +1 -1
- package/dist/modules/agent-guide/index.d.ts +1 -1
- package/dist/modules/agent-guide/index.d.ts.map +1 -1
- package/dist/modules/agent-guide/index.js +1 -1
- package/dist/modules/agent-guide/index.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +5 -0
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +19 -2
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +19 -19
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/audit/agent-audit.service.d.ts +21 -2
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +43 -2
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/agent-connection.routes.d.ts +34 -0
- package/dist/modules/audit/agent-connection.routes.d.ts.map +1 -0
- package/dist/modules/audit/agent-connection.routes.js +38 -0
- package/dist/modules/audit/agent-connection.routes.js.map +1 -0
- package/dist/modules/audit/audit.contract.d.ts +24 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/audit/index.d.ts +1 -0
- package/dist/modules/audit/index.d.ts.map +1 -1
- package/dist/modules/audit/index.js +1 -0
- package/dist/modules/audit/index.js.map +1 -1
- package/dist/modules/auth/account-hooks.d.ts +46 -0
- package/dist/modules/auth/account-hooks.d.ts.map +1 -0
- package/dist/modules/auth/account-hooks.js +25 -0
- package/dist/modules/auth/account-hooks.js.map +1 -0
- package/dist/modules/auth/account-switch-guard.d.ts +15 -0
- package/dist/modules/auth/account-switch-guard.d.ts.map +1 -0
- package/dist/modules/auth/account-switch-guard.js +2 -0
- package/dist/modules/auth/account-switch-guard.js.map +1 -0
- package/dist/modules/auth/account-switch-lock.d.ts +34 -0
- package/dist/modules/auth/account-switch-lock.d.ts.map +1 -0
- package/dist/modules/auth/account-switch-lock.js +48 -0
- package/dist/modules/auth/account-switch-lock.js.map +1 -0
- package/dist/modules/auth/account.routes.d.ts +4 -1
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +110 -13
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +7 -0
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +1 -1
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +101 -6
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +246 -51
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/password-hash.d.ts +5 -2
- package/dist/modules/auth/password-hash.d.ts.map +1 -1
- package/dist/modules/auth/password-hash.js +5 -2
- package/dist/modules/auth/password-hash.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +11 -3
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +96 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +32 -3
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/pg-error.d.ts +9 -0
- package/dist/modules/database/pg-error.d.ts.map +1 -0
- package/dist/modules/database/pg-error.js +17 -0
- package/dist/modules/database/pg-error.js.map +1 -0
- package/dist/modules/embed/account-link.service.d.ts +42 -0
- package/dist/modules/embed/account-link.service.d.ts.map +1 -0
- package/dist/modules/embed/account-link.service.js +50 -0
- package/dist/modules/embed/account-link.service.js.map +1 -0
- package/dist/modules/embed/embed-link.d.ts +58 -0
- package/dist/modules/embed/embed-link.d.ts.map +1 -0
- package/dist/modules/embed/embed-link.js +160 -0
- package/dist/modules/embed/embed-link.js.map +1 -0
- package/dist/modules/embed/embed.errors.d.ts +30 -0
- package/dist/modules/embed/embed.errors.d.ts.map +1 -0
- package/dist/modules/embed/embed.errors.js +42 -0
- package/dist/modules/embed/embed.errors.js.map +1 -0
- package/dist/modules/embed/embed.interface.d.ts +204 -0
- package/dist/modules/embed/embed.interface.d.ts.map +1 -0
- package/dist/modules/embed/embed.interface.js +2 -0
- package/dist/modules/embed/embed.interface.js.map +1 -0
- package/dist/modules/embed/embed.routes.d.ts +59 -0
- package/dist/modules/embed/embed.routes.d.ts.map +1 -0
- package/dist/modules/embed/embed.routes.js +343 -0
- package/dist/modules/embed/embed.routes.js.map +1 -0
- package/dist/modules/embed/embed.service.d.ts +169 -0
- package/dist/modules/embed/embed.service.d.ts.map +1 -0
- package/dist/modules/embed/embed.service.js +627 -0
- package/dist/modules/embed/embed.service.js.map +1 -0
- package/dist/modules/embed/embed.tools.d.ts +51 -0
- package/dist/modules/embed/embed.tools.d.ts.map +1 -0
- package/dist/modules/embed/embed.tools.js +170 -0
- package/dist/modules/embed/embed.tools.js.map +1 -0
- package/dist/modules/embed/mcp-app.d.ts +64 -0
- package/dist/modules/embed/mcp-app.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.js +147 -0
- package/dist/modules/embed/mcp-app.js.map +1 -0
- package/dist/modules/embed/mcp-app.routes.d.ts +21 -0
- package/dist/modules/embed/mcp-app.routes.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.routes.js +36 -0
- package/dist/modules/embed/mcp-app.routes.js.map +1 -0
- package/dist/modules/github-app/github-app.connection.d.ts +15 -0
- package/dist/modules/github-app/github-app.connection.d.ts.map +1 -1
- package/dist/modules/github-app/github-app.connection.js +34 -3
- package/dist/modules/github-app/github-app.connection.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts +65 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +300 -0
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +5 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.js +6 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +2 -1
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +28 -1
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +64 -8
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +32 -4
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/onboarding/onboarding.contract.d.ts +80 -0
- package/dist/modules/onboarding/onboarding.contract.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.contract.js +7 -0
- package/dist/modules/onboarding/onboarding.contract.js.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts +18 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.js +81 -0
- package/dist/modules/onboarding/onboarding.routes.js.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts +213 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.js +492 -0
- package/dist/modules/onboarding/starter-pack.service.js.map +1 -0
- package/dist/modules/onboarding/starter-packs.d.ts +78 -0
- package/dist/modules/onboarding/starter-packs.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-packs.js +156 -0
- package/dist/modules/onboarding/starter-packs.js.map +1 -0
- package/dist/modules/plugins/index.d.ts +1 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.d.ts +19 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +18 -1
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +0 -36
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +31 -0
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +34 -0
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +134 -1
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/repository-source.d.ts +1 -0
- package/dist/modules/settings/repository-source.d.ts.map +1 -1
- package/dist/modules/settings/repository-source.js +3 -3
- package/dist/modules/settings/repository-source.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +50 -32
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts +10 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.js +11 -3
- package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +8 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +24 -2
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +59 -2
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +9 -4
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/index.d.ts +28 -1
- package/dist/modules/tool-helpers/index.d.ts.map +1 -1
- package/dist/modules/tool-helpers/index.js +28 -1
- package/dist/modules/tool-helpers/index.js.map +1 -1
- package/dist/modules/tool-helpers/route-argument-check.d.ts +10 -0
- package/dist/modules/tool-helpers/route-argument-check.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-argument-check.js +132 -0
- package/dist/modules/tool-helpers/route-argument-check.js.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts +89 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js +113 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js.map +1 -0
- package/dist/modules/tool-helpers/tool-def.d.ts +71 -0
- package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-def.js +70 -6
- package/dist/modules/tool-helpers/tool-def.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +46 -6
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +157 -8
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +9 -0
- 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-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +14 -5
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +6 -2
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
- package/dist/modules/tool-registry/description-length.js +27 -5
- package/dist/modules/tool-registry/description-length.js.map +1 -1
- package/dist/modules/tool-registry/guide-first.d.ts +4 -0
- package/dist/modules/tool-registry/guide-first.d.ts.map +1 -1
- package/dist/modules/tool-registry/guide-first.js +9 -1
- package/dist/modules/tool-registry/guide-first.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +53 -0
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js +97 -68
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +8 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +124 -37
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +29 -5
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +62 -5
- 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 +172 -36
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +15 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +78 -6
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +15 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +25 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +6 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +23 -6
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +1 -1
- 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 +2 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/sanitize-error.d.ts +16 -0
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
- package/dist/modules/workflow/sanitize-error.js +45 -0
- package/dist/modules/workflow/sanitize-error.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +183 -15
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +807 -107
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-download.builder.d.ts +105 -0
- package/dist/modules/workspace/agent-download.builder.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.builder.js +238 -0
- package/dist/modules/workspace/agent-download.builder.js.map +1 -0
- package/dist/modules/workspace/agent-download.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-download.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.routes.js +156 -0
- package/dist/modules/workspace/agent-download.routes.js.map +1 -0
- package/dist/modules/workspace/agent-download.store.d.ts +225 -0
- package/dist/modules/workspace/agent-download.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.store.js +423 -0
- package/dist/modules/workspace/agent-download.store.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +10 -1
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -1
- package/dist/modules/workspace/agent-upload.store.js +16 -6
- package/dist/modules/workspace/agent-upload.store.js.map +1 -1
- package/dist/modules/workspace/file-readers/raw-mime.d.ts +6 -0
- package/dist/modules/workspace/file-readers/raw-mime.d.ts.map +1 -0
- package/dist/modules/workspace/file-readers/raw-mime.js +38 -0
- package/dist/modules/workspace/file-readers/raw-mime.js.map +1 -0
- package/dist/modules/workspace/file-readers/text-reader.d.ts +5 -5
- package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.js +5 -8
- package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
- package/dist/modules/workspace/first-run.d.ts +111 -0
- package/dist/modules/workspace/first-run.d.ts.map +1 -0
- package/dist/modules/workspace/first-run.js +181 -0
- package/dist/modules/workspace/first-run.js.map +1 -0
- package/dist/modules/workspace/move-links.d.ts +92 -0
- package/dist/modules/workspace/move-links.d.ts.map +1 -0
- package/dist/modules/workspace/move-links.js +173 -0
- package/dist/modules/workspace/move-links.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +3 -13
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +63 -22
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +75 -2
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +197 -24
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +59 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +443 -65
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/write-access/write-access.d.ts.map +1 -1
- package/dist/modules/write-access/write-access.js +9 -0
- package/dist/modules/write-access/write-access.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +73 -1
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +86 -12
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +10 -1
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/utf8-text.d.ts +15 -0
- package/dist/shared/utf8-text.d.ts.map +1 -0
- package/dist/shared/utf8-text.js +20 -0
- package/dist/shared/utf8-text.js.map +1 -0
- package/dist/tenancy/static-tenant-source.d.ts +2 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +9 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/mcp-app/page.html +349 -0
- package/migrations/0017_atlassian_account_links.sql +37 -0
- package/migrations/meta/0017_snapshot.json +2396 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +5 -3
- package/src/__tests__/packaged-assets-ship.test.ts +16 -4
- package/src/__tests__/pg-scratch.ts +48 -0
- package/src/assets.ts +28 -2
- package/src/core/__tests__/catalog-revision.test.ts +40 -0
- package/src/core/__tests__/gated-boot.test.ts +1 -0
- package/src/core/__tests__/lifecycle.test.ts +14 -0
- package/src/core/catalog-revision.ts +15 -2
- package/src/core/create-core-server.ts +69 -3
- package/src/core/create-core-services.ts +151 -6
- package/src/core/lifecycle.ts +28 -2
- package/src/core-config.ts +24 -1
- package/src/index.ts +2 -2
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +1 -0
- package/src/modules/access/__tests__/access-control.service.test.ts +19 -0
- package/src/modules/access/__tests__/access.routes.admins.test.ts +203 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +25 -5
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +1 -1
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +1 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +1 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +1 -1
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +1 -0
- package/src/modules/access/__tests__/roles-admin.security.test.ts +1 -1
- package/src/modules/access/__tests__/roles-admin.service.test.ts +15 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +132 -7
- package/src/modules/access/__tests__/user-access-removal.service.test.ts +2 -2
- package/src/modules/access/__tests__/users-db-double.ts +12 -3
- package/src/modules/access/access-control.interface.ts +23 -0
- package/src/modules/access/access-control.service.ts +35 -0
- package/src/modules/access/access.routes.ts +67 -6
- package/src/modules/access/directory-sync-bot.ts +1 -1
- package/src/modules/access/roles-admin.service.ts +51 -11
- package/src/modules/access/user-access-removal.service.ts +1 -1
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +58 -0
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +19 -1
- package/src/modules/agent-guide/agent-guide.ts +9 -0
- package/src/modules/agent-guide/index.ts +1 -0
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +14 -0
- package/src/modules/agent-instructions/compose.ts +17 -2
- package/src/modules/agent-instructions/shared-file-rules.ts +19 -19
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +64 -4
- package/src/modules/audit/__tests__/agent-connection.routes.test.ts +102 -0
- package/src/modules/audit/agent-audit.service.ts +58 -3
- package/src/modules/audit/agent-connection.routes.ts +56 -0
- package/src/modules/audit/audit.contract.ts +26 -0
- package/src/modules/audit/index.ts +1 -0
- package/src/modules/auth/__tests__/account-deactivation.test.ts +145 -16
- package/src/modules/auth/__tests__/account-hooks.test.ts +57 -0
- package/src/modules/auth/__tests__/account-switch-lock.pg.test.ts +78 -0
- package/src/modules/auth/__tests__/account-switch-lock.test.ts +43 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +387 -9
- package/src/modules/auth/__tests__/auth.service.created.pg.test.ts +69 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +159 -1
- package/src/modules/auth/account-hooks.ts +69 -0
- package/src/modules/auth/account-switch-guard.ts +14 -0
- package/src/modules/auth/account-switch-lock.ts +58 -0
- package/src/modules/auth/account.routes.ts +122 -13
- package/src/modules/auth/auth.middleware.ts +1 -1
- package/src/modules/auth/auth.service.ts +268 -52
- package/src/modules/auth/password-hash.ts +5 -2
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +18 -2
- package/src/modules/code-mode/code-mode.tool.ts +13 -2
- package/src/modules/database/__tests__/pg-error.test.ts +21 -0
- package/src/modules/database/__tests__/pii-backfill.pg.test.ts +6 -27
- package/src/modules/database/core-schema.ts +33 -3
- package/src/modules/database/pg-error.ts +15 -0
- package/src/modules/embed/__tests__/account-links.migration.test.ts +98 -0
- package/src/modules/embed/__tests__/embed-link.test.ts +122 -0
- package/src/modules/embed/__tests__/embed.routes.test.ts +362 -0
- package/src/modules/embed/__tests__/embed.service.lock.test.ts +193 -0
- package/src/modules/embed/__tests__/embed.service.test.ts +805 -0
- package/src/modules/embed/__tests__/embed.tools.test.ts +304 -0
- package/src/modules/embed/__tests__/mcp-app.test.ts +207 -0
- package/src/modules/embed/account-link.service.ts +76 -0
- package/src/modules/embed/embed-link.ts +180 -0
- package/src/modules/embed/embed.errors.ts +43 -0
- package/src/modules/embed/embed.interface.ts +203 -0
- package/src/modules/embed/embed.routes.ts +363 -0
- package/src/modules/embed/embed.service.ts +705 -0
- package/src/modules/embed/embed.tools.ts +206 -0
- package/src/modules/embed/mcp-app.routes.ts +41 -0
- package/src/modules/embed/mcp-app.ts +173 -0
- package/src/modules/github-app/__tests__/github-app.test.ts +76 -0
- package/src/modules/github-app/github-app.connection.ts +38 -4
- package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +304 -0
- package/src/modules/kb-fs/locking-filesystem.ts +287 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +35 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +2 -0
- package/src/modules/kb-sync/kb-sync.service.ts +6 -0
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +68 -11
- package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +37 -2
- package/src/modules/mcp/__tests__/mcp.app.e2e.test.ts +200 -0
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +3 -1
- package/src/modules/mcp/__tests__/mcp.service.test.ts +23 -13
- package/src/modules/mcp/__tests__/tool-call-interface.e2e.test.ts +499 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +2 -1
- package/src/modules/mcp/mcp.service.ts +81 -12
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +42 -4
- package/src/modules/onboarding/__tests__/onboarding.routes.test.ts +163 -0
- package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +635 -0
- package/src/modules/onboarding/__tests__/starter-packs.test.ts +169 -0
- package/src/modules/onboarding/onboarding.contract.ts +85 -0
- package/src/modules/onboarding/onboarding.routes.ts +85 -0
- package/src/modules/onboarding/starter-pack.service.ts +580 -0
- package/src/modules/onboarding/starter-packs.ts +182 -0
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +25 -2
- package/src/modules/plugins/index.ts +1 -0
- package/src/modules/plugins/plugin-provision.service.ts +22 -1
- package/src/modules/plugins/plugins.routes.ts +29 -0
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +150 -1
- package/src/modules/settings/__tests__/repository-source.test.ts +9 -0
- package/src/modules/settings/deployment-settings.service.ts +131 -1
- package/src/modules/settings/repository-source.ts +4 -4
- package/src/modules/skills/__tests__/skills.tools.description.test.ts +5 -1
- package/src/modules/skills/skills.tools.ts +55 -35
- package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +32 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +65 -0
- package/src/modules/tool-auth/connection-key-rejection.ts +13 -3
- package/src/modules/tool-auth/external-api-key.interface.ts +9 -0
- package/src/modules/tool-auth/external-api-key.service.ts +61 -4
- package/src/modules/tool-auth/tool-auth.middleware.ts +13 -4
- package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +502 -0
- package/src/modules/tool-helpers/__tests__/route-argument-check.test.ts +337 -0
- package/src/modules/tool-helpers/index.ts +34 -1
- package/src/modules/tool-helpers/route-argument-check.ts +149 -0
- package/src/modules/tool-helpers/route-tool-schemas.ts +171 -0
- package/src/modules/tool-helpers/tool-def.ts +132 -7
- package/src/modules/tool-helpers/tool-handler.ts +196 -8
- package/src/modules/tool-helpers/tool.contract.ts +9 -0
- package/src/modules/tool-manuals/tool-manuals.tools.ts +15 -5
- package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +14 -1
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +23 -10
- package/src/modules/tool-registry/description-length.ts +34 -7
- package/src/modules/tool-registry/guide-first.ts +8 -1
- package/src/modules/workflow/__tests__/file-lock.service.deletion-gate.test.ts +104 -0
- package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +66 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +31 -2
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +110 -6
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +89 -1
- package/src/modules/workflow/__tests__/workflow.routes.push-refused.test.ts +202 -0
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +6 -6
- package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +1026 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +468 -10
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +4 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +5 -4
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +54 -2
- package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +41 -5
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +127 -2
- package/src/modules/workflow/agent-tools/change-request-read.tools.ts +157 -122
- package/src/modules/workflow/agent-tools/workflow.tools.ts +129 -35
- package/src/modules/workflow/file-lock.service.ts +67 -5
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +164 -0
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +48 -1
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +231 -2
- package/src/modules/workflow/git/git.service.ts +176 -38
- package/src/modules/workflow/git/node-git-runner.ts +82 -7
- package/src/modules/workflow/pending-commits.service.ts +29 -1
- package/src/modules/workflow/pending-commits.worker.ts +24 -6
- package/src/modules/workflow/recovery-bot.ts +1 -1
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +2 -1
- package/src/modules/workflow/sanitize-error.ts +50 -0
- package/src/modules/workflow/workflow.service.ts +881 -113
- package/src/modules/workspace/__tests__/agent-downloads.test.ts +823 -0
- package/src/modules/workspace/__tests__/first-run.test.ts +234 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +5 -2
- package/src/modules/workspace/__tests__/md-links.test.ts +353 -0
- package/src/modules/workspace/__tests__/move-links.test.ts +261 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +219 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -0
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +102 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +52 -5
- package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +190 -0
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +8 -4
- package/src/modules/workspace/__tests__/workspace.service.test.ts +50 -8
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +44 -1
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +409 -12
- package/src/modules/workspace/agent-download.builder.ts +308 -0
- package/src/modules/workspace/agent-download.routes.ts +183 -0
- package/src/modules/workspace/agent-download.store.ts +533 -0
- package/src/modules/workspace/agent-upload.store.ts +23 -7
- package/src/modules/workspace/file-readers/raw-mime.ts +40 -0
- package/src/modules/workspace/file-readers/text-reader.ts +5 -8
- package/src/modules/workspace/first-run.ts +222 -0
- package/src/modules/workspace/move-links.ts +237 -0
- package/src/modules/workspace/startup/steps/seed-tree.ts +3 -12
- package/src/modules/workspace/workspace.routes.ts +64 -22
- package/src/modules/workspace/workspace.service.ts +202 -25
- package/src/modules/workspace/workspace.tools.ts +492 -63
- package/src/modules/write-access/__tests__/write-access.test.ts +13 -0
- package/src/modules/write-access/write-access.ts +9 -0
- package/src/shared/domain-errors.ts +108 -11
- package/src/shared/git.contract.ts +8 -1
- package/src/shared/utf8-text.ts +21 -0
- package/src/tenancy/__tests__/static-tenant-source.test.ts +1 -0
- package/src/tenancy/static-tenant-source.ts +10 -2
- package/starter-packs/README.md +103 -0
- package/starter-packs/engineering/KnowledgeBase/About us.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Architecture.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/How we ship.md +9 -0
- package/starter-packs/engineering/Plugins/engineering-starter/LICENSE +21 -0
- package/starter-packs/engineering/Plugins/engineering-starter/SOURCE.md +20 -0
- package/starter-packs/engineering/Plugins/engineering-starter/access.md +19 -0
- package/starter-packs/engineering/Plugins/engineering-starter/plugin.json +10 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/SKILL.md +171 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2a-startup-diagnostic.md +157 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2b-builder-brainstorm.md +39 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-ceo-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-eng-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/SKILL.md +80 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/checklist.md +184 -0
- package/starter-packs/engineering/pack.yaml +11 -0
- package/starter-packs/general/KnowledgeBase/About us.md +9 -0
- package/starter-packs/general/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/general/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/general/Plugins/general-starter/LICENSE +202 -0
- package/starter-packs/general/Plugins/general-starter/SOURCE.md +10 -0
- package/starter-packs/general/Plugins/general-starter/access.md +19 -0
- package/starter-packs/general/Plugins/general-starter/plugin.json +10 -0
- package/starter-packs/general/Plugins/general-starter/skills/memory-management/SKILL.md +332 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-start/SKILL.md +163 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-update/SKILL.md +167 -0
- package/starter-packs/general/Plugins/general-starter/skills/task-management/SKILL.md +83 -0
- package/starter-packs/general/pack.yaml +10 -0
- package/starter-packs/operations/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/operations/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Processes.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Tools we use.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Vendors.md +9 -0
- package/starter-packs/operations/Plugins/operations-starter/LICENSE +202 -0
- package/starter-packs/operations/Plugins/operations-starter/SOURCE.md +10 -0
- package/starter-packs/operations/Plugins/operations-starter/access.md +19 -0
- package/starter-packs/operations/Plugins/operations-starter/plugin.json +10 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/capacity-plan/SKILL.md +111 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/change-request/SKILL.md +119 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/compliance-tracking/SKILL.md +44 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-doc/SKILL.md +80 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-optimization/SKILL.md +39 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/risk-assessment/SKILL.md +40 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/runbook/SKILL.md +85 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/status-report/SKILL.md +75 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/vendor-review/SKILL.md +102 -0
- package/starter-packs/operations/pack.yaml +12 -0
- package/starter-packs/sales/KnowledgeBase/About us.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Customers.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/sales/KnowledgeBase/How we sell.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Objections.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Pricing.md +9 -0
- package/starter-packs/sales/Plugins/sales-starter/LICENSE +202 -0
- package/starter-packs/sales/Plugins/sales-starter/SOURCE.md +10 -0
- package/starter-packs/sales/Plugins/sales-starter/access.md +19 -0
- package/starter-packs/sales/Plugins/sales-starter/plugin.json +10 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-context/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-plan/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-research/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-tiering/SKILL.md +101 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-prep/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-summary/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/close-plan/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/competitive-intelligence/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/create-an-asset/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/crm-hygiene-check/SKILL.md +103 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-health/SKILL.md +108 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-voice/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/daily-briefing/SKILL.md +61 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-advance-gap/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-review/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-signals/SKILL.md +96 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-slip-scenario/SKILL.md +104 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/draft-outreach/SKILL.md +116 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/end-of-day/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/expansion-whitespace/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/forecast/SKILL.md +100 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/handle-objection/SKILL.md +93 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/inbox-sweep/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/lead-triage/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/log-activity/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/pipeline-review/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/renewal-radar/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/rep-context/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/route-lead/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/sales-setup/SKILL.md +200 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/schedule-meeting/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/stakeholder-map/SKILL.md +107 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/team-pipeline/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/update-opportunity/SKILL.md +112 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/weekly-wrap/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/win-loss-review/SKILL.md +88 -0
- package/starter-packs/sales/pack.yaml +13 -0
- package/starter-packs/support/KnowledgeBase/Escalations.md +9 -0
- package/starter-packs/support/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/support/KnowledgeBase/Known issues.md +9 -0
- package/starter-packs/support/KnowledgeBase/Products.md +9 -0
- package/starter-packs/support/KnowledgeBase/Tone of voice.md +9 -0
- package/starter-packs/support/Plugins/support-starter/LICENSE +202 -0
- package/starter-packs/support/Plugins/support-starter/SOURCE.md +10 -0
- package/starter-packs/support/Plugins/support-starter/access.md +19 -0
- package/starter-packs/support/Plugins/support-starter/plugin.json +10 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-escalation/SKILL.md +246 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-research/SKILL.md +250 -0
- package/starter-packs/support/Plugins/support-starter/skills/draft-response/SKILL.md +416 -0
- package/starter-packs/support/Plugins/support-starter/skills/kb-article/SKILL.md +350 -0
- package/starter-packs/support/Plugins/support-starter/skills/ticket-triage/SKILL.md +273 -0
- package/starter-packs/support/pack.yaml +12 -0
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Database } from '../database/connection.js';
|
|
2
2
|
import type { AuthUser } from '@bevel-software/platform-shared';
|
|
3
3
|
import { type IAccountAdmission } from './account-admission.js';
|
|
4
|
+
import type { IAccountSwitchGuard } from './account-switch-guard.js';
|
|
5
|
+
/** What an admin is told when they try to switch an owner off. */
|
|
6
|
+
export declare const OWNER_SWITCH_OFF_REFUSAL = "The owner can't be switched off.";
|
|
7
|
+
/** What an admin is told when deleting an owner's account would leave them no way to sign in. */
|
|
8
|
+
export declare const OWNER_DELETE_REFUSAL = "The owner's account can't be deleted: they would have no way to sign in.";
|
|
4
9
|
/**
|
|
5
10
|
* What this service needs from the deployment's configuration, and nothing
|
|
6
11
|
* more. Narrowed from `CoreConfig` so the caller can supply a value that is
|
|
@@ -39,7 +44,7 @@ export interface SsoLoginOptions {
|
|
|
39
44
|
*/
|
|
40
45
|
admittedByProvider?: boolean;
|
|
41
46
|
}
|
|
42
|
-
export declare class AuthService {
|
|
47
|
+
export declare class AuthService implements IAccountSwitchGuard {
|
|
43
48
|
private readonly db;
|
|
44
49
|
private readonly config;
|
|
45
50
|
/**
|
|
@@ -115,7 +120,10 @@ export declare class AuthService {
|
|
|
115
120
|
* Password sign-in refuses it until a password is set. For an address that
|
|
116
121
|
* already has an account it changes nothing but an explicitly given name.
|
|
117
122
|
*
|
|
118
|
-
* WITH a password,
|
|
123
|
+
* WITH a password, this refuses two kinds of target. The accounts the
|
|
124
|
+
* platform runs its own work as are refused first
|
|
125
|
+
* ({@link assertNotSystemAccount}): a password would make them able to sign
|
|
126
|
+
* in. The deployment admin is refused for the reason
|
|
119
127
|
* {@link changePassword} refuses it: that account's password is the
|
|
120
128
|
* environment's, so a stored hash would not replace it but ADD a second
|
|
121
129
|
* credential — one that keeps signing in after `ADMIN_PASSWORD` is rotated,
|
|
@@ -124,6 +132,40 @@ export declare class AuthService {
|
|
|
124
132
|
* with an arbitrary email.
|
|
125
133
|
*/
|
|
126
134
|
createAccount(email: string, name: string | undefined, password?: string): Promise<AuthUser>;
|
|
135
|
+
/**
|
|
136
|
+
* An account for single sign-on, or for a password somebody sets later:
|
|
137
|
+
* created without one, and for an address that already has an account,
|
|
138
|
+
* nothing changes but an explicitly given name. `created` says which
|
|
139
|
+
* happened, from the one statement that did it (see {@link CREATED}): an
|
|
140
|
+
* invite is only an invite when the account is new.
|
|
141
|
+
*/
|
|
142
|
+
createAccountWithoutPassword(email: string, name: string | undefined): Promise<{
|
|
143
|
+
user: AuthUser;
|
|
144
|
+
created: boolean;
|
|
145
|
+
}>;
|
|
146
|
+
/**
|
|
147
|
+
* An invite's starting password: create the account with it, or give it to
|
|
148
|
+
* an existing account that is switched on and has no password yet — never
|
|
149
|
+
* to one that has a password of its own or is switched off. The test and
|
|
150
|
+
* the write are ONE statement (the upsert's `setWhere`), so a password the
|
|
151
|
+
* person sets between the admin reading the account list and sending the
|
|
152
|
+
* invite is never overwritten; `passwordSet` says which happened, and
|
|
153
|
+
* `created` whether the account is new (see {@link CREATED}).
|
|
154
|
+
*
|
|
155
|
+
* The deployment admin has its own password (the environment's): it is
|
|
156
|
+
* left as it is, as {@link createAccount} would refuse to store one. The
|
|
157
|
+
* accounts the platform runs its own work as are refused outright: a
|
|
158
|
+
* password would make them able to sign in.
|
|
159
|
+
*
|
|
160
|
+
* When nothing was written, `deactivated` tells a switched-off account
|
|
161
|
+
* apart from one that has a password of its own.
|
|
162
|
+
*/
|
|
163
|
+
createAccountWithStartingPassword(email: string, password: string): Promise<{
|
|
164
|
+
user: AuthUser;
|
|
165
|
+
created: boolean;
|
|
166
|
+
passwordSet: boolean;
|
|
167
|
+
deactivated: boolean;
|
|
168
|
+
}>;
|
|
127
169
|
/**
|
|
128
170
|
* Is `email` the deployment admin — the account whose password is set in
|
|
129
171
|
* the deployment environment (`ADMIN_EMAIL` while `ADMIN_PASSWORD` is set)
|
|
@@ -139,6 +181,39 @@ export declare class AuthService {
|
|
|
139
181
|
* client is TOLD does follow the switch: {@link reportsAsEnvAdmin}.
|
|
140
182
|
*/
|
|
141
183
|
private isEnvAdminEmail;
|
|
184
|
+
/**
|
|
185
|
+
* Is `email` an owner of this deployment — an address it is configured
|
|
186
|
+
* with as its owner (`ADMIN_EMAIL`; on Hexis by Bevel cloud, whoever
|
|
187
|
+
* created the workspace)? Unlike {@link isEnvAdminEmail} this asks nothing
|
|
188
|
+
* of `ADMIN_PASSWORD`: the cloud never sets one, and its owner must be
|
|
189
|
+
* protected all the same. Behind {@link deactivate} (which always refuses
|
|
190
|
+
* an owner) and {@link assertDeletable} (which refuses one who could not
|
|
191
|
+
* sign back in). Canonicalises both sides, so `email` may be raw.
|
|
192
|
+
*/
|
|
193
|
+
isOwnerEmail(email: string): boolean;
|
|
194
|
+
/**
|
|
195
|
+
* An owner is never switched off, but an earlier version did switch off an
|
|
196
|
+
* owner without a server-held password, and no admin can undo that for a
|
|
197
|
+
* workspace's only owner. So wherever an owner's row is read to let them
|
|
198
|
+
* in, a switch-off left on it is cleared — on the row itself, so that the
|
|
199
|
+
* credentials that read the column directly (API keys, MCP tokens) are
|
|
200
|
+
* honoured again too. Returns `row` as it now stands; anyone else's row
|
|
201
|
+
* unchanged.
|
|
202
|
+
*/
|
|
203
|
+
private clearOwnerSwitchOff;
|
|
204
|
+
/**
|
|
205
|
+
* The configured owner addresses, canonical, blanks dropped. One today
|
|
206
|
+
* (`ADMIN_EMAIL` is a single string); a list so that every owner is
|
|
207
|
+
* treated alike should the configuration ever carry more.
|
|
208
|
+
*/
|
|
209
|
+
private ownerEmails;
|
|
210
|
+
/**
|
|
211
|
+
* Refuse to delete `email`'s account when it is an owner who could not
|
|
212
|
+
* sign back in afterwards: only an owner whose password the server holds,
|
|
213
|
+
* with password sign-in on (exactly {@link reportsAsEnvAdmin}), still has a
|
|
214
|
+
* way in once the row is gone. Anyone else's account passes.
|
|
215
|
+
*/
|
|
216
|
+
assertDeletable(email: string): void;
|
|
142
217
|
/**
|
|
143
218
|
* Whether a client is told `email` is the deployment admin — behind
|
|
144
219
|
* {@link toClientUser} and {@link listAccounts}. The identity is
|
|
@@ -189,12 +264,18 @@ export declare class AuthService {
|
|
|
189
264
|
name: string;
|
|
190
265
|
hasPassword: boolean;
|
|
191
266
|
isEnvAdmin: boolean;
|
|
267
|
+
/** One of the deployment's owners (`ADMIN_EMAIL`), with or without a server-held password. */
|
|
268
|
+
isOwner: boolean;
|
|
269
|
+
/** An owner whose account may be deleted, since they can sign back in (see {@link assertDeletable}); false for everyone else. */
|
|
270
|
+
ownerCanBeDeleted: boolean;
|
|
192
271
|
/** When an admin switched the account off; null while it is on. */
|
|
193
272
|
deactivatedAt: Date | null;
|
|
194
273
|
/** One of the accounts the platform runs its own work as: never switched off, nobody signs in with it. */
|
|
195
274
|
isSystem: boolean;
|
|
196
275
|
createdAt: Date;
|
|
197
276
|
}>>;
|
|
277
|
+
/** Every password write calls this first: the system accounts never get one. */
|
|
278
|
+
private assertNotSystemAccount;
|
|
198
279
|
private assertPasswordPolicy;
|
|
199
280
|
private signToken;
|
|
200
281
|
/**
|
|
@@ -245,18 +326,32 @@ export declare class AuthService {
|
|
|
245
326
|
* environment password signs it in regardless (see `loginWithPassword`),
|
|
246
327
|
* and a session that sign-in mints must then be usable too, or the way
|
|
247
328
|
* back into a deployment would lead nowhere. {@link deactivate} refuses
|
|
248
|
-
* the account, but a host may write the column itself.
|
|
329
|
+
* the account, but a host may write the column itself. Any owner's
|
|
330
|
+
* leftover switch-off is cleared here, as at sign-in
|
|
331
|
+
* ({@link clearOwnerSwitchOff}).
|
|
249
332
|
*/
|
|
250
333
|
isActive(userId: string): Promise<boolean>;
|
|
334
|
+
/**
|
|
335
|
+
* Run `write` only while `email` is not switched off, and keep it so until
|
|
336
|
+
* `write` has finished: a transaction takes the address's switch lock (see
|
|
337
|
+
* account-switch-lock), reads the account and stays open across `write`.
|
|
338
|
+
* Every write that switches an account off, or creates one switched off,
|
|
339
|
+
* takes the same lock first, so it either committed before the read (and
|
|
340
|
+
* `write` is refused) or waits until `write` is done. An address with no
|
|
341
|
+
* account yet counts as switched on. A switched-off account throws a 409
|
|
342
|
+
* `deactivated` and `write` never runs.
|
|
343
|
+
*/
|
|
344
|
+
whileSwitchedOn(email: string, write: () => Promise<void>): Promise<void>;
|
|
251
345
|
/**
|
|
252
346
|
* Switch an account off. Its row, history and place in roles and groups
|
|
253
347
|
* stay; every credential it holds stops being honoured (see
|
|
254
348
|
* {@link isActive}). Idempotent: an account already off keeps the moment
|
|
255
349
|
* it was first switched off.
|
|
256
350
|
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
351
|
+
* An owner is refused, on every deployment, whether or not the server
|
|
352
|
+
* holds a password for them: switching the owner off locks them out of
|
|
353
|
+
* their own workspace (and, where `ADMIN_PASSWORD` is set, closes the
|
|
354
|
+
* way back into a deployment whose every other admin is gone). So are the machine accounts
|
|
260
355
|
* core runs its own work as. Refusing an admin's own account is the
|
|
261
356
|
* route's concern, since it knows who is asking.
|
|
262
357
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAOL,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import jwt from 'jsonwebtoken';
|
|
2
|
-
import { and, eq, isNotNull, isNull } from 'drizzle-orm';
|
|
2
|
+
import { and, eq, getTableColumns, isNotNull, isNull, sql } from 'drizzle-orm';
|
|
3
3
|
import { users } from '../database/schema.js';
|
|
4
4
|
import { canonicalEmail, hashEmail } from '../../shared/email-identity.js';
|
|
5
5
|
import { AccountAdmissionRefusedError, AccountChangeRefusedError, AccountDeactivatedError, AuthBackendError, admitEveryone, } from './account-admission.js';
|
|
6
6
|
import { hashPassword, verifyPassword, timingSafeStringEqual, MIN_PASSWORD_LENGTH, } from './password-hash.js';
|
|
7
7
|
import { RECOVERY_BOT_EMAIL } from '../workflow/recovery-bot.js';
|
|
8
8
|
import { DIRECTORY_SYNC_BOT_EMAIL } from '../access/directory-sync-bot.js';
|
|
9
|
+
import { takeAccountSwitchLock } from './account-switch-lock.js';
|
|
10
|
+
import { WorkflowDomainError } from '../../shared/domain-errors.js';
|
|
9
11
|
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
10
12
|
/**
|
|
11
13
|
* How long {@link AuthService.isActive} trusts what it last read. Every
|
|
@@ -16,6 +18,20 @@ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
16
18
|
const ACTIVE_CACHE_MS = 30_000;
|
|
17
19
|
/** The accounts core runs its own work as. Nobody signs in with them, and nobody switches them off. */
|
|
18
20
|
const SYSTEM_ACCOUNT_EMAILS = [RECOVERY_BOT_EMAIL, DIRECTORY_SYNC_BOT_EMAIL];
|
|
21
|
+
/**
|
|
22
|
+
* Whether an upsert's returned row was INSERTED rather than updated, from the
|
|
23
|
+
* one statement that did it: Postgres leaves `xmax` at zero on a row the
|
|
24
|
+
* statement inserted and sets it on one its `ON CONFLICT DO UPDATE` touched.
|
|
25
|
+
* So "is this account new" is answered by the write itself, never by a read
|
|
26
|
+
* before it that a concurrent create could make a lie of.
|
|
27
|
+
*/
|
|
28
|
+
const CREATED = sql `(xmax = 0)`;
|
|
29
|
+
/**
|
|
30
|
+
* What every password write refuses for the accounts the platform runs its own
|
|
31
|
+
* work as: a stored hash would make them able to sign in. One guard,
|
|
32
|
+
* {@link AuthService.assertNotSystemAccount}, on every path that writes one.
|
|
33
|
+
*/
|
|
34
|
+
const SYSTEM_ACCOUNT_PASSWORD_REFUSAL = 'System accounts cannot be given a password';
|
|
19
35
|
/**
|
|
20
36
|
* What every caller is told when it tries to give the deployment admin a
|
|
21
37
|
* stored password — the Account page's own change and an admin's "Set
|
|
@@ -23,6 +39,10 @@ const SYSTEM_ACCOUNT_EMAILS = [RECOVERY_BOT_EMAIL, DIRECTORY_SYNC_BOT_EMAIL];
|
|
|
23
39
|
* two surfaces cannot end up explaining the same rule differently.
|
|
24
40
|
*/
|
|
25
41
|
const ENV_ADMIN_PASSWORD_REFUSAL = "This account's password is set in the deployment environment and cannot be changed here";
|
|
42
|
+
/** What an admin is told when they try to switch an owner off. */
|
|
43
|
+
export const OWNER_SWITCH_OFF_REFUSAL = "The owner can't be switched off.";
|
|
44
|
+
/** What an admin is told when deleting an owner's account would leave them no way to sign in. */
|
|
45
|
+
export const OWNER_DELETE_REFUSAL = "The owner's account can't be deleted: they would have no way to sign in.";
|
|
26
46
|
/**
|
|
27
47
|
* Decoy hash verified when the email is unknown or has no password set, so
|
|
28
48
|
* those paths cost the same scrypt work as a real wrong-password attempt —
|
|
@@ -87,16 +107,20 @@ export class AuthService {
|
|
|
87
107
|
// is put on file, switched off, for an admin to switch on — and is still
|
|
88
108
|
// refused now, with the port's words. Only for single sign-on, the one
|
|
89
109
|
// provisioning nobody asked for (see `AccountAdmissionVerdict`).
|
|
110
|
+
// Created switched off, so under the switch lock (see account-switch-lock).
|
|
90
111
|
if (verdict.waitForAdmin && reason === 'sso') {
|
|
91
|
-
await this.db
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
await this.db.transaction(async (tx) => {
|
|
113
|
+
await takeAccountSwitchLock(tx, normalizedEmail);
|
|
114
|
+
await tx
|
|
115
|
+
.insert(users)
|
|
116
|
+
.values({
|
|
117
|
+
email: normalizedEmail,
|
|
118
|
+
emailBidx: normalizedEmail,
|
|
119
|
+
name: normalizedEmail.split('@')[0] || normalizedEmail,
|
|
120
|
+
deactivatedAt: new Date(),
|
|
121
|
+
})
|
|
122
|
+
.onConflictDoNothing({ target: users.emailBidx });
|
|
123
|
+
});
|
|
100
124
|
throw new AccountAdmissionRefusedError(verdict.message, { waitingForAdmin: true });
|
|
101
125
|
}
|
|
102
126
|
throw new AccountAdmissionRefusedError(verdict.message);
|
|
@@ -165,10 +189,12 @@ export class AuthService {
|
|
|
165
189
|
}
|
|
166
190
|
// Never refused for being switched off: the environment password is
|
|
167
191
|
// the deployment's way back in, and {@link deactivate} refuses this
|
|
168
|
-
// account for that reason.
|
|
192
|
+
// account for that reason. A switch-off left on the row by an earlier
|
|
193
|
+
// version is cleared here too, so the credentials that read the column
|
|
194
|
+
// directly come back with this sign-in ({@link clearOwnerSwitchOff}).
|
|
169
195
|
const defaultName = normalizedEmail.split('@')[0] || normalizedEmail;
|
|
170
196
|
await this.assertAdmitted(normalizedEmail, 'bootstrap');
|
|
171
|
-
const admin = await this.upsertUserByEmail(normalizedEmail, defaultName);
|
|
197
|
+
const admin = await this.clearOwnerSwitchOff(await this.upsertUserByEmail(normalizedEmail, defaultName));
|
|
172
198
|
return { token: this.signToken(admin.id, admin.email), user: this.toClientUser(admin) };
|
|
173
199
|
}
|
|
174
200
|
// Always run one scrypt verification — against the stored hash when there
|
|
@@ -182,9 +208,10 @@ export class AuthService {
|
|
|
182
208
|
}
|
|
183
209
|
// Only now, with the password proven: telling a stranger that an address
|
|
184
210
|
// is switched off would tell them it has an account.
|
|
185
|
-
|
|
211
|
+
const signedIn = await this.clearOwnerSwitchOff(user);
|
|
212
|
+
if (signedIn.deactivatedAt)
|
|
186
213
|
throw new AccountDeactivatedError();
|
|
187
|
-
return { token: this.signToken(
|
|
214
|
+
return { token: this.signToken(signedIn.id, signedIn.email), user: this.toClientUser(signedIn) };
|
|
188
215
|
}
|
|
189
216
|
/**
|
|
190
217
|
* Establish a session for a user whose identity an SSO provider (the
|
|
@@ -210,7 +237,7 @@ export class AuthService {
|
|
|
210
237
|
this.assertAllowedDomain(normalizedEmail);
|
|
211
238
|
await this.assertAdmitted(normalizedEmail, 'sso');
|
|
212
239
|
const displayName = (name ?? '').trim() || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
213
|
-
const user = await this.upsertUserByEmail(normalizedEmail, displayName);
|
|
240
|
+
const user = await this.clearOwnerSwitchOff(await this.upsertUserByEmail(normalizedEmail, displayName));
|
|
214
241
|
if (user.deactivatedAt)
|
|
215
242
|
throw new AccountDeactivatedError();
|
|
216
243
|
return { token: this.signToken(user.id, user.email), user: this.toClientUser(user) };
|
|
@@ -227,7 +254,10 @@ export class AuthService {
|
|
|
227
254
|
* Password sign-in refuses it until a password is set. For an address that
|
|
228
255
|
* already has an account it changes nothing but an explicitly given name.
|
|
229
256
|
*
|
|
230
|
-
* WITH a password,
|
|
257
|
+
* WITH a password, this refuses two kinds of target. The accounts the
|
|
258
|
+
* platform runs its own work as are refused first
|
|
259
|
+
* ({@link assertNotSystemAccount}): a password would make them able to sign
|
|
260
|
+
* in. The deployment admin is refused for the reason
|
|
231
261
|
* {@link changePassword} refuses it: that account's password is the
|
|
232
262
|
* environment's, so a stored hash would not replace it but ADD a second
|
|
233
263
|
* credential — one that keeps signing in after `ADMIN_PASSWORD` is rotated,
|
|
@@ -242,20 +272,11 @@ export class AuthService {
|
|
|
242
272
|
}
|
|
243
273
|
const suppliedName = (name ?? '').trim();
|
|
244
274
|
const displayName = suppliedName || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
245
|
-
if (!password)
|
|
246
|
-
await this.
|
|
247
|
-
const [created] = await this.db
|
|
248
|
-
.insert(users)
|
|
249
|
-
.values({ email: normalizedEmail, emailBidx: normalizedEmail, name: displayName })
|
|
250
|
-
.onConflictDoUpdate({
|
|
251
|
-
target: users.emailBidx,
|
|
252
|
-
set: suppliedName ? { name: suppliedName, updatedAt: new Date() } : { updatedAt: new Date() },
|
|
253
|
-
})
|
|
254
|
-
.returning();
|
|
255
|
-
return this.toClientUser(created);
|
|
256
|
-
}
|
|
275
|
+
if (!password)
|
|
276
|
+
return (await this.createAccountWithoutPassword(email, name)).user;
|
|
257
277
|
// Before the policy check, so the refusal names the real reason rather
|
|
258
278
|
// than sending the admin off to pick a longer password first.
|
|
279
|
+
this.assertNotSystemAccount(normalizedEmail);
|
|
259
280
|
if (this.isEnvAdminEmail(normalizedEmail)) {
|
|
260
281
|
throw new Error(ENV_ADMIN_PASSWORD_REFUSAL);
|
|
261
282
|
}
|
|
@@ -278,6 +299,82 @@ export class AuthService {
|
|
|
278
299
|
.returning();
|
|
279
300
|
return this.toClientUser(row);
|
|
280
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* An account for single sign-on, or for a password somebody sets later:
|
|
304
|
+
* created without one, and for an address that already has an account,
|
|
305
|
+
* nothing changes but an explicitly given name. `created` says which
|
|
306
|
+
* happened, from the one statement that did it (see {@link CREATED}): an
|
|
307
|
+
* invite is only an invite when the account is new.
|
|
308
|
+
*/
|
|
309
|
+
async createAccountWithoutPassword(email, name) {
|
|
310
|
+
const normalizedEmail = canonicalEmail(email ?? '');
|
|
311
|
+
if (!EMAIL_REGEX.test(normalizedEmail)) {
|
|
312
|
+
throw new Error('Invalid email');
|
|
313
|
+
}
|
|
314
|
+
const suppliedName = (name ?? '').trim();
|
|
315
|
+
const displayName = suppliedName || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
316
|
+
await this.assertAdmitted(normalizedEmail, 'admin-create');
|
|
317
|
+
const [row] = await this.db
|
|
318
|
+
.insert(users)
|
|
319
|
+
.values({ email: normalizedEmail, emailBidx: normalizedEmail, name: displayName })
|
|
320
|
+
.onConflictDoUpdate({
|
|
321
|
+
target: users.emailBidx,
|
|
322
|
+
set: suppliedName ? { name: suppliedName, updatedAt: new Date() } : { updatedAt: new Date() },
|
|
323
|
+
})
|
|
324
|
+
.returning({ ...getTableColumns(users), created: CREATED });
|
|
325
|
+
return { user: this.toClientUser(row), created: row.created === true };
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* An invite's starting password: create the account with it, or give it to
|
|
329
|
+
* an existing account that is switched on and has no password yet — never
|
|
330
|
+
* to one that has a password of its own or is switched off. The test and
|
|
331
|
+
* the write are ONE statement (the upsert's `setWhere`), so a password the
|
|
332
|
+
* person sets between the admin reading the account list and sending the
|
|
333
|
+
* invite is never overwritten; `passwordSet` says which happened, and
|
|
334
|
+
* `created` whether the account is new (see {@link CREATED}).
|
|
335
|
+
*
|
|
336
|
+
* The deployment admin has its own password (the environment's): it is
|
|
337
|
+
* left as it is, as {@link createAccount} would refuse to store one. The
|
|
338
|
+
* accounts the platform runs its own work as are refused outright: a
|
|
339
|
+
* password would make them able to sign in.
|
|
340
|
+
*
|
|
341
|
+
* When nothing was written, `deactivated` tells a switched-off account
|
|
342
|
+
* apart from one that has a password of its own.
|
|
343
|
+
*/
|
|
344
|
+
async createAccountWithStartingPassword(email, password) {
|
|
345
|
+
const normalizedEmail = canonicalEmail(email ?? '');
|
|
346
|
+
if (!EMAIL_REGEX.test(normalizedEmail)) {
|
|
347
|
+
throw new Error('Invalid email');
|
|
348
|
+
}
|
|
349
|
+
this.assertNotSystemAccount(normalizedEmail);
|
|
350
|
+
if (this.isEnvAdminEmail(normalizedEmail)) {
|
|
351
|
+
const [existing] = await this.db.select().from(users).where(eq(users.emailBidx, normalizedEmail)).limit(1);
|
|
352
|
+
if (existing) {
|
|
353
|
+
return { user: this.toClientUser(existing), created: false, passwordSet: false, deactivated: existing.deactivatedAt != null };
|
|
354
|
+
}
|
|
355
|
+
throw new Error(ENV_ADMIN_PASSWORD_REFUSAL);
|
|
356
|
+
}
|
|
357
|
+
this.assertPasswordPolicy(password);
|
|
358
|
+
await this.assertAdmitted(normalizedEmail, 'admin-create');
|
|
359
|
+
const passwordHash = await hashPassword(password);
|
|
360
|
+
const displayName = normalizedEmail.split('@')[0] || normalizedEmail;
|
|
361
|
+
const [row] = await this.db
|
|
362
|
+
.insert(users)
|
|
363
|
+
.values({ email: normalizedEmail, emailBidx: normalizedEmail, name: displayName, passwordHash })
|
|
364
|
+
.onConflictDoUpdate({
|
|
365
|
+
target: users.emailBidx,
|
|
366
|
+
set: { passwordHash, updatedAt: new Date() },
|
|
367
|
+
setWhere: and(isNull(users.passwordHash), isNull(users.deactivatedAt)),
|
|
368
|
+
})
|
|
369
|
+
.returning({ ...getTableColumns(users), created: CREATED });
|
|
370
|
+
if (row)
|
|
371
|
+
return { user: this.toClientUser(row), created: row.created === true, passwordSet: true, deactivated: false };
|
|
372
|
+
// The conflicting account has a password or is switched off: untouched.
|
|
373
|
+
const [existing] = await this.db.select().from(users).where(eq(users.emailBidx, normalizedEmail)).limit(1);
|
|
374
|
+
if (!existing)
|
|
375
|
+
throw new Error('Could not set the password');
|
|
376
|
+
return { user: this.toClientUser(existing), created: false, passwordSet: false, deactivated: existing.deactivatedAt != null };
|
|
377
|
+
}
|
|
281
378
|
/**
|
|
282
379
|
* Is `email` the deployment admin — the account whose password is set in
|
|
283
380
|
* the deployment environment (`ADMIN_EMAIL` while `ADMIN_PASSWORD` is set)
|
|
@@ -297,6 +394,59 @@ export class AuthService {
|
|
|
297
394
|
this.config.adminPassword.length > 0 &&
|
|
298
395
|
email === this.config.adminEmail);
|
|
299
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Is `email` an owner of this deployment — an address it is configured
|
|
399
|
+
* with as its owner (`ADMIN_EMAIL`; on Hexis by Bevel cloud, whoever
|
|
400
|
+
* created the workspace)? Unlike {@link isEnvAdminEmail} this asks nothing
|
|
401
|
+
* of `ADMIN_PASSWORD`: the cloud never sets one, and its owner must be
|
|
402
|
+
* protected all the same. Behind {@link deactivate} (which always refuses
|
|
403
|
+
* an owner) and {@link assertDeletable} (which refuses one who could not
|
|
404
|
+
* sign back in). Canonicalises both sides, so `email` may be raw.
|
|
405
|
+
*/
|
|
406
|
+
isOwnerEmail(email) {
|
|
407
|
+
// Not trusted to be text: the accounts API passes a request body's value
|
|
408
|
+
// in before validating it, and a non-string is simply not an owner.
|
|
409
|
+
const candidate = typeof email === 'string' ? canonicalEmail(email) : '';
|
|
410
|
+
return candidate.length > 0 && this.ownerEmails().includes(candidate);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* An owner is never switched off, but an earlier version did switch off an
|
|
414
|
+
* owner without a server-held password, and no admin can undo that for a
|
|
415
|
+
* workspace's only owner. So wherever an owner's row is read to let them
|
|
416
|
+
* in, a switch-off left on it is cleared — on the row itself, so that the
|
|
417
|
+
* credentials that read the column directly (API keys, MCP tokens) are
|
|
418
|
+
* honoured again too. Returns `row` as it now stands; anyone else's row
|
|
419
|
+
* unchanged.
|
|
420
|
+
*/
|
|
421
|
+
async clearOwnerSwitchOff(row) {
|
|
422
|
+
if (!row.deactivatedAt || !this.isOwnerEmail(row.email))
|
|
423
|
+
return row;
|
|
424
|
+
await this.db
|
|
425
|
+
.update(users)
|
|
426
|
+
.set({ deactivatedAt: null, updatedAt: new Date() })
|
|
427
|
+
.where(and(eq(users.id, row.id), isNotNull(users.deactivatedAt)));
|
|
428
|
+
this.activeCache.delete(row.id);
|
|
429
|
+
return { ...row, deactivatedAt: null };
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* The configured owner addresses, canonical, blanks dropped. One today
|
|
433
|
+
* (`ADMIN_EMAIL` is a single string); a list so that every owner is
|
|
434
|
+
* treated alike should the configuration ever carry more.
|
|
435
|
+
*/
|
|
436
|
+
ownerEmails() {
|
|
437
|
+
return [this.config.adminEmail].map((e) => canonicalEmail(e ?? '')).filter((e) => e.length > 0);
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Refuse to delete `email`'s account when it is an owner who could not
|
|
441
|
+
* sign back in afterwards: only an owner whose password the server holds,
|
|
442
|
+
* with password sign-in on (exactly {@link reportsAsEnvAdmin}), still has a
|
|
443
|
+
* way in once the row is gone. Anyone else's account passes.
|
|
444
|
+
*/
|
|
445
|
+
assertDeletable(email) {
|
|
446
|
+
if (this.isOwnerEmail(email) && !this.reportsAsEnvAdmin(canonicalEmail(email ?? ''))) {
|
|
447
|
+
throw new AccountChangeRefusedError(OWNER_DELETE_REFUSAL);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
300
450
|
/**
|
|
301
451
|
* Whether a client is told `email` is the deployment admin — behind
|
|
302
452
|
* {@link toClientUser} and {@link listAccounts}. The identity is
|
|
@@ -343,6 +493,7 @@ export class AuthService {
|
|
|
343
493
|
throw new Error('User not found');
|
|
344
494
|
// Before the policy check, so the deployment admin is told the real reason
|
|
345
495
|
// rather than being sent to fix a password that would be refused anyway.
|
|
496
|
+
this.assertNotSystemAccount(user.email);
|
|
346
497
|
if (this.isEnvAdminEmail(user.email)) {
|
|
347
498
|
throw new Error(ENV_ADMIN_PASSWORD_REFUSAL);
|
|
348
499
|
}
|
|
@@ -370,18 +521,30 @@ export class AuthService {
|
|
|
370
521
|
// would sort by IV noise. The table is one row per team member.
|
|
371
522
|
const rows = await this.db.select().from(users);
|
|
372
523
|
return rows
|
|
373
|
-
.map((row) =>
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
524
|
+
.map((row) => {
|
|
525
|
+
const isEnvAdmin = this.reportsAsEnvAdmin(row.email);
|
|
526
|
+
const isOwner = this.isOwnerEmail(row.email);
|
|
527
|
+
return {
|
|
528
|
+
id: row.id,
|
|
529
|
+
email: row.email,
|
|
530
|
+
name: row.name,
|
|
531
|
+
hasPassword: row.passwordHash != null,
|
|
532
|
+
isEnvAdmin,
|
|
533
|
+
isOwner,
|
|
534
|
+
ownerCanBeDeleted: isOwner && isEnvAdmin,
|
|
535
|
+
deactivatedAt: row.deactivatedAt,
|
|
536
|
+
isSystem: SYSTEM_ACCOUNT_EMAILS.includes(row.email),
|
|
537
|
+
createdAt: row.createdAt,
|
|
538
|
+
};
|
|
539
|
+
})
|
|
383
540
|
.sort((a, b) => a.email.localeCompare(b.email));
|
|
384
541
|
}
|
|
542
|
+
/** Every password write calls this first: the system accounts never get one. */
|
|
543
|
+
assertNotSystemAccount(normalizedEmail) {
|
|
544
|
+
if (SYSTEM_ACCOUNT_EMAILS.includes(normalizedEmail)) {
|
|
545
|
+
throw new Error(SYSTEM_ACCOUNT_PASSWORD_REFUSAL);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
385
548
|
assertPasswordPolicy(password) {
|
|
386
549
|
if ((password ?? '').length < MIN_PASSWORD_LENGTH) {
|
|
387
550
|
throw new Error(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
|
|
@@ -404,7 +567,7 @@ export class AuthService {
|
|
|
404
567
|
return null;
|
|
405
568
|
await this.assertAdmitted(normalizedEmail, 'embed');
|
|
406
569
|
const displayName = (name ?? '').trim() || normalizedEmail.split('@')[0] || normalizedEmail;
|
|
407
|
-
const user = await this.upsertUserByEmail(normalizedEmail, displayName);
|
|
570
|
+
const user = await this.clearOwnerSwitchOff(await this.upsertUserByEmail(normalizedEmail, displayName));
|
|
408
571
|
if (user.deactivatedAt)
|
|
409
572
|
throw new AccountDeactivatedError();
|
|
410
573
|
return { id: user.id, email: user.email, name: user.name };
|
|
@@ -462,31 +625,58 @@ export class AuthService {
|
|
|
462
625
|
* environment password signs it in regardless (see `loginWithPassword`),
|
|
463
626
|
* and a session that sign-in mints must then be usable too, or the way
|
|
464
627
|
* back into a deployment would lead nowhere. {@link deactivate} refuses
|
|
465
|
-
* the account, but a host may write the column itself.
|
|
628
|
+
* the account, but a host may write the column itself. Any owner's
|
|
629
|
+
* leftover switch-off is cleared here, as at sign-in
|
|
630
|
+
* ({@link clearOwnerSwitchOff}).
|
|
466
631
|
*/
|
|
467
632
|
async isActive(userId) {
|
|
468
633
|
const now = Date.now();
|
|
469
634
|
const hit = this.activeCache.get(userId);
|
|
470
635
|
if (hit && now - hit.at < ACTIVE_CACHE_MS)
|
|
471
636
|
return hit.active;
|
|
472
|
-
const [
|
|
473
|
-
.select({ email: users.email, deactivatedAt: users.deactivatedAt })
|
|
637
|
+
const [found] = await this.db
|
|
638
|
+
.select({ id: users.id, email: users.email, deactivatedAt: users.deactivatedAt })
|
|
474
639
|
.from(users)
|
|
475
640
|
.where(eq(users.id, userId))
|
|
476
641
|
.limit(1);
|
|
642
|
+
const row = found && (await this.clearOwnerSwitchOff(found));
|
|
477
643
|
const active = row !== undefined && (!row.deactivatedAt || this.isEnvAdminEmail(row.email ?? ''));
|
|
478
644
|
this.activeCache.set(userId, { active, at: now });
|
|
479
645
|
return active;
|
|
480
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* Run `write` only while `email` is not switched off, and keep it so until
|
|
649
|
+
* `write` has finished: a transaction takes the address's switch lock (see
|
|
650
|
+
* account-switch-lock), reads the account and stays open across `write`.
|
|
651
|
+
* Every write that switches an account off, or creates one switched off,
|
|
652
|
+
* takes the same lock first, so it either committed before the read (and
|
|
653
|
+
* `write` is refused) or waits until `write` is done. An address with no
|
|
654
|
+
* account yet counts as switched on. A switched-off account throws a 409
|
|
655
|
+
* `deactivated` and `write` never runs.
|
|
656
|
+
*/
|
|
657
|
+
async whileSwitchedOn(email, write) {
|
|
658
|
+
await this.db.transaction(async (tx) => {
|
|
659
|
+
await takeAccountSwitchLock(tx, email);
|
|
660
|
+
const [account] = await tx
|
|
661
|
+
.select({ deactivatedAt: users.deactivatedAt })
|
|
662
|
+
.from(users)
|
|
663
|
+
.where(eq(users.emailBidx, canonicalEmail(email)));
|
|
664
|
+
if (account?.deactivatedAt) {
|
|
665
|
+
throw new WorkflowDomainError('This account is switched off', 409, { kind: 'deactivated' });
|
|
666
|
+
}
|
|
667
|
+
await write();
|
|
668
|
+
});
|
|
669
|
+
}
|
|
481
670
|
/**
|
|
482
671
|
* Switch an account off. Its row, history and place in roles and groups
|
|
483
672
|
* stay; every credential it holds stops being honoured (see
|
|
484
673
|
* {@link isActive}). Idempotent: an account already off keeps the moment
|
|
485
674
|
* it was first switched off.
|
|
486
675
|
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
676
|
+
* An owner is refused, on every deployment, whether or not the server
|
|
677
|
+
* holds a password for them: switching the owner off locks them out of
|
|
678
|
+
* their own workspace (and, where `ADMIN_PASSWORD` is set, closes the
|
|
679
|
+
* way back into a deployment whose every other admin is gone). So are the machine accounts
|
|
490
680
|
* core runs its own work as. Refusing an admin's own account is the
|
|
491
681
|
* route's concern, since it knows who is asking.
|
|
492
682
|
*
|
|
@@ -496,16 +686,21 @@ export class AuthService {
|
|
|
496
686
|
const [row] = await this.db.select().from(users).where(eq(users.id, userId)).limit(1);
|
|
497
687
|
if (!row)
|
|
498
688
|
return false;
|
|
499
|
-
if (this.
|
|
500
|
-
throw new AccountChangeRefusedError(
|
|
689
|
+
if (this.isOwnerEmail(row.email)) {
|
|
690
|
+
throw new AccountChangeRefusedError(OWNER_SWITCH_OFF_REFUSAL);
|
|
501
691
|
}
|
|
502
692
|
if (SYSTEM_ACCOUNT_EMAILS.includes(row.email)) {
|
|
503
693
|
throw new AccountChangeRefusedError('This account belongs to the platform itself and cannot be switched off.');
|
|
504
694
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
695
|
+
// Under the switch lock, so a promotion that checked this account was
|
|
696
|
+
// on is written before this lands (see account-switch-lock).
|
|
697
|
+
await this.db.transaction(async (tx) => {
|
|
698
|
+
await takeAccountSwitchLock(tx, row.email);
|
|
699
|
+
await tx
|
|
700
|
+
.update(users)
|
|
701
|
+
.set({ deactivatedAt: new Date(), updatedAt: new Date() })
|
|
702
|
+
.where(and(eq(users.id, userId), isNull(users.deactivatedAt)));
|
|
703
|
+
});
|
|
509
704
|
this.activeCache.delete(userId);
|
|
510
705
|
return true;
|
|
511
706
|
}
|