@bevel-software/platform-core-backend 0.35.0 → 0.36.1
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/directory-structure.md +1 -1
- package/agent-guide/onboarding.md +111 -0
- package/agent-guide/start-here.md +1 -0
- package/agent-guide/tool-manuals.md +1 -0
- package/dist/core/catalog-cache-invalidation.d.ts +2 -2
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
- package/dist/core/compose-embed.d.ts +2 -2
- package/dist/core/compose-embed.d.ts.map +1 -1
- package/dist/core/compose-embed.js +1 -1
- package/dist/core/compose-embed.js.map +1 -1
- package/dist/core/core-ports.d.ts +19 -4
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +26 -24
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +19 -13
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +68 -56
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +4 -4
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +5 -5
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +2 -2
- package/dist/core-config.js +3 -3
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.service.js +1 -1
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-declarations.d.ts +2 -2
- package/dist/modules/access/access-declarations.d.ts.map +1 -1
- package/dist/modules/access/access-declarations.js +2 -2
- package/dist/modules/access/access-declarations.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +2 -2
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +2 -2
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-requests.routes.d.ts +2 -2
- package/dist/modules/access/access-requests.routes.d.ts.map +1 -1
- package/dist/modules/access/access-requests.routes.js +5 -5
- package/dist/modules/access/access-requests.routes.js.map +1 -1
- package/dist/modules/access/access-requests.service.d.ts +2 -2
- package/dist/modules/access/access-requests.service.d.ts.map +1 -1
- package/dist/modules/access/access-requests.service.js +11 -11
- package/dist/modules/access/access-requests.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +2 -2
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +9 -9
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/admin-locked-commit.d.ts +5 -5
- package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
- package/dist/modules/access/admin-locked-commit.js +7 -7
- package/dist/modules/access/admin-locked-commit.js.map +1 -1
- package/dist/modules/access/admin-route-helpers.d.ts +1 -1
- package/dist/modules/access/admin-route-helpers.js +5 -5
- package/dist/modules/access/admin-route-helpers.js.map +1 -1
- package/dist/modules/access/change-read-gate.d.ts +1 -1
- package/dist/modules/access/change-read-gate.js +1 -1
- package/dist/modules/access/groups-admin.service.d.ts +7 -7
- package/dist/modules/access/groups-admin.service.d.ts.map +1 -1
- package/dist/modules/access/groups-admin.service.js +6 -6
- package/dist/modules/access/groups-admin.service.js.map +1 -1
- package/dist/modules/access/reference-scan.d.ts +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +7 -7
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +8 -8
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access/synced-groups-committer.d.ts +4 -4
- package/dist/modules/access/synced-groups-committer.d.ts.map +1 -1
- package/dist/modules/access/synced-groups-committer.js +7 -7
- package/dist/modules/access/synced-groups-committer.js.map +1 -1
- package/dist/modules/access/user-access-removal.service.d.ts +6 -6
- package/dist/modules/access/user-access-removal.service.d.ts.map +1 -1
- package/dist/modules/access/user-access-removal.service.js +4 -4
- package/dist/modules/access/user-access-removal.service.js.map +1 -1
- package/dist/modules/access-model/access-errors.d.ts +4 -4
- package/dist/modules/access-model/access-errors.d.ts.map +1 -1
- package/dist/modules/access-model/access-errors.js +4 -4
- package/dist/modules/access-model/access-errors.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +1 -1
- package/dist/modules/access-model/access-grammar.js +4 -4
- package/dist/modules/access-model/change-gate.d.ts +1 -1
- package/dist/modules/access-model/change-gate.js +1 -1
- package/dist/modules/access-model/roles-yaml-guard.d.ts +4 -4
- package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
- package/dist/modules/access-model/roles-yaml-guard.js +4 -4
- package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +19 -14
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
- package/dist/modules/agent-guide/agent-guide.js +42 -18
- package/dist/modules/agent-guide/agent-guide.js.map +1 -1
- package/dist/modules/agent-guide/guide-budget.d.ts +5 -2
- package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -1
- package/dist/modules/agent-guide/guide-budget.js +6 -2
- package/dist/modules/agent-guide/guide-budget.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +12 -5
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.js +13 -6
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +15 -8
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +24 -10
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +14 -6
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +51 -11
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/auth/account-erasure.service.d.ts +36 -7
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +30 -10
- package/dist/modules/auth/account-erasure.service.js.map +1 -1
- package/dist/modules/auth/account-hooks.d.ts +20 -0
- package/dist/modules/auth/account-hooks.d.ts.map +1 -1
- package/dist/modules/auth/account-hooks.js +10 -0
- package/dist/modules/auth/account-hooks.js.map +1 -1
- package/dist/modules/auth/account-switch-lock.js +2 -2
- package/dist/modules/auth/account-switch-lock.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts +7 -2
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +23 -4
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +1 -1
- package/dist/modules/auth/auth.service.js +4 -4
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/index.d.ts +1 -0
- package/dist/modules/auth/index.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +2 -2
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/diff/diff.routes.d.ts +3 -3
- package/dist/modules/diff/diff.routes.d.ts.map +1 -1
- package/dist/modules/diff/diff.routes.js +7 -7
- package/dist/modules/diff/diff.routes.js.map +1 -1
- package/dist/modules/embed/embed.routes.js +1 -1
- package/dist/modules/embed/embed.routes.js.map +1 -1
- package/dist/modules/embed/embed.service.d.ts +4 -4
- package/dist/modules/embed/embed.service.d.ts.map +1 -1
- package/dist/modules/embed/embed.service.js +18 -18
- package/dist/modules/embed/embed.service.js.map +1 -1
- package/dist/modules/kb-fs/branch-name.js +8 -8
- package/dist/modules/kb-fs/branch-name.js.map +1 -1
- package/dist/modules/kb-fs/file-change-notifier.d.ts +2 -2
- package/dist/modules/kb-fs/locking-filesystem.d.ts +9 -9
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +27 -27
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +5 -5
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-fs/tmp-ref.d.ts +2 -2
- package/dist/modules/kb-fs/tmp-ref.d.ts.map +1 -1
- package/dist/modules/kb-fs/tmp-ref.js +2 -2
- package/dist/modules/kb-fs/tmp-ref.js.map +1 -1
- package/dist/modules/kb-sync/index.d.ts +1 -1
- package/dist/modules/kb-sync/index.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +3 -3
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.d.ts +3 -3
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.js +10 -10
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +11 -4
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +75 -19
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/onboarding/onboarding.contract.d.ts +1 -1
- package/dist/modules/onboarding/onboarding.routes.js +2 -2
- package/dist/modules/onboarding/onboarding.routes.js.map +1 -1
- package/dist/modules/onboarding/starter-pack.service.d.ts +4 -4
- package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -1
- package/dist/modules/onboarding/starter-pack.service.js +4 -4
- package/dist/modules/onboarding/starter-pack.service.js.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts +2 -2
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.js +7 -7
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
- package/dist/modules/plugins/join-requests.service.d.ts +3 -3
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +5 -5
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.js +1 -1
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +2 -2
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +8 -8
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +77 -2
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +479 -53
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +2 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js +1 -0
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/managed-sign-in-erasure.d.ts +36 -0
- package/dist/modules/secrets-vault/managed-sign-in-erasure.d.ts.map +1 -0
- package/dist/modules/secrets-vault/managed-sign-in-erasure.js +37 -0
- package/dist/modules/secrets-vault/managed-sign-in-erasure.js.map +1 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +152 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +7 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +125 -10
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/connection-check.js +1 -1
- package/dist/modules/settings/connection-check.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +1 -1
- package/dist/modules/settings/git-root-folders.js +1 -1
- package/dist/modules/settings/git-root-folders.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +1 -1
- package/dist/modules/settings/setup.routes.js +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +3 -3
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +4 -4
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +2 -2
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.js +5 -5
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts +4 -4
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +2 -2
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +2 -2
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +4 -4
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -3
- package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.js +4 -4
- package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.js +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +43 -3
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +6 -0
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +51 -5
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +36 -15
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/version-control/agent-tools/change-request-read-shape.d.ts.map +1 -0
- package/dist/modules/version-control/agent-tools/change-request-read-shape.js.map +1 -0
- package/dist/modules/version-control/agent-tools/change-request-read.tools.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/agent-tools/change-request-read.tools.js +2 -2
- package/dist/modules/version-control/agent-tools/change-request-read.tools.js.map +1 -0
- package/dist/modules/version-control/agent-tools/change-request-summary.d.ts.map +1 -0
- package/dist/modules/version-control/agent-tools/change-request-summary.js.map +1 -0
- package/dist/modules/{workflow/agent-tools/workflow.tools.d.ts → version-control/agent-tools/version-control.tools.d.ts} +4 -4
- package/dist/modules/version-control/agent-tools/version-control.tools.d.ts.map +1 -0
- package/dist/modules/{workflow/agent-tools/workflow.tools.js → version-control/agent-tools/version-control.tools.js} +31 -31
- package/dist/modules/version-control/agent-tools/version-control.tools.js.map +1 -0
- package/dist/modules/{workflow → version-control}/change-request-hooks.d.ts +1 -1
- package/dist/modules/version-control/change-request-hooks.d.ts.map +1 -0
- package/dist/modules/version-control/change-request-hooks.js.map +1 -0
- package/dist/modules/version-control/change-request-review/approval-lock.d.ts.map +1 -0
- package/dist/modules/version-control/change-request-review/approval-lock.js.map +1 -0
- package/dist/modules/{workflow/review-workflow/review-workflow.interface.d.ts → version-control/change-request-review/change-request-review.interface.d.ts} +2 -2
- package/dist/modules/version-control/change-request-review/change-request-review.interface.d.ts.map +1 -0
- package/dist/modules/version-control/change-request-review/change-request-review.interface.js +2 -0
- package/dist/modules/version-control/change-request-review/change-request-review.interface.js.map +1 -0
- package/dist/modules/{workflow/review-workflow/review-workflow.service.d.ts → version-control/change-request-review/change-request-review.service.d.ts} +3 -3
- package/dist/modules/version-control/change-request-review/change-request-review.service.d.ts.map +1 -0
- package/dist/modules/{workflow/review-workflow/review-workflow.service.js → version-control/change-request-review/change-request-review.service.js} +26 -26
- package/dist/modules/version-control/change-request-review/change-request-review.service.js.map +1 -0
- package/dist/modules/{workflow → version-control}/event-bus.d.ts +8 -8
- package/dist/modules/version-control/event-bus.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/event-bus.js +4 -4
- package/dist/modules/version-control/event-bus.js.map +1 -0
- package/dist/modules/{workflow → version-control}/events-delivery.d.ts +4 -4
- package/dist/modules/version-control/events-delivery.d.ts.map +1 -0
- package/dist/modules/version-control/events-delivery.js.map +1 -0
- package/dist/modules/{workflow → version-control}/events.routes.d.ts +4 -4
- package/dist/modules/version-control/events.routes.d.ts.map +1 -0
- package/dist/modules/version-control/events.routes.js.map +1 -0
- package/dist/modules/{workflow → version-control}/file-lock.service.d.ts +2 -2
- package/dist/modules/version-control/file-lock.service.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/file-lock.service.js +6 -6
- package/dist/modules/version-control/file-lock.service.js.map +1 -0
- package/dist/modules/version-control/git/change-request-link.d.ts.map +1 -0
- package/dist/modules/version-control/git/change-request-link.js.map +1 -0
- package/dist/modules/version-control/git/git-version.d.ts.map +1 -0
- package/dist/modules/version-control/git/git-version.js.map +1 -0
- package/dist/modules/{workflow → version-control}/git/git.service.d.ts +8 -8
- package/dist/modules/version-control/git/git.service.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/git/git.service.js +53 -53
- package/dist/modules/version-control/git/git.service.js.map +1 -0
- package/dist/modules/version-control/git/merge-commit.d.ts.map +1 -0
- package/dist/modules/version-control/git/merge-commit.js.map +1 -0
- package/dist/modules/version-control/git/node-git-runner.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/git/node-git-runner.js +1 -1
- package/dist/modules/version-control/git/node-git-runner.js.map +1 -0
- package/dist/modules/{workflow → version-control}/git/pull-request.service.d.ts +5 -5
- package/dist/modules/version-control/git/pull-request.service.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/git/pull-request.service.js +10 -10
- package/dist/modules/version-control/git/pull-request.service.js.map +1 -0
- package/dist/modules/{workflow → version-control}/index.d.ts +6 -5
- package/dist/modules/version-control/index.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/index.js +3 -2
- package/dist/modules/version-control/index.js.map +1 -0
- package/dist/modules/{workflow → version-control}/pending-commits.service.d.ts +2 -2
- package/dist/modules/version-control/pending-commits.service.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/pending-commits.service.js +2 -2
- package/dist/modules/version-control/pending-commits.service.js.map +1 -0
- package/dist/modules/{workflow → version-control}/pending-commits.worker.d.ts +5 -5
- package/dist/modules/version-control/pending-commits.worker.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/pending-commits.worker.js +3 -3
- package/dist/modules/version-control/pending-commits.worker.js.map +1 -0
- package/dist/modules/{workflow → version-control}/read-on-branch.d.ts +2 -2
- package/dist/modules/version-control/read-on-branch.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/read-on-branch.js +2 -2
- package/dist/modules/version-control/read-on-branch.js.map +1 -0
- package/dist/modules/version-control/recovery-bot.d.ts.map +1 -0
- package/dist/modules/version-control/recovery-bot.js.map +1 -0
- package/dist/modules/version-control/sanitize-error.d.ts.map +1 -0
- package/dist/modules/version-control/sanitize-error.js.map +1 -0
- package/dist/modules/{workflow/workflow-hooks.d.ts → version-control/version-control-hooks.d.ts} +4 -4
- package/dist/modules/version-control/version-control-hooks.d.ts.map +1 -0
- package/dist/modules/{workflow/workflow-hooks.js → version-control/version-control-hooks.js} +3 -3
- package/dist/modules/version-control/version-control-hooks.js.map +1 -0
- package/dist/modules/{workflow/workflow.routes.d.ts → version-control/version-control.routes.d.ts} +6 -5
- package/dist/modules/version-control/version-control.routes.d.ts.map +1 -0
- package/dist/modules/{workflow/workflow.routes.js → version-control/version-control.routes.js} +61 -60
- package/dist/modules/version-control/version-control.routes.js.map +1 -0
- package/dist/modules/{workflow/workflow.service.d.ts → version-control/version-control.service.d.ts} +26 -31
- package/dist/modules/version-control/version-control.service.d.ts.map +1 -0
- package/dist/modules/{workflow/workflow.service.js → version-control/version-control.service.js} +89 -94
- package/dist/modules/version-control/version-control.service.js.map +1 -0
- package/dist/modules/workspace/agent-access.gate.d.ts +5 -5
- package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
- package/dist/modules/workspace/agent-access.gate.js +1 -1
- package/dist/modules/workspace/git-internals.middleware.d.ts +1 -1
- package/dist/modules/workspace/git-internals.middleware.js +2 -2
- package/dist/modules/workspace/kb-repository.js +1 -1
- package/dist/modules/workspace/kb-repository.js.map +1 -1
- package/dist/modules/workspace/startup/kb-git.d.ts +1 -1
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-git.js +1 -1
- package/dist/modules/workspace/startup/kb-git.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +2 -2
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +2 -2
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +2 -2
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +17 -4
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +8 -5
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/workspace-tmp.js +3 -3
- package/dist/modules/workspace/workspace-tmp.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts +4 -4
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +22 -22
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +6 -6
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +11 -11
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +2 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +15 -10
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/canonical-file-identity.d.ts +2 -2
- package/dist/shared/canonical-file-identity.js +5 -5
- package/dist/shared/canonical-file-identity.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +39 -39
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +44 -44
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +1 -1
- package/dist/shared/git.contract.js +1 -1
- package/dist/shared/http-errors.d.ts +3 -3
- package/dist/shared/http-errors.d.ts.map +1 -1
- package/dist/shared/http-errors.js +1 -1
- package/dist/shared/http-errors.js.map +1 -1
- package/dist/shared/pending-proposals.d.ts +3 -3
- package/dist/shared/pending-proposals.d.ts.map +1 -1
- package/dist/shared/pending-proposals.js +2 -2
- package/dist/shared/pending-proposals.js.map +1 -1
- package/dist/tenancy/static-tenant-source.js +1 -1
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/migrations/0012_agent_audit.sql +40 -40
- package/migrations/0013_agent_identity_and_key_soft_delete.sql +29 -29
- package/migrations/meta/0012_snapshot.json +2246 -2246
- package/migrations/meta/0013_snapshot.json +2258 -2258
- package/package.json +4 -4
- package/src/__tests__/account-admin.ts +27 -0
- package/src/__tests__/hexis-catalog.ts +4 -4
- package/src/__tests__/kb-context.ts +30 -30
- package/src/__tests__/kb-layout-config.test.ts +21 -0
- package/src/__tests__/open-change-gate.ts +1 -1
- package/src/__tests__/tool-handler.ts +15 -0
- package/src/__tests__/tool-wiring.ts +10 -4
- package/src/__tests__/workspace-service.ts +1 -1
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +3 -3
- package/src/core/__tests__/compose-embed.test.ts +1 -1
- package/src/core/__tests__/damaged-document.tools.test.ts +3 -3
- package/src/core/__tests__/erasure-slices-wired-once.test.ts +46 -0
- package/src/core/__tests__/gated-boot.test.ts +2 -2
- package/src/core/__tests__/lifecycle.test.ts +9 -9
- package/src/core/catalog-cache-invalidation.ts +2 -2
- package/src/core/compose-embed.ts +3 -3
- package/src/core/core-ports.ts +19 -4
- package/src/core/create-core-server.ts +39 -25
- package/src/core/create-core-services.ts +79 -63
- package/src/core/lifecycle.ts +9 -9
- package/src/core-config.ts +3 -3
- package/src/index.ts +5 -5
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +4 -4
- package/src/modules/access/__tests__/access-own-read-grant.test.ts +14 -14
- package/src/modules/access/__tests__/access-requests.routes.test.ts +20 -20
- package/src/modules/access/__tests__/access.routes.admins.test.ts +4 -4
- package/src/modules/access/__tests__/access.routes.batch.test.ts +4 -4
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +6 -6
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -6
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +5 -5
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +6 -6
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +3 -3
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +6 -6
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +6 -6
- package/src/modules/access/__tests__/admin-locked-commit.test.ts +6 -6
- package/src/modules/access/__tests__/admin-route-helpers.test.ts +5 -5
- package/src/modules/access/__tests__/groups-admin.service.test.ts +27 -27
- package/src/modules/access/__tests__/roles-admin.security.test.ts +14 -13
- package/src/modules/access/__tests__/roles-admin.service.test.ts +27 -27
- package/src/modules/access/__tests__/roles-capabilities.test.ts +19 -19
- package/src/modules/access/__tests__/roles.routes.test.ts +6 -6
- package/src/modules/access/__tests__/synced-groups-committer.test.ts +13 -13
- package/src/modules/access/__tests__/user-access-removal.service.test.ts +29 -29
- package/src/modules/access/access-control.service.ts +1 -1
- package/src/modules/access/access-declarations.ts +2 -2
- package/src/modules/access/access-mutation.service.ts +2 -2
- package/src/modules/access/access-requests.routes.ts +7 -7
- package/src/modules/access/access-requests.service.ts +13 -13
- package/src/modules/access/access.routes.ts +11 -11
- package/src/modules/access/admin-locked-commit.ts +11 -11
- package/src/modules/access/admin-route-helpers.ts +5 -5
- package/src/modules/access/change-read-gate.ts +1 -1
- package/src/modules/access/groups-admin.service.ts +7 -7
- package/src/modules/access/reference-scan.ts +1 -1
- package/src/modules/access/roles-admin.service.ts +9 -9
- package/src/modules/access/synced-groups-committer.ts +11 -11
- package/src/modules/access/user-access-removal.service.ts +7 -7
- package/src/modules/access-model/__tests__/access-grammar.test.ts +2 -2
- package/src/modules/access-model/access-errors.ts +4 -4
- package/src/modules/access-model/access-grammar.ts +4 -4
- package/src/modules/access-model/change-gate.ts +1 -1
- package/src/modules/access-model/roles-yaml-guard.ts +4 -4
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +19 -4
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +1 -1
- package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +2 -2
- package/src/modules/agent-guide/agent-guide.ts +48 -22
- package/src/modules/agent-guide/guide-budget.ts +6 -2
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +4 -5
- package/src/modules/agent-instructions/__tests__/compose.test.ts +1 -1
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +113 -7
- package/src/modules/agent-instructions/agent-instructions.routes.ts +12 -5
- package/src/modules/agent-instructions/compose.ts +24 -9
- package/src/modules/agent-instructions/shared-file-rules.ts +53 -11
- package/src/modules/agent-scope/__tests__/agent-surfaces.connect-as.test.ts +1 -1
- package/src/modules/audit/__tests__/agent-rest-audit.middleware.test.ts +113 -113
- package/src/modules/audit/__tests__/event-classifier.test.ts +121 -121
- package/src/modules/audit/event-classifier.ts +155 -155
- package/src/modules/auth/__tests__/account-deactivation.test.ts +1 -1
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +4 -4
- package/src/modules/auth/__tests__/account-erasure.participants.test.ts +56 -0
- package/src/modules/auth/__tests__/account-switch-lock.pg.test.ts +2 -2
- package/src/modules/auth/__tests__/account-switch-lock.test.ts +2 -2
- package/src/modules/auth/__tests__/account.routes.test.ts +73 -11
- package/src/modules/auth/__tests__/auth.service.test.ts +1 -1
- package/src/modules/auth/account-erasure.service.ts +27 -9
- package/src/modules/auth/account-hooks.ts +34 -0
- package/src/modules/auth/account-switch-lock.ts +2 -2
- package/src/modules/auth/account.routes.ts +23 -5
- package/src/modules/auth/auth.service.ts +4 -4
- package/src/modules/auth/index.ts +1 -0
- package/src/modules/database/__tests__/advisory-lock.test.ts +169 -169
- package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -32
- package/src/modules/database/core-schema.ts +2 -2
- package/src/modules/diff/__tests__/diff.routes.read-gate.test.ts +2 -2
- package/src/modules/diff/__tests__/diff.routes.rejectPathsLocked.test.ts +12 -12
- package/src/modules/diff/diff.routes.ts +8 -8
- package/src/modules/embed/__tests__/embed.service.lock.test.ts +7 -7
- package/src/modules/embed/__tests__/embed.service.test.ts +42 -42
- package/src/modules/embed/__tests__/embed.tools.test.ts +1 -1
- package/src/modules/embed/embed.routes.ts +1 -1
- package/src/modules/embed/embed.service.ts +17 -17
- package/src/modules/kb-fs/__tests__/branch-name.test.ts +2 -2
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +260 -260
- package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +34 -34
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +8 -8
- package/src/modules/kb-fs/branch-name.ts +8 -8
- package/src/modules/kb-fs/file-change-notifier.ts +2 -2
- package/src/modules/kb-fs/locking-filesystem.ts +30 -30
- package/src/modules/kb-fs/repo-path.ts +5 -5
- package/src/modules/kb-fs/tmp-ref.ts +3 -3
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +36 -36
- package/src/modules/kb-sync/index.ts +1 -1
- package/src/modules/kb-sync/kb-sync.interface.ts +3 -3
- package/src/modules/kb-sync/kb-sync.service.ts +9 -9
- package/src/modules/marketplace/__tests__/github-facade.test.ts +1 -1
- package/src/modules/marketplace/__tests__/marketplace-git.test.ts +1 -1
- package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +4 -2
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +69 -8
- package/src/modules/mcp/__tests__/mcp.service.test.ts +91 -13
- package/src/modules/mcp/__tests__/read-only-hint.pg.test.ts +194 -0
- package/src/modules/mcp/mcp.service.ts +89 -28
- package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +55 -55
- package/src/modules/onboarding/onboarding.contract.ts +1 -1
- package/src/modules/onboarding/onboarding.routes.ts +2 -2
- package/src/modules/onboarding/starter-pack.service.ts +6 -6
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +1 -1
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +1 -1
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +43 -43
- package/src/modules/plugins/__tests__/join-requests.service.test.ts +19 -19
- package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +26 -26
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +25 -25
- package/src/modules/plugins/join-request-jobs.service.ts +10 -10
- package/src/modules/plugins/join-requests.service.ts +4 -4
- package/src/modules/plugins/plugin-rename.service.ts +1 -1
- package/src/modules/plugins/plugins.routes.ts +9 -9
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.managed.test.ts +773 -0
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +28 -1
- package/src/modules/secrets-vault/__tests__/managed-sign-in.route.test.ts +351 -0
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +562 -95
- package/src/modules/secrets-vault/index.ts +7 -0
- package/src/modules/secrets-vault/managed-sign-in-erasure.ts +41 -0
- package/src/modules/secrets-vault/secrets-variable-loader.ts +134 -134
- package/src/modules/secrets-vault/secrets-vault.contract.ts +152 -0
- package/src/modules/secrets-vault/secrets-vault.routes.ts +157 -10
- package/src/modules/settings/__tests__/repository-source.test.ts +1 -1
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +1 -1
- package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +1 -1
- package/src/modules/settings/connection-check.ts +1 -1
- package/src/modules/settings/deployment-settings.service.ts +1 -1
- package/src/modules/settings/git-root-folders.ts +1 -1
- package/src/modules/settings/setup.routes.ts +1 -1
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +3 -3
- package/src/modules/skills/__tests__/branch-skills.tools.test.ts +1 -1
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +8 -8
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +18 -18
- package/src/modules/skills/__tests__/skills.service.test.ts +3 -3
- package/src/modules/skills/pending-skills.service.ts +3 -3
- package/src/modules/skills/skill-access-requests.routes.ts +7 -7
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +4 -4
- package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +3 -3
- package/src/modules/tool-helpers/__tests__/can-write.tools.test.ts +3 -3
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +4 -4
- package/src/modules/tool-helpers/__tests__/tools-surface.test.ts +3 -3
- package/src/modules/tool-helpers/__tests__/validate-token.test.ts +1 -1
- package/src/modules/tool-helpers/tool-context.ts +6 -6
- package/src/modules/tool-helpers/tool-handler.ts +2 -2
- package/src/modules/tool-helpers/tool.contract.ts +4 -4
- package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +9 -9
- package/src/modules/tool-manuals/__tests__/tool-manuals.managed-sign-in.test.ts +89 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +1 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +36 -1
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +1 -1
- package/src/modules/tool-manuals/pending-tools.service.ts +3 -3
- package/src/modules/tool-manuals/tool-delete.service.ts +1 -1
- package/src/modules/tool-manuals/tool-manuals.contract.ts +41 -3
- package/src/modules/tool-manuals/tool-manuals.service.ts +53 -5
- package/src/modules/tool-manuals/tool-manuals.tools.ts +41 -15
- package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +4 -4
- package/src/modules/tool-registry/__tests__/read-only-hints.test.ts +3 -3
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -6
- package/src/modules/{workflow → version-control}/__tests__/apply-failure.test.ts +54 -54
- package/src/modules/{workflow → version-control}/__tests__/event-bus.test.ts +27 -27
- package/src/modules/{workflow → version-control}/__tests__/events-delivery.test.ts +37 -37
- package/src/modules/{workflow → version-control}/__tests__/file-lock.service.deletion-gate.test.ts +2 -2
- package/src/modules/{workflow → version-control}/__tests__/file-lock.service.path-identity.test.ts +8 -8
- package/src/modules/{workflow → version-control}/__tests__/merge-announces-tree.test.ts +11 -11
- package/src/modules/{workflow → version-control}/__tests__/pending-commits.worker.concurrency.test.ts +9 -9
- package/src/modules/{workflow → version-control}/__tests__/pending-commits.worker.test.ts +25 -25
- package/src/modules/{workflow → version-control}/__tests__/preserve-roles-yaml.test.ts +42 -42
- package/src/modules/version-control/__tests__/public-surface.test.ts +114 -0
- package/src/modules/{workflow/__tests__/workflow.routes.folder-proposals.test.ts → version-control/__tests__/version-control.routes.folder-proposals.test.ts} +18 -18
- package/src/modules/{workflow/__tests__/workflow.routes.fork-point-file.test.ts → version-control/__tests__/version-control.routes.fork-point-file.test.ts} +19 -19
- package/src/modules/{workflow/__tests__/workflow.routes.history-read-gate.test.ts → version-control/__tests__/version-control.routes.history-read-gate.test.ts} +13 -13
- package/src/modules/{workflow/__tests__/workflow.routes.lock-path.test.ts → version-control/__tests__/version-control.routes.lock-path.test.ts} +18 -18
- package/src/modules/{workflow/__tests__/workflow.routes.open-change-request.test.ts → version-control/__tests__/version-control.routes.open-change-request.test.ts} +7 -7
- package/src/modules/{workflow/__tests__/workflow.routes.push-refused.test.ts → version-control/__tests__/version-control.routes.push-refused.test.ts} +14 -14
- package/src/modules/{workflow/__tests__/workflow.service.branch-in-use.test.ts → version-control/__tests__/version-control.service.branch-in-use.test.ts} +6 -6
- package/src/modules/{workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts → version-control/__tests__/version-control.service.commitFileWhileLocked.test.ts} +14 -14
- package/src/modules/{workflow/__tests__/workflow.service.delete-branch.test.ts → version-control/__tests__/version-control.service.delete-branch.test.ts} +14 -14
- package/src/modules/{workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts → version-control/__tests__/version-control.service.deleted-branch-sweep.test.ts} +4 -4
- package/src/modules/{workflow/__tests__/workflow.service.facade.test.ts → version-control/__tests__/version-control.service.facade.test.ts} +97 -99
- package/src/modules/{workflow/__tests__/workflow.service.folder-proposals.test.ts → version-control/__tests__/version-control.service.folder-proposals.test.ts} +7 -7
- package/src/modules/{workflow/__tests__/workflow.service.platform-restore.test.ts → version-control/__tests__/version-control.service.platform-restore.test.ts} +6 -6
- package/src/modules/{workflow/__tests__/workflow.service.read-gate.test.ts → version-control/__tests__/version-control.service.read-gate.test.ts} +9 -9
- package/src/modules/{workflow/__tests__/workflow.service.releaseLock.test.ts → version-control/__tests__/version-control.service.releaseLock.test.ts} +42 -42
- package/src/modules/{workflow/__tests__/workflow.service.repository-replaced.test.ts → version-control/__tests__/version-control.service.repository-replaced.test.ts} +4 -4
- package/src/modules/{workflow/__tests__/workflow.service.sync.test.ts → version-control/__tests__/version-control.service.sync.test.ts} +17 -17
- package/src/modules/{workflow/__tests__/workflow.service.update-change-request.pg.test.ts → version-control/__tests__/version-control.service.update-change-request.pg.test.ts} +6 -6
- package/src/modules/{workflow/__tests__/workflow.service.update-change-request.test.ts → version-control/__tests__/version-control.service.update-change-request.test.ts} +11 -11
- package/src/modules/{workflow/__tests__/workflow.service.update-from-target.test.ts → version-control/__tests__/version-control.service.update-from-target.test.ts} +22 -22
- package/src/modules/{workflow → version-control}/agent-tools/__tests__/change-request-read.tools.test.ts +3 -3
- package/src/modules/{workflow → version-control}/agent-tools/__tests__/update-change-request.parity.test.ts +16 -16
- package/src/modules/{workflow/agent-tools/__tests__/workflow.tools.test.ts → version-control/agent-tools/__tests__/version-control.tools.test.ts} +9 -9
- package/src/modules/{workflow → version-control}/agent-tools/change-request-read.tools.ts +2 -2
- package/src/modules/{workflow/agent-tools/workflow.tools.ts → version-control/agent-tools/version-control.tools.ts} +30 -30
- package/src/modules/{workflow → version-control}/change-request-hooks.ts +1 -1
- package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/approval-states.test.ts +5 -5
- package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/cancel-pr.test.ts +8 -8
- package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/carry-approvals-forward.test.ts +6 -6
- package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/erase-approver.test.ts +5 -5
- package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/merge-gate.test.ts +3 -3
- package/src/modules/{workflow/review-workflow → version-control/change-request-review}/__tests__/merge-records-own-commit.test.ts +3 -3
- package/src/modules/{workflow/review-workflow/review-workflow.interface.ts → version-control/change-request-review/change-request-review.interface.ts} +1 -1
- package/src/modules/{workflow/review-workflow/review-workflow.service.ts → version-control/change-request-review/change-request-review.service.ts} +28 -28
- package/src/modules/{workflow → version-control}/event-bus.ts +14 -14
- package/src/modules/{workflow → version-control}/events-delivery.ts +11 -11
- package/src/modules/{workflow → version-control}/events.routes.ts +5 -5
- package/src/modules/{workflow → version-control}/file-lock.service.ts +6 -6
- package/src/modules/{workflow → version-control}/git/__tests__/git-test-helpers.ts +3 -3
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.accessGating.test.ts +5 -5
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.appliedChange.test.ts +4 -4
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.changedFilesForPr.test.ts +16 -16
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.changedPathsMemo.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.commitChanges.test.ts +3 -3
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.commitFile.strayPath.test.ts +10 -10
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.commitFile.test.ts +3 -3
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.createBranch.test.ts +3 -3
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.deleteBranch.test.ts +7 -7
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.diffFileAtCommit.test.ts +9 -9
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.diffFileBetweenBranches.test.ts +23 -23
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.fileBytesAtCommit.test.ts +7 -7
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -3
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.forkPoint.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.history-guards.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.list-branches.test.ts +12 -12
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.listTrackedFiles.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.logForFile.test.ts +6 -6
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.mergeChangeRequest.test.ts +20 -20
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.mergeIntoBranch.test.ts +16 -16
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.pendingChanges.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.postMergePull.test.ts +5 -5
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.prFetchFailure.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.pull.test.ts +12 -12
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.readFileAtRef.test.ts +3 -3
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.status.test.ts +4 -4
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.syncFromRemote.test.ts +8 -8
- package/src/modules/{workflow → version-control}/git/__tests__/git.service.worktrees.test.ts +4 -4
- package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.list-fetch.test.ts +2 -2
- package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.test.ts +6 -6
- package/src/modules/{workflow → version-control}/git/git.service.ts +57 -57
- package/src/modules/{workflow → version-control}/git/node-git-runner.ts +1 -1
- package/src/modules/{workflow → version-control}/git/pull-request.service.ts +12 -12
- package/src/modules/{workflow → version-control}/index.ts +6 -5
- package/src/modules/{workflow → version-control}/pending-commits.service.ts +2 -2
- package/src/modules/{workflow → version-control}/pending-commits.worker.ts +8 -8
- package/src/modules/{workflow → version-control}/read-on-branch.ts +2 -2
- package/src/modules/{workflow/workflow-hooks.ts → version-control/version-control-hooks.ts} +3 -3
- package/src/modules/{workflow/workflow.routes.ts → version-control/version-control.routes.ts} +64 -63
- package/src/modules/{workflow/workflow.service.ts → version-control/version-control.service.ts} +92 -97
- package/src/modules/workspace/__tests__/agent-access.gate.test.ts +8 -8
- package/src/modules/workspace/__tests__/agent-downloads.test.ts +13 -16
- package/src/modules/workspace/__tests__/agent-uploads.test.ts +11 -12
- package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +11 -16
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -3
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +27 -27
- package/src/modules/workspace/__tests__/kb-repository.test.ts +1 -1
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +3 -3
- package/src/modules/workspace/__tests__/start-session.default-branch.test.ts +152 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +6 -6
- package/src/modules/workspace/__tests__/workspace-tmp.tools.test.ts +13 -14
- package/src/modules/workspace/__tests__/workspace.routes.at-ref.test.ts +6 -6
- package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +4 -4
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +6 -6
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +8 -8
- package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +4 -4
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +6 -6
- package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +6 -6
- package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +4 -4
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +6 -6
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +7 -7
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +13 -13
- package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +1 -1
- package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +1 -1
- package/src/modules/workspace/__tests__/workspace.service.worktrees.test.ts +1 -1
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +5 -5
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +5 -5
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +27 -27
- package/src/modules/workspace/agent-access.gate.ts +5 -5
- package/src/modules/workspace/git-internals.middleware.ts +2 -2
- package/src/modules/workspace/kb-repository.ts +1 -1
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +1 -1
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +1 -1
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.worktrees.test.ts +1 -1
- package/src/modules/workspace/startup/kb-git.ts +1 -1
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +94 -70
- package/src/modules/workspace/startup/steps/seed-tree.ts +4 -5
- package/src/modules/workspace/startup/steps/template-files.step.ts +4 -5
- package/src/modules/workspace/startup/steps/template-source.ts +21 -6
- package/src/modules/workspace/workspace-tmp.ts +4 -4
- package/src/modules/workspace/workspace.routes.ts +26 -26
- package/src/modules/workspace/workspace.service.ts +11 -11
- package/src/modules/workspace/workspace.tools.ts +17 -10
- package/src/shared/__tests__/canonical-file-identity.test.ts +5 -5
- package/src/shared/__tests__/http-errors.test.ts +4 -4
- package/src/shared/canonical-file-identity.ts +5 -5
- package/src/shared/domain-errors.ts +44 -44
- package/src/shared/git.contract.ts +1 -1
- package/src/shared/http-errors.ts +3 -3
- package/src/shared/kb-context.ts +130 -130
- package/src/shared/pending-proposals.ts +4 -4
- package/src/tenancy/__tests__/tenant-host.test.ts +372 -372
- package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -221
- package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -35
- package/src/tenancy/index.ts +35 -35
- package/src/tenancy/static-tenant-source.ts +1 -1
- package/src/tenancy/tenant-host.ts +261 -261
- package/src/tenancy/tenant-runtime.ts +208 -208
- package/src/tenancy/tenant-source.contract.ts +59 -59
- package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts.map +0 -1
- package/dist/modules/workflow/agent-tools/change-request-read-shape.js.map +0 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +0 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +0 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +0 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.js.map +0 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +0 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +0 -1
- package/dist/modules/workflow/change-request-hooks.d.ts.map +0 -1
- package/dist/modules/workflow/change-request-hooks.js.map +0 -1
- package/dist/modules/workflow/event-bus.d.ts.map +0 -1
- package/dist/modules/workflow/event-bus.js.map +0 -1
- package/dist/modules/workflow/events-delivery.d.ts.map +0 -1
- package/dist/modules/workflow/events-delivery.js.map +0 -1
- package/dist/modules/workflow/events.routes.d.ts.map +0 -1
- package/dist/modules/workflow/events.routes.js.map +0 -1
- package/dist/modules/workflow/file-lock.service.d.ts.map +0 -1
- package/dist/modules/workflow/file-lock.service.js.map +0 -1
- package/dist/modules/workflow/git/change-request-link.d.ts.map +0 -1
- package/dist/modules/workflow/git/change-request-link.js.map +0 -1
- package/dist/modules/workflow/git/git-version.d.ts.map +0 -1
- package/dist/modules/workflow/git/git-version.js.map +0 -1
- package/dist/modules/workflow/git/git.service.d.ts.map +0 -1
- package/dist/modules/workflow/git/git.service.js.map +0 -1
- package/dist/modules/workflow/git/merge-commit.d.ts.map +0 -1
- package/dist/modules/workflow/git/merge-commit.js.map +0 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +0 -1
- package/dist/modules/workflow/git/node-git-runner.js.map +0 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +0 -1
- package/dist/modules/workflow/git/pull-request.service.js.map +0 -1
- package/dist/modules/workflow/index.d.ts.map +0 -1
- package/dist/modules/workflow/index.js.map +0 -1
- package/dist/modules/workflow/pending-commits.service.d.ts.map +0 -1
- package/dist/modules/workflow/pending-commits.service.js.map +0 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +0 -1
- package/dist/modules/workflow/pending-commits.worker.js.map +0 -1
- package/dist/modules/workflow/read-on-branch.d.ts.map +0 -1
- package/dist/modules/workflow/read-on-branch.js.map +0 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +0 -1
- package/dist/modules/workflow/recovery-bot.js.map +0 -1
- package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +0 -1
- package/dist/modules/workflow/review-workflow/approval-lock.js.map +0 -1
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +0 -1
- package/dist/modules/workflow/review-workflow/review-workflow.interface.js +0 -2
- package/dist/modules/workflow/review-workflow/review-workflow.interface.js.map +0 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +0 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +0 -1
- package/dist/modules/workflow/sanitize-error.d.ts.map +0 -1
- package/dist/modules/workflow/sanitize-error.js.map +0 -1
- package/dist/modules/workflow/workflow-hooks.d.ts.map +0 -1
- package/dist/modules/workflow/workflow-hooks.js.map +0 -1
- package/dist/modules/workflow/workflow.routes.d.ts.map +0 -1
- package/dist/modules/workflow/workflow.routes.js.map +0 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +0 -1
- package/dist/modules/workflow/workflow.service.js.map +0 -1
- package/src/modules/mcp/__tests__/read-only-hint.e2e.test.ts +0 -213
- /package/dist/modules/{workflow → version-control}/agent-tools/change-request-read-shape.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/agent-tools/change-request-read-shape.js +0 -0
- /package/dist/modules/{workflow → version-control}/agent-tools/change-request-read.tools.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/agent-tools/change-request-summary.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/agent-tools/change-request-summary.js +0 -0
- /package/dist/modules/{workflow → version-control}/change-request-hooks.js +0 -0
- /package/dist/modules/{workflow/review-workflow → version-control/change-request-review}/approval-lock.d.ts +0 -0
- /package/dist/modules/{workflow/review-workflow → version-control/change-request-review}/approval-lock.js +0 -0
- /package/dist/modules/{workflow → version-control}/events-delivery.js +0 -0
- /package/dist/modules/{workflow → version-control}/events.routes.js +0 -0
- /package/dist/modules/{workflow → version-control}/git/change-request-link.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/git/change-request-link.js +0 -0
- /package/dist/modules/{workflow → version-control}/git/git-version.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/git/git-version.js +0 -0
- /package/dist/modules/{workflow → version-control}/git/merge-commit.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/git/merge-commit.js +0 -0
- /package/dist/modules/{workflow → version-control}/git/node-git-runner.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/recovery-bot.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/recovery-bot.js +0 -0
- /package/dist/modules/{workflow → version-control}/sanitize-error.d.ts +0 -0
- /package/dist/modules/{workflow → version-control}/sanitize-error.js +0 -0
- /package/src/modules/{workflow → version-control}/__tests__/change-request-hooks.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/__tests__/fake-file-lock-db.ts +0 -0
- /package/src/modules/{workflow → version-control}/__tests__/pending-commits.canonical.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/__tests__/pending-commits.enqueue-if-absent.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/__tests__/pending-commits.repository-replaced.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/__tests__/sanitize-error.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/agent-tools/__tests__/change-request-read-shape.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/agent-tools/change-request-read-shape.ts +0 -0
- /package/src/modules/{workflow → version-control}/agent-tools/change-request-summary.ts +0 -0
- /package/src/modules/{workflow/review-workflow → version-control/change-request-review}/approval-lock.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/branchAuthor.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/change-request-link.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/git-version.failure.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/git-version.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/node-git-runner.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.getPrDetail.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.placeholder.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.viewer-can-delete.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/__tests__/pull-request.service.viewer-can-update.test.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/change-request-link.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/git-version.ts +0 -0
- /package/src/modules/{workflow → version-control}/git/merge-commit.ts +0 -0
- /package/src/modules/{workflow → version-control}/recovery-bot.ts +0 -0
- /package/src/modules/{workflow → version-control}/sanitize-error.ts +0 -0
|
@@ -5,7 +5,7 @@ import { logger } from '../../shared/logging.js';
|
|
|
5
5
|
const log = logger('vault');
|
|
6
6
|
import { and, desc, eq, inArray, isNull, like, or } from 'drizzle-orm';
|
|
7
7
|
import type { Database } from '../database/index.js';
|
|
8
|
-
import { secrets } from '../database/index.js';
|
|
8
|
+
import { pgErrorCode, secrets } from '../database/index.js';
|
|
9
9
|
import { TokenCrypto } from '../../shared/token-crypto.js';
|
|
10
10
|
import { assertSafeFetchUrl } from '../../shared/ssrf.js';
|
|
11
11
|
import { readCapped } from '../../shared/read-capped.js';
|
|
@@ -24,10 +24,16 @@ import {
|
|
|
24
24
|
type NamespaceSecretCount,
|
|
25
25
|
isKeyInNamespace,
|
|
26
26
|
type ForcedRefreshOutcome,
|
|
27
|
+
type ManagedSignInRef,
|
|
28
|
+
type ManagedTokenRefresher,
|
|
29
|
+
type ManagedTokenRevoker,
|
|
30
|
+
type DeliveredTokenSet,
|
|
31
|
+
type ManagedRowsWriter,
|
|
27
32
|
InvalidSecretError,
|
|
28
33
|
SecretNotFoundError,
|
|
29
34
|
SecretOAuthError,
|
|
30
35
|
} from './secrets-vault.contract.js';
|
|
36
|
+
import { timingSafeStringEqual } from '../auth/index.js';
|
|
31
37
|
|
|
32
38
|
const MAX_KEY_LEN = 200;
|
|
33
39
|
const MAX_LABEL_LEN = 200;
|
|
@@ -36,6 +42,31 @@ const MAX_VALUE_LEN = 100_000;
|
|
|
36
42
|
const REFRESH_SKEW_MS = 60_000;
|
|
37
43
|
/** Upper bound on a token-endpoint round-trip, so a hung provider can't block callers. */
|
|
38
44
|
const TOKEN_REQUEST_TIMEOUT_MS = 10_000;
|
|
45
|
+
/** Upper bound on telling a provider a managed sign-in is over, so a deletion never waits on it. */
|
|
46
|
+
const REVOKE_TIMEOUT_MS = 10_000;
|
|
47
|
+
/** Upper bound on a broker renewing a managed token: past it the stale token is served and the next call asks again. */
|
|
48
|
+
const REFRESH_TIMEOUT_MS = 15_000;
|
|
49
|
+
/** How many times a guarded replacement re-reads a row that changed under its revoke before giving up. */
|
|
50
|
+
const REPLACE_ATTEMPTS = 3;
|
|
51
|
+
/** How many times a handoff re-reads a row a refresh rotated under it (the nonce rides along) before refusing. */
|
|
52
|
+
const HANDOFF_ATTEMPTS = 3;
|
|
53
|
+
|
|
54
|
+
/** `work`, or a rejection once `ms` have passed: a handler that never settles must not hold the vault. */
|
|
55
|
+
async function bounded<T>(work: Promise<T>, ms: number, what: string): Promise<T> {
|
|
56
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
57
|
+
try {
|
|
58
|
+
return await Promise.race([
|
|
59
|
+
work,
|
|
60
|
+
new Promise<never>((_, reject) => {
|
|
61
|
+
timer = setTimeout(() => reject(new Error(`${what} did not settle within ${ms}ms`)), ms);
|
|
62
|
+
}),
|
|
63
|
+
]);
|
|
64
|
+
} finally {
|
|
65
|
+
if (timer) clearTimeout(timer);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** How often a managed sign-in's opening is retried against a row that keeps changing under it. */
|
|
69
|
+
const BEGIN_MANAGED_ATTEMPTS = 3;
|
|
39
70
|
/** How much of a refusing token endpoint's body is read for its error code: an OAuth error object, never a whole error page. */
|
|
40
71
|
const TOKEN_ERROR_BODY_MAX_BYTES = 2048;
|
|
41
72
|
// Hexis's own sentences for a refused token request: what to change, in our
|
|
@@ -155,6 +186,14 @@ interface OAuthBlob {
|
|
|
155
186
|
* like the verifier: consumed by the exchange it was minted for.
|
|
156
187
|
*/
|
|
157
188
|
pendingScopes?: string;
|
|
189
|
+
/**
|
|
190
|
+
* A managed sign-in in progress: the one-time nonce the broker's handoff
|
|
191
|
+
* must echo. Stored encrypted like the verifier, and for the same reason —
|
|
192
|
+
* it binds the handoff to the sign-in this row opened, so a handoff that
|
|
193
|
+
* is replayed or forged lands nowhere. Cleared by the handoff it was
|
|
194
|
+
* minted for.
|
|
195
|
+
*/
|
|
196
|
+
pendingNonce?: string;
|
|
158
197
|
}
|
|
159
198
|
|
|
160
199
|
/**
|
|
@@ -171,6 +210,20 @@ interface OAuthBlob {
|
|
|
171
210
|
*/
|
|
172
211
|
export class DbSecretsVaultService implements ISecretsVaultService {
|
|
173
212
|
private cryptoInstance: TokenCrypto | null = null;
|
|
213
|
+
/**
|
|
214
|
+
* How a MANAGED sign-in's tokens are renewed and revoked — through the
|
|
215
|
+
* broker and the provider respectively. Setter-injected: both depend on
|
|
216
|
+
* the connector catalog, which is built after this service. Without them a
|
|
217
|
+
* managed token that expires is served stale (a transient, retried on the
|
|
218
|
+
* next call) and a disconnect deletes without revoking.
|
|
219
|
+
*/
|
|
220
|
+
private managedRefresher?: ManagedTokenRefresher;
|
|
221
|
+
private managedRevoker?: ManagedTokenRevoker;
|
|
222
|
+
|
|
223
|
+
setManagedTokenHandlers(handlers: { refresher?: ManagedTokenRefresher; revoker?: ManagedTokenRevoker }): void {
|
|
224
|
+
this.managedRefresher = handlers.refresher;
|
|
225
|
+
this.managedRevoker = handlers.revoker;
|
|
226
|
+
}
|
|
174
227
|
|
|
175
228
|
constructor(
|
|
176
229
|
private readonly db: Database,
|
|
@@ -246,15 +299,20 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
246
299
|
const label = this.normaliseLabel(input.label);
|
|
247
300
|
const valueEncrypted = this.crypto().encrypt(input.value);
|
|
248
301
|
|
|
302
|
+
// The caller's row may hold a managed sign-in: its tokens are told to the
|
|
303
|
+
// provider before this value replaces them, as on every replacement.
|
|
249
304
|
// Upsert on (user_id, key): re-saving a key replaces its value in place.
|
|
250
|
-
const
|
|
251
|
-
.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
305
|
+
const row = await this.revokedReplacement(and(eq(secrets.userId, userId), eq(secrets.key, key)), (guard) =>
|
|
306
|
+
this.db
|
|
307
|
+
.insert(secrets)
|
|
308
|
+
.values({ userId, key, kind: 'static', label, valueEncrypted, oauthMeta: null })
|
|
309
|
+
.onConflictDoUpdate({
|
|
310
|
+
target: [secrets.userId, secrets.key],
|
|
311
|
+
set: { kind: 'static', label, valueEncrypted, oauthMeta: null, updatedAt: new Date() },
|
|
312
|
+
...(guard ? { setWhere: guard } : {}),
|
|
313
|
+
})
|
|
314
|
+
.returning(),
|
|
315
|
+
);
|
|
258
316
|
this.notifyMutation(userId);
|
|
259
317
|
return this.toSummary(row);
|
|
260
318
|
}
|
|
@@ -269,26 +327,28 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
269
327
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
270
328
|
const oauthMeta = this.publicProviderMeta(provider);
|
|
271
329
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
330
|
+
// A managed sign-in under this key loses its tokens here: the provider is
|
|
331
|
+
// told first, as on every replacement.
|
|
332
|
+
const row = await this.revokedReplacement(and(eq(secrets.userId, userId), eq(secrets.key, key)), (guard) =>
|
|
333
|
+
this.db
|
|
334
|
+
.insert(secrets)
|
|
335
|
+
.values({ userId, key, kind: 'oauth', label, valueEncrypted, oauthMeta })
|
|
336
|
+
.onConflictDoUpdate({
|
|
337
|
+
target: [secrets.userId, secrets.key],
|
|
338
|
+
// Replacing an oauth secret's provider config drops any existing tokens
|
|
339
|
+
// (they belonged to the old client) — the user must re-authorize.
|
|
340
|
+
set: { kind: 'oauth', label, valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
341
|
+
...(guard ? { setWhere: guard } : {}),
|
|
342
|
+
})
|
|
343
|
+
.returning(),
|
|
344
|
+
);
|
|
282
345
|
this.notifyMutation(userId);
|
|
283
346
|
return this.toSummary(row);
|
|
284
347
|
}
|
|
285
348
|
|
|
286
349
|
async remove(userId: string, id: string): Promise<void> {
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
.where(and(eq(secrets.id, id), eq(secrets.userId, userId)))
|
|
290
|
-
.returning({ id: secrets.id });
|
|
291
|
-
if (res.length === 0) throw new SecretNotFoundError(id);
|
|
350
|
+
const gone = await this.revokeThenDelete(and(eq(secrets.id, id), eq(secrets.userId, userId)));
|
|
351
|
+
if (gone.length === 0) throw new SecretNotFoundError(id);
|
|
292
352
|
this.notifyMutation(userId);
|
|
293
353
|
}
|
|
294
354
|
|
|
@@ -303,37 +363,199 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
303
363
|
const label = this.normaliseLabel(input.label);
|
|
304
364
|
const valueEncrypted = this.crypto().encrypt(input.value);
|
|
305
365
|
|
|
366
|
+
// A shared row that held a managed workspace connection is told to its
|
|
367
|
+
// provider before this value replaces it; the tokens would otherwise go
|
|
368
|
+
// unrevoked with the row's kind.
|
|
306
369
|
// Upsert the ONE shared row for this key (user_id NULL). The conflict target
|
|
307
370
|
// is the partial unique index `secrets_shared_key_unq` — hence `targetWhere`.
|
|
308
|
-
const
|
|
309
|
-
.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
371
|
+
const row = await this.revokedReplacement(and(isNull(secrets.userId), eq(secrets.key, key)), (guard) =>
|
|
372
|
+
this.db
|
|
373
|
+
.insert(secrets)
|
|
374
|
+
.values({ userId: null, key, kind: 'static', label, valueEncrypted, oauthMeta: null })
|
|
375
|
+
.onConflictDoUpdate({
|
|
376
|
+
target: secrets.key,
|
|
377
|
+
targetWhere: isNull(secrets.userId),
|
|
378
|
+
set: { kind: 'static', label, valueEncrypted, oauthMeta: null, updatedAt: new Date() },
|
|
379
|
+
...(guard ? { setWhere: guard } : {}),
|
|
380
|
+
})
|
|
381
|
+
.returning(),
|
|
382
|
+
);
|
|
317
383
|
this.notifyMutation(null);
|
|
318
384
|
return this.toSummary(row);
|
|
319
385
|
}
|
|
320
386
|
|
|
321
387
|
async removeShared(key: string): Promise<void> {
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
.where(and(isNull(secrets.userId), eq(secrets.key, key)))
|
|
325
|
-
.returning({ id: secrets.id });
|
|
326
|
-
if (res.length === 0) throw new SecretNotFoundError(key);
|
|
388
|
+
const gone = await this.revokeThenDelete(and(isNull(secrets.userId), eq(secrets.key, key)));
|
|
389
|
+
if (gone.length === 0) throw new SecretNotFoundError(key);
|
|
327
390
|
this.notifyMutation(null);
|
|
328
391
|
}
|
|
329
392
|
|
|
330
393
|
async removeUserByKey(userId: string, key: string): Promise<void> {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
394
|
+
const gone = await this.revokeThenDelete(and(eq(secrets.userId, userId), eq(secrets.key, key)));
|
|
395
|
+
if (gone.length === 0) throw new SecretNotFoundError(key);
|
|
396
|
+
this.notifyMutation(userId);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Delete the rows `where` names, each told to its provider first when it
|
|
401
|
+
* is a managed sign-in with tokens (see {@link revokeRow}). The delete is
|
|
402
|
+
* guarded on the ciphertext the revoke read: a handoff or a refresh landing
|
|
403
|
+
* in between has replaced the tokens, and deleting THOSE unrevoked is the
|
|
404
|
+
* leak the revoke exists to prevent. A miss re-reads the row and goes
|
|
405
|
+
* again, bounded; what could not be settled in those passes is left
|
|
406
|
+
* standing rather than deleted blind.
|
|
407
|
+
*/
|
|
408
|
+
private async revokeThenDelete(where: ReturnType<typeof and>): Promise<{ id: string; userId: string | null; kind: string }[]> {
|
|
409
|
+
// No revoker, nothing to read first: one plain delete, as every removal
|
|
410
|
+
// was before managed sign-ins, and no miss to re-read for.
|
|
411
|
+
if (!this.managedRevoker) {
|
|
412
|
+
return this.db.delete(secrets).where(where).returning({ id: secrets.id, userId: secrets.userId, kind: secrets.kind });
|
|
413
|
+
}
|
|
414
|
+
const gone: { id: string; userId: string | null; kind: string }[] = [];
|
|
415
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
416
|
+
const rows = await this.db.select().from(secrets).where(where);
|
|
417
|
+
if (rows.length === 0) break;
|
|
418
|
+
// Every row's revocation at once, so a bulk delete (a namespace, a
|
|
419
|
+
// person's connections) waits one revocation's bound, not one per row.
|
|
420
|
+
await Promise.all(rows.map((row) => this.revokeRow(row)));
|
|
421
|
+
let missed = false;
|
|
422
|
+
for (const row of rows) {
|
|
423
|
+
const deleted = await this.db
|
|
424
|
+
.delete(secrets)
|
|
425
|
+
.where(and(eq(secrets.id, row.id), eq(secrets.valueEncrypted, row.valueEncrypted)))
|
|
426
|
+
.returning({ id: secrets.id, userId: secrets.userId, kind: secrets.kind });
|
|
427
|
+
if (deleted.length === 0) missed = true;
|
|
428
|
+
else gone.push(...deleted);
|
|
429
|
+
}
|
|
430
|
+
if (!missed) break;
|
|
431
|
+
}
|
|
432
|
+
return gone;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async removeManagedRowsOfUser(userId: string, within?: ManagedRowsWriter): Promise<number> {
|
|
436
|
+
if (within) return this.removeManagedRowsLocked(userId, within);
|
|
437
|
+
// Every oauth row of the user whose meta names a managed app. Read
|
|
438
|
+
// first (the managed mark lives in JSON the WHERE cannot see), revoked,
|
|
439
|
+
// then deleted by id; the per-user filter on the delete is what keeps a
|
|
440
|
+
// row read for this user from ever deleting another's.
|
|
441
|
+
const rows = await this.db
|
|
442
|
+
.select()
|
|
443
|
+
.from(secrets)
|
|
444
|
+
.where(and(eq(secrets.userId, userId), eq(secrets.kind, 'oauth')));
|
|
445
|
+
const managed = rows.filter((r) => this.managedRefOf(r.oauthMeta) !== null);
|
|
446
|
+
if (managed.length === 0) return 0;
|
|
447
|
+
const deleted = await this.revokeThenDelete(and(eq(secrets.userId, userId), inArray(secrets.id, managed.map((r) => r.id))));
|
|
448
|
+
if (deleted.length > 0) this.notifyMutation(userId);
|
|
449
|
+
// A row the guarded delete could not settle (its tokens kept changing
|
|
450
|
+
// under the revoke) is still here, unrevoked. The caller is told rather
|
|
451
|
+
// than left to believe the person holds nothing: an erasure refuses and
|
|
452
|
+
// is retried, a switch-off logs it and the row is refused on use.
|
|
453
|
+
if (deleted.length < managed.length) {
|
|
454
|
+
throw new SecretOAuthError(
|
|
455
|
+
`${managed.length - deleted.length} managed sign-in(s) kept changing while being revoked and were not removed; try again`,
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return deleted.length;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* The same removal inside a caller's transaction (an account erasure, just
|
|
463
|
+
* before the user row goes): the rows are LOCKED for the transaction, so a
|
|
464
|
+
* handoff or a refresh arriving now waits and then finds no row, which is
|
|
465
|
+
* what makes the revoke final. No guard is needed under the lock; the
|
|
466
|
+
* revoke stays bounded (see {@link revokeRow}), so the transaction never
|
|
467
|
+
* waits on a provider longer than that.
|
|
468
|
+
*/
|
|
469
|
+
private async removeManagedRowsLocked(userId: string, tx: ManagedRowsWriter): Promise<number> {
|
|
470
|
+
const rows = await tx
|
|
471
|
+
.select()
|
|
472
|
+
.from(secrets)
|
|
473
|
+
.where(and(eq(secrets.userId, userId), eq(secrets.kind, 'oauth')))
|
|
474
|
+
.for('update');
|
|
475
|
+
const managed = rows.filter((r) => this.managedRefOf(r.oauthMeta) !== null);
|
|
476
|
+
if (managed.length === 0) return 0;
|
|
477
|
+
await Promise.all(managed.map((row) => this.revokeRow(row)));
|
|
478
|
+
await tx.delete(secrets).where(
|
|
479
|
+
and(
|
|
480
|
+
eq(secrets.userId, userId),
|
|
481
|
+
inArray(
|
|
482
|
+
secrets.id,
|
|
483
|
+
managed.map((r) => r.id),
|
|
484
|
+
),
|
|
485
|
+
),
|
|
486
|
+
);
|
|
336
487
|
this.notifyMutation(userId);
|
|
488
|
+
return managed.length;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Replace what `where` names with `write`, a managed sign-in's tokens told
|
|
493
|
+
* to their provider first (see {@link revokeRow}; best effort, never holding
|
|
494
|
+
* the user's decision). The write is guarded on the ciphertext the revoke
|
|
495
|
+
* read (`setWhere`): a refresh or a handoff landing in between has rotated
|
|
496
|
+
* the tokens, and writing over THOSE unrevoked is the leak the revoke exists
|
|
497
|
+
* to prevent — as the guarded delete of {@link revokeThenDelete}. A miss
|
|
498
|
+
* re-reads and goes again, bounded; a row that kept changing is left as it
|
|
499
|
+
* is and the caller is told. With no revoker wired there is nothing to read
|
|
500
|
+
* first: one plain write, as every replacement was before managed sign-ins.
|
|
501
|
+
*/
|
|
502
|
+
private async revokedReplacement<T>(
|
|
503
|
+
where: ReturnType<typeof and>,
|
|
504
|
+
write: (guard?: ReturnType<typeof eq>) => Promise<T[]>,
|
|
505
|
+
): Promise<T> {
|
|
506
|
+
if (!this.managedRevoker) return (await write())[0] as T;
|
|
507
|
+
for (let attempt = 0; attempt < REPLACE_ATTEMPTS; attempt++) {
|
|
508
|
+
const rows = await this.db.select().from(secrets).where(where);
|
|
509
|
+
await Promise.all(rows.map((row) => this.revokeRow(row)));
|
|
510
|
+
// The row as it was read, or no row at all: a conflict with anything
|
|
511
|
+
// else (a row written meanwhile) is a miss, and the next pass reads it.
|
|
512
|
+
const guard =
|
|
513
|
+
rows.length > 0
|
|
514
|
+
? inArray(
|
|
515
|
+
secrets.valueEncrypted,
|
|
516
|
+
rows.map((row) => row.valueEncrypted),
|
|
517
|
+
)
|
|
518
|
+
: eq(secrets.valueEncrypted, '');
|
|
519
|
+
const written = await write(guard);
|
|
520
|
+
if (written.length > 0) return written[0] as T;
|
|
521
|
+
}
|
|
522
|
+
throw new SecretOAuthError('the sign-in kept changing while being replaced; try again');
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
private async revokeRow(row: typeof secrets.$inferSelect): Promise<void> {
|
|
526
|
+
if (!this.managedRevoker || row.kind !== 'oauth') return;
|
|
527
|
+
const ref = this.managedRefOf(row.oauthMeta);
|
|
528
|
+
if (!ref) return;
|
|
529
|
+
const tokens = this.readTokensSafe(row.valueEncrypted);
|
|
530
|
+
if (!tokens?.access_token) return;
|
|
531
|
+
// Bounded: the delete that follows is the user's decision, and a revoker
|
|
532
|
+
// that never settles must not hold it.
|
|
533
|
+
try {
|
|
534
|
+
await bounded(
|
|
535
|
+
this.managedRevoker.revoke(ref, { access_token: tokens.access_token, refresh_token: tokens.refresh_token }),
|
|
536
|
+
REVOKE_TIMEOUT_MS,
|
|
537
|
+
'revocation',
|
|
538
|
+
);
|
|
539
|
+
} catch (err) {
|
|
540
|
+
// The key and the connector name the sign-in. The error itself stays
|
|
541
|
+
// out: a revoker's message is its own, and this line is the one place a
|
|
542
|
+
// token could otherwise reach a log from this path.
|
|
543
|
+
log.warn('revoking a managed sign-in with its provider failed; deleting it here anyway', {
|
|
544
|
+
key: row.key,
|
|
545
|
+
connector: ref.connectorId,
|
|
546
|
+
failure: err instanceof Error ? err.name : typeof err,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** The managed reference a row's public meta carries, or null for an ordinary sign-in. */
|
|
552
|
+
private managedRefOf(raw: unknown): ManagedSignInRef | null {
|
|
553
|
+
const meta = (raw ?? {}) as Record<string, unknown>;
|
|
554
|
+
const m = meta.managed as Record<string, unknown> | undefined;
|
|
555
|
+
if (!m || typeof m !== 'object') return null;
|
|
556
|
+
const connectorId = typeof m.connectorId === 'string' ? m.connectorId : '';
|
|
557
|
+
const scopeSet = typeof m.scopeSet === 'string' ? m.scopeSet : '';
|
|
558
|
+
return connectorId && scopeSet ? { connectorId, scopeSet } : null;
|
|
337
559
|
}
|
|
338
560
|
|
|
339
561
|
async countNamespace(prefix: string): Promise<NamespaceSecretCount> {
|
|
@@ -356,10 +578,10 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
356
578
|
for (let pass = 0; pass < 5; pass++) {
|
|
357
579
|
const rows = await this.namespaceRows(prefix);
|
|
358
580
|
if (rows.length === 0) break;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
581
|
+
// A managed sign-in under the namespace is told to its provider
|
|
582
|
+
// before the delete, for the same reason the per-row removals do —
|
|
583
|
+
// and guarded the same way, when a revoker is there to need it.
|
|
584
|
+
const deleted = await this.revokeThenDelete(inArray(secrets.id, rows.map((r) => r.id)));
|
|
363
585
|
gone.push(...deleted);
|
|
364
586
|
}
|
|
365
587
|
} finally {
|
|
@@ -409,6 +631,7 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
409
631
|
userId: secrets.userId,
|
|
410
632
|
kind: secrets.kind,
|
|
411
633
|
valueEncrypted: secrets.valueEncrypted,
|
|
634
|
+
oauthMeta: secrets.oauthMeta,
|
|
412
635
|
})
|
|
413
636
|
.from(secrets)
|
|
414
637
|
.where(and(inArray(secrets.key, keys), or(isNull(secrets.userId), eq(secrets.userId, userId))));
|
|
@@ -417,6 +640,15 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
417
640
|
const adminRow = rows.find((r) => r.key === key && r.userId === null);
|
|
418
641
|
// Decrypt+parse the oauth row's token set once, then derive both fields from it.
|
|
419
642
|
const tokens = userRow?.kind === 'oauth' ? this.readTokensSafe(userRow.valueEncrypted) : undefined;
|
|
643
|
+
// The shared row holds tokens only for a managed workspace connection;
|
|
644
|
+
// an ordinary sign-in's shared row is the client secret and reads as
|
|
645
|
+
// "no tokens" here, which is the truthful answer for it.
|
|
646
|
+
const adminTokens = adminRow?.kind === 'oauth' ? this.readTokensSafe(adminRow.valueEncrypted) : undefined;
|
|
647
|
+
const adminMeta = (adminRow?.oauthMeta ?? null) as Record<string, unknown> | null;
|
|
648
|
+
const accountOf = (meta: unknown): string | undefined => {
|
|
649
|
+
const a = (meta as Record<string, unknown> | null)?.account;
|
|
650
|
+
return typeof a === 'string' && a ? a : undefined;
|
|
651
|
+
};
|
|
420
652
|
return {
|
|
421
653
|
key,
|
|
422
654
|
adminConfigured: !!adminRow,
|
|
@@ -430,10 +662,157 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
430
662
|
// Only meaningful for an oauth row: the scopes the caller's token was granted,
|
|
431
663
|
// so a coverage check can tell whether it still covers the tool's live scopes.
|
|
432
664
|
grantedScopes: userRow?.kind === 'oauth' ? tokens?.scope : undefined,
|
|
665
|
+
adminAuthorized: adminRow?.kind === 'oauth' ? Boolean(adminTokens?.access_token) : undefined,
|
|
666
|
+
adminGrantedScopes: adminRow?.kind === 'oauth' ? adminTokens?.scope : undefined,
|
|
667
|
+
userAccount: userRow?.kind === 'oauth' ? accountOf(userRow.oauthMeta) : undefined,
|
|
668
|
+
adminAccount: adminRow?.kind === 'oauth' ? accountOf(adminMeta) : undefined,
|
|
669
|
+
// An owner's own app on the shared row: a client id without the managed mark.
|
|
670
|
+
adminBringsOwnApp:
|
|
671
|
+
adminRow?.kind === 'oauth'
|
|
672
|
+
? this.managedRefOf(adminMeta) === null && typeof adminMeta?.clientId === 'string' && adminMeta.clientId !== ''
|
|
673
|
+
: undefined,
|
|
433
674
|
};
|
|
434
675
|
});
|
|
435
676
|
}
|
|
436
677
|
|
|
678
|
+
async beginManagedConnection(input: {
|
|
679
|
+
userId: string | null;
|
|
680
|
+
key: string;
|
|
681
|
+
provider: OAuthProviderConfig;
|
|
682
|
+
nonce: string;
|
|
683
|
+
}): Promise<void> {
|
|
684
|
+
const key = this.requireKey(input.key);
|
|
685
|
+
const userId = input.userId === null ? null : this.requireUserId(input.userId);
|
|
686
|
+
const ref = input.provider.managed;
|
|
687
|
+
if (!ref) throw new InvalidSecretError('a managed sign-in needs its connector reference');
|
|
688
|
+
if (typeof input.nonce !== 'string' || input.nonce.length < 16) throw new InvalidSecretError('nonce is required');
|
|
689
|
+
const oauthMeta = this.publicProviderMeta(this.requireProvider(input.provider, { managed: true }));
|
|
690
|
+
const where = userId === null ? and(isNull(secrets.userId), eq(secrets.key, key)) : and(eq(secrets.userId, userId), eq(secrets.key, key));
|
|
691
|
+
// Read-modify-write on a LIVE credential: a refresh running concurrently
|
|
692
|
+
// may rotate the tokens between the read and the write, and writing our
|
|
693
|
+
// copy over them would leave the row holding a dead refresh token. So the
|
|
694
|
+
// update is guarded on the ciphertext read, and a miss re-reads and
|
|
695
|
+
// rebuilds from the fresh row; bounded, so a row that keeps changing
|
|
696
|
+
// fails loudly rather than forever. The same rule `beginOAuth` follows.
|
|
697
|
+
for (let attempt = 0; attempt < BEGIN_MANAGED_ATTEMPTS; attempt++) {
|
|
698
|
+
const [existing] = await this.db.select().from(secrets).where(where).limit(1);
|
|
699
|
+
// Keep what the SAME connection already holds (its tokens, so an
|
|
700
|
+
// abandoned reconnect costs nothing). The same connection means the same
|
|
701
|
+
// connector and scope set: a row of another connector's, or of another
|
|
702
|
+
// set's, holds a token for something else, and a declaration that
|
|
703
|
+
// changed must not hand it to the new one. Start clean over anything
|
|
704
|
+
// else too — a static value or an owner-app sign-in under this key.
|
|
705
|
+
const storedRef = existing?.kind === 'oauth' ? this.managedRefOf(existing.oauthMeta) : null;
|
|
706
|
+
const sameConnection =
|
|
707
|
+
storedRef !== null && storedRef.connectorId === ref.connectorId && storedRef.scopeSet === ref.scopeSet;
|
|
708
|
+
let kept: OAuthBlob = {};
|
|
709
|
+
if (existing && sameConnection) {
|
|
710
|
+
try {
|
|
711
|
+
kept = this.readBlob(existing.valueEncrypted);
|
|
712
|
+
} catch {
|
|
713
|
+
kept = {};
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const next: OAuthBlob = { tokens: kept.tokens, pendingNonce: input.nonce };
|
|
717
|
+
const valueEncrypted = this.crypto().encrypt(JSON.stringify(next));
|
|
718
|
+
const account = sameConnection ? (existing!.oauthMeta as Record<string, unknown> | null)?.account : undefined;
|
|
719
|
+
const meta = typeof account === 'string' && account ? { ...oauthMeta, account } : oauthMeta;
|
|
720
|
+
if (!existing) {
|
|
721
|
+
try {
|
|
722
|
+
await this.db.insert(secrets).values({ userId, key, kind: 'oauth', label: null, valueEncrypted, oauthMeta: meta });
|
|
723
|
+
return;
|
|
724
|
+
} catch (err) {
|
|
725
|
+
// Another start of the same sign-in inserted the row first: the
|
|
726
|
+
// next pass finds it and updates it, as it would any existing row.
|
|
727
|
+
if (pgErrorCode(err) === '23505') continue; // unique_violation
|
|
728
|
+
throw err;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
// A row that held ANOTHER connection's tokens is about to lose them:
|
|
732
|
+
// the provider is told first, as on every other replacement.
|
|
733
|
+
if (!sameConnection && storedRef) await this.revokeRow(existing);
|
|
734
|
+
const written = await this.db
|
|
735
|
+
.update(secrets)
|
|
736
|
+
.set({ kind: 'oauth', valueEncrypted, oauthMeta: meta, updatedAt: new Date() })
|
|
737
|
+
.where(and(eq(secrets.id, existing.id), eq(secrets.valueEncrypted, existing.valueEncrypted)))
|
|
738
|
+
.returning({ id: secrets.id });
|
|
739
|
+
if (Array.isArray(written) && written.length === 0) continue;
|
|
740
|
+
// A row that held something ELSE (a value, another connection's
|
|
741
|
+
// tokens, an owner's app) no longer does: caches keyed on it are told.
|
|
742
|
+
// The same connection's tokens carried over unchanged tell nobody.
|
|
743
|
+
if (!kept.tokens) this.notifyMutation(userId);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
throw new SecretOAuthError('The sign-in could not be opened: its stored credential kept changing. Try again.');
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
async completeManagedConnection(input: {
|
|
750
|
+
userId: string | null;
|
|
751
|
+
key: string;
|
|
752
|
+
nonce: string;
|
|
753
|
+
tokens: DeliveredTokenSet;
|
|
754
|
+
account?: string;
|
|
755
|
+
}): Promise<void> {
|
|
756
|
+
const key = this.requireKey(input.key);
|
|
757
|
+
const userId = input.userId === null ? null : this.requireUserId(input.userId);
|
|
758
|
+
const where = userId === null ? and(isNull(secrets.userId), eq(secrets.key, key)) : and(eq(secrets.userId, userId), eq(secrets.key, key));
|
|
759
|
+
// Every refusal below is the same words: a caller that can tell "no such
|
|
760
|
+
// sign-in" from "wrong nonce" from "already used" has learned which rows
|
|
761
|
+
// exist, and the handoff route is reachable with the broker secret alone.
|
|
762
|
+
const refused = () => new SecretOAuthError('No managed sign-in is waiting for this handoff');
|
|
763
|
+
const tokens = input.tokens;
|
|
764
|
+
if (typeof tokens?.access_token !== 'string' || !tokens.access_token) {
|
|
765
|
+
throw new InvalidSecretError('the handoff carries no access token');
|
|
766
|
+
}
|
|
767
|
+
// Read, check the nonce, write guarded on what was read; and again when
|
|
768
|
+
// the write misses. A miss with the nonce still on the row is a refresh
|
|
769
|
+
// that rotated the row's tokens meanwhile (a refresh carries the nonce
|
|
770
|
+
// across, see `inFlightOf`), not a second handoff: the broker delivered
|
|
771
|
+
// these tokens once, and refusing them would lose a valid sign-in for
|
|
772
|
+
// good. A row whose nonce is gone or changed refuses on the re-read.
|
|
773
|
+
for (let attempt = 0; ; attempt++) {
|
|
774
|
+
const [row] = await this.db.select().from(secrets).where(where).limit(1);
|
|
775
|
+
if (!row || row.kind !== 'oauth' || !this.managedRefOf(row.oauthMeta)) throw refused();
|
|
776
|
+
let blob: OAuthBlob;
|
|
777
|
+
try {
|
|
778
|
+
blob = this.readBlob(row.valueEncrypted);
|
|
779
|
+
} catch {
|
|
780
|
+
throw refused();
|
|
781
|
+
}
|
|
782
|
+
if (!blob.pendingNonce || typeof input.nonce !== 'string' || !timingSafeStringEqual(input.nonce, blob.pendingNonce)) {
|
|
783
|
+
throw refused();
|
|
784
|
+
}
|
|
785
|
+
const meta = { ...((row.oauthMeta ?? {}) as Record<string, unknown>) };
|
|
786
|
+
// A broker that echoes no `scope` delivered what the sign-in asked for:
|
|
787
|
+
// the scopes the row was opened with stand in, as `pendingScopes` does
|
|
788
|
+
// for the code exchange. Stored as unknown, the grant would count as
|
|
789
|
+
// covering nothing, and the sign-in would read as under-scoped for good.
|
|
790
|
+
// An EMPTY scope is not "none echoed": it is an empty grant, kept as such.
|
|
791
|
+
const requested = Array.isArray(meta.scopes) && meta.scopes.length > 0 ? meta.scopes.map(String).join(' ') : undefined;
|
|
792
|
+
const stored: OAuthTokenSet = {
|
|
793
|
+
access_token: tokens.access_token,
|
|
794
|
+
refresh_token: typeof tokens.refresh_token === 'string' ? tokens.refresh_token : undefined,
|
|
795
|
+
expires_at: typeof tokens.expires_at === 'number' ? tokens.expires_at : undefined,
|
|
796
|
+
token_type: typeof tokens.token_type === 'string' ? tokens.token_type : undefined,
|
|
797
|
+
scope: typeof tokens.scope === 'string' ? tokens.scope : requested,
|
|
798
|
+
};
|
|
799
|
+
// The nonce is one-time: it leaves with the handoff it was minted for,
|
|
800
|
+
// so the same handoff presented twice finds nothing to match.
|
|
801
|
+
const next: OAuthBlob = { tokens: stored };
|
|
802
|
+
if (typeof input.account === 'string' && input.account.trim()) meta.account = input.account.trim().slice(0, MAX_LABEL_LEN);
|
|
803
|
+
// Guarded on the ciphertext read above: two handoffs racing for one
|
|
804
|
+
// nonce cannot both land, whichever the provider sent first wins.
|
|
805
|
+
const landed = await this.db
|
|
806
|
+
.update(secrets)
|
|
807
|
+
.set({ valueEncrypted: this.crypto().encrypt(JSON.stringify(next)), oauthMeta: meta, updatedAt: new Date() })
|
|
808
|
+
.where(and(eq(secrets.id, row.id), eq(secrets.valueEncrypted, row.valueEncrypted)))
|
|
809
|
+
.returning({ id: secrets.id });
|
|
810
|
+
if (!(Array.isArray(landed) && landed.length === 0)) break;
|
|
811
|
+
if (attempt + 1 >= HANDOFF_ATTEMPTS) throw refused();
|
|
812
|
+
}
|
|
813
|
+
this.notifyMutation(userId);
|
|
814
|
+
}
|
|
815
|
+
|
|
437
816
|
/**
|
|
438
817
|
* Decrypt+parse an oauth row's token set once, or undefined if the blob can't be
|
|
439
818
|
* read — a corrupted row or rotated encKey must degrade to "not authorized", not throw.
|
|
@@ -571,14 +950,20 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
571
950
|
const blob: OAuthBlob = { clientSecret: provider.clientSecret };
|
|
572
951
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
573
952
|
const oauthMeta = this.publicProviderMeta(provider);
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
.
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
953
|
+
// An owner's own app replacing a managed workspace connection: the
|
|
954
|
+
// connection's tokens are revoked with the provider before they go.
|
|
955
|
+
await this.revokedReplacement(and(isNull(secrets.userId), eq(secrets.key, key)), (guard) =>
|
|
956
|
+
this.db
|
|
957
|
+
.insert(secrets)
|
|
958
|
+
.values({ userId: null, key, kind: 'oauth', label: null, valueEncrypted, oauthMeta })
|
|
959
|
+
.onConflictDoUpdate({
|
|
960
|
+
target: secrets.key,
|
|
961
|
+
targetWhere: isNull(secrets.userId),
|
|
962
|
+
set: { kind: 'oauth', valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
963
|
+
...(guard ? { setWhere: guard } : {}),
|
|
964
|
+
})
|
|
965
|
+
.returning({ id: secrets.id }),
|
|
966
|
+
);
|
|
582
967
|
this.notifyMutation(null);
|
|
583
968
|
}
|
|
584
969
|
|
|
@@ -616,8 +1001,11 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
616
1001
|
}
|
|
617
1002
|
// The save's own validation (https, no private or local address), BEFORE
|
|
618
1003
|
// the caller's row is written or a URL built: a declaration that fails it
|
|
619
|
-
// sends nothing anywhere.
|
|
620
|
-
|
|
1004
|
+
// sends nothing anywhere. A MANAGED declaration names no client of its
|
|
1005
|
+
// own; reaching here means an owner pinned the deployment's own app on
|
|
1006
|
+
// the shared row, and that app's id is the one the flow runs with.
|
|
1007
|
+
const clientId = input.provider.clientId || this.readMeta(shared.oauthMeta).clientId;
|
|
1008
|
+
meta = this.requireProvider({ ...input.provider, clientId, publicClient: false });
|
|
621
1009
|
oauthMeta = this.publicProviderMeta(meta);
|
|
622
1010
|
// A MOVED PROPERTY, decided on the ticket (OAuth-Sign-In-Follows-The-Live-
|
|
623
1011
|
// Declaration, 2026-10-27): the secret used to be bound to the token
|
|
@@ -695,14 +1083,22 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
695
1083
|
pendingScopes,
|
|
696
1084
|
};
|
|
697
1085
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1086
|
+
// A managed sign-in on the caller's row (an owner pinned their own app
|
|
1087
|
+
// after the person connected through the broker) is replaced by this
|
|
1088
|
+
// start: its tokens are told to the provider first, and the write is
|
|
1089
|
+
// guarded on what the revoke read. Tokens carried over above were minted
|
|
1090
|
+
// under THIS configuration, never a managed one.
|
|
1091
|
+
const row = await this.revokedReplacement(and(eq(secrets.userId, userId), eq(secrets.key, key)), (guard) =>
|
|
1092
|
+
this.db
|
|
1093
|
+
.insert(secrets)
|
|
1094
|
+
.values({ userId, key, kind: 'oauth', label: shared.label, valueEncrypted, oauthMeta })
|
|
1095
|
+
.onConflictDoUpdate({
|
|
1096
|
+
target: [secrets.userId, secrets.key],
|
|
1097
|
+
set: { kind: 'oauth', valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
1098
|
+
...(guard ? { setWhere: guard } : {}),
|
|
1099
|
+
})
|
|
1100
|
+
.returning(),
|
|
1101
|
+
);
|
|
706
1102
|
|
|
707
1103
|
// Build the consent URL exactly as beginOAuth does, from the configuration
|
|
708
1104
|
// this start uses, with the requested scopes chosen above.
|
|
@@ -777,10 +1173,16 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
777
1173
|
}
|
|
778
1174
|
|
|
779
1175
|
async forceRefresh(userId: string, key: string): Promise<ForcedRefreshOutcome> {
|
|
1176
|
+
// The caller's own row, or — for a variable the manual declares shared
|
|
1177
|
+
// (a managed workspace connection) — the one shared row every caller
|
|
1178
|
+
// resolves. Same tier rule as `resolve`, so a 401 against a shared
|
|
1179
|
+
// token repairs the row the call actually used.
|
|
1180
|
+
const scope = (await this.scopeOf?.(key)) ?? 'admin';
|
|
1181
|
+
const scopeFilter = scope === 'user' ? eq(secrets.userId, userId) : isNull(secrets.userId);
|
|
780
1182
|
const [row] = await this.db
|
|
781
1183
|
.select()
|
|
782
1184
|
.from(secrets)
|
|
783
|
-
.where(and(
|
|
1185
|
+
.where(and(scopeFilter, eq(secrets.key, key)))
|
|
784
1186
|
.limit(1);
|
|
785
1187
|
if (!row || row.kind !== 'oauth') return 'skipped';
|
|
786
1188
|
let blob: OAuthBlob;
|
|
@@ -827,17 +1229,38 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
827
1229
|
return this.wipeTokens(row, blob);
|
|
828
1230
|
}
|
|
829
1231
|
const meta = this.readMeta(row.oauthMeta);
|
|
830
|
-
const body = new URLSearchParams({
|
|
831
|
-
grant_type: 'refresh_token',
|
|
832
|
-
refresh_token: tokens.refresh_token,
|
|
833
|
-
client_id: meta.clientId,
|
|
834
|
-
});
|
|
835
|
-
if (blob.clientSecret) body.set('client_secret', blob.clientSecret);
|
|
836
|
-
if (meta.resource) body.set('resource', meta.resource);
|
|
837
|
-
|
|
838
1232
|
let refreshed: OAuthTokenSet;
|
|
839
1233
|
try {
|
|
840
|
-
|
|
1234
|
+
if (meta.managed) {
|
|
1235
|
+
// A managed sign-in renews through the broker, which holds the
|
|
1236
|
+
// client secret this deployment never sees. No broker wired is an
|
|
1237
|
+
// outage from here, not a dead grant: the stale token is served and
|
|
1238
|
+
// the next call asks again.
|
|
1239
|
+
if (!this.managedRefresher) return { outcome: 'transient' };
|
|
1240
|
+
// Bounded like a revocation: a broker that never answers is an outage
|
|
1241
|
+
// (the stale token is served) and must not hold every resolution.
|
|
1242
|
+
const delivered = await bounded(
|
|
1243
|
+
this.managedRefresher.refresh(meta.managed, tokens.refresh_token),
|
|
1244
|
+
REFRESH_TIMEOUT_MS,
|
|
1245
|
+
'the broker refresh',
|
|
1246
|
+
);
|
|
1247
|
+
refreshed = {
|
|
1248
|
+
access_token: delivered.access_token,
|
|
1249
|
+
refresh_token: delivered.refresh_token,
|
|
1250
|
+
expires_at: delivered.expires_at,
|
|
1251
|
+
token_type: delivered.token_type,
|
|
1252
|
+
scope: delivered.scope,
|
|
1253
|
+
};
|
|
1254
|
+
} else {
|
|
1255
|
+
const body = new URLSearchParams({
|
|
1256
|
+
grant_type: 'refresh_token',
|
|
1257
|
+
refresh_token: tokens.refresh_token,
|
|
1258
|
+
client_id: meta.clientId,
|
|
1259
|
+
});
|
|
1260
|
+
if (blob.clientSecret) body.set('client_secret', blob.clientSecret);
|
|
1261
|
+
if (meta.resource) body.set('resource', meta.resource);
|
|
1262
|
+
refreshed = await this.tokenRequest(meta.tokenUrl, body);
|
|
1263
|
+
}
|
|
841
1264
|
} catch (err) {
|
|
842
1265
|
// A DEFINITIVE provider rejection (400/401 — invalid_grant and friends)
|
|
843
1266
|
// means the grant is dead: no future refresh will revive it. Wipe the
|
|
@@ -859,7 +1282,12 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
859
1282
|
: result.outcome === 'refreshed'
|
|
860
1283
|
? 'the row had been rotated by a concurrent refresh meanwhile; the rotated sign-in is kept'
|
|
861
1284
|
: 'the row had been removed or replaced meanwhile; nothing wiped';
|
|
862
|
-
|
|
1285
|
+
// A managed row's refusal came from the refresher, whose words are its
|
|
1286
|
+
// own (a port may put the refresh token it was handed in them): only
|
|
1287
|
+
// the status it reported is logged. The vault's own token request
|
|
1288
|
+
// writes fixed sentences, which are safe to repeat.
|
|
1289
|
+
const why = meta.managed ? `the broker refused it (HTTP ${err.providerStatus ?? 'unknown'})` : err.message;
|
|
1290
|
+
log.warn(`refresh refused for "${row.key}", ${did}: ${why}`);
|
|
863
1291
|
return result;
|
|
864
1292
|
}
|
|
865
1293
|
return { outcome: 'transient' };
|
|
@@ -871,7 +1299,10 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
871
1299
|
// rule as the exchange: only an ABSENT field means "unchanged"; an echoed
|
|
872
1300
|
// value, empty included, is the provider's word.
|
|
873
1301
|
if (refreshed.scope === undefined) refreshed.scope = tokens.scope;
|
|
874
|
-
|
|
1302
|
+
// A sign-in in flight on this row (a managed reconnect waiting for its
|
|
1303
|
+
// handoff, a consent waiting for its exchange) survives the refresh: the
|
|
1304
|
+
// nonce or verifier it minted is what that handoff must match.
|
|
1305
|
+
const next: OAuthBlob = { clientSecret: blob.clientSecret, tokens: refreshed, ...this.inFlightOf(blob) };
|
|
875
1306
|
|
|
876
1307
|
// Optimistic concurrency: only persist if the stored ciphertext is unchanged,
|
|
877
1308
|
// so a concurrent refresh on the same row doesn't clobber (Microsoft pattern).
|
|
@@ -918,7 +1349,22 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
918
1349
|
}
|
|
919
1350
|
}
|
|
920
1351
|
|
|
921
|
-
/**
|
|
1352
|
+
/**
|
|
1353
|
+
* What a row holds for a sign-in IN FLIGHT: the one-time nonce a managed
|
|
1354
|
+
* handoff must echo, the PKCE verifier and requested scopes a code exchange
|
|
1355
|
+
* must present. A refresh or a wipe rewrites the row's tokens, never these:
|
|
1356
|
+
* dropping them would refuse the handoff or the exchange that is about to
|
|
1357
|
+
* land, for a reason the person could not see.
|
|
1358
|
+
*/
|
|
1359
|
+
private inFlightOf(blob: OAuthBlob): Pick<OAuthBlob, 'pendingNonce' | 'pendingVerifier' | 'pendingScopes'> {
|
|
1360
|
+
return {
|
|
1361
|
+
...(blob.pendingNonce ? { pendingNonce: blob.pendingNonce } : {}),
|
|
1362
|
+
...(blob.pendingVerifier ? { pendingVerifier: blob.pendingVerifier } : {}),
|
|
1363
|
+
...(blob.pendingScopes ? { pendingScopes: blob.pendingScopes } : {}),
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/** Drop a dead grant's token set, keeping the client secret and any sign-in in flight. See {@link refreshRow}. */
|
|
922
1368
|
private async wipeTokens(
|
|
923
1369
|
row: typeof secrets.$inferSelect,
|
|
924
1370
|
blob: OAuthBlob,
|
|
@@ -926,7 +1372,7 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
926
1372
|
| { outcome: 'refreshed'; accessToken: string; wiped: false }
|
|
927
1373
|
| { outcome: 'rejected'; accessToken: null; wiped: boolean }
|
|
928
1374
|
> {
|
|
929
|
-
const next: OAuthBlob = { clientSecret: blob.clientSecret };
|
|
1375
|
+
const next: OAuthBlob = { clientSecret: blob.clientSecret, ...this.inFlightOf(blob) };
|
|
930
1376
|
// Guarded on the ciphertext we read, so a concurrent refresh that
|
|
931
1377
|
// already persisted ROTATED tokens can't be wiped by our stale
|
|
932
1378
|
// failure. When that guard trips (0 rows), the failure was against a
|
|
@@ -964,17 +1410,21 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
964
1410
|
const blob: OAuthBlob = { clientSecret: provider.clientSecret };
|
|
965
1411
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
966
1412
|
const oauthMeta = this.publicProviderMeta(provider);
|
|
967
|
-
await this.
|
|
968
|
-
.
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1413
|
+
await this.revokedReplacement(and(isNull(secrets.userId), eq(secrets.key, key)), (guard) =>
|
|
1414
|
+
this.db
|
|
1415
|
+
.insert(secrets)
|
|
1416
|
+
.values({ userId: null, key, kind: 'oauth', label, valueEncrypted, oauthMeta })
|
|
1417
|
+
// Preserve an existing row's label on re-upsert (matches
|
|
1418
|
+
// `putSharedOAuthClientSecret`): the optional input label is only for the
|
|
1419
|
+
// FIRST write, so a later re-registration can't null out an owner label.
|
|
1420
|
+
.onConflictDoUpdate({
|
|
1421
|
+
target: secrets.key,
|
|
1422
|
+
targetWhere: isNull(secrets.userId),
|
|
1423
|
+
set: { kind: 'oauth', valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
1424
|
+
...(guard ? { setWhere: guard } : {}),
|
|
1425
|
+
})
|
|
1426
|
+
.returning({ id: secrets.id }),
|
|
1427
|
+
);
|
|
978
1428
|
this.notifyMutation(null);
|
|
979
1429
|
}
|
|
980
1430
|
|
|
@@ -1066,18 +1516,22 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
1066
1516
|
}
|
|
1067
1517
|
|
|
1068
1518
|
private readMeta(raw: unknown): Required<Pick<OAuthProviderConfig, 'authorizationUrl' | 'tokenUrl' | 'clientId'>> &
|
|
1069
|
-
Pick<OAuthProviderConfig, 'scopes' | 'authParams' | 'pkce' | 'publicClient' | 'resource'> {
|
|
1519
|
+
Pick<OAuthProviderConfig, 'scopes' | 'authParams' | 'pkce' | 'publicClient' | 'resource' | 'managed'> {
|
|
1070
1520
|
const meta = (raw ?? {}) as Record<string, unknown>;
|
|
1071
1521
|
const authorizationUrl = typeof meta.authorizationUrl === 'string' ? meta.authorizationUrl : '';
|
|
1072
1522
|
const tokenUrl = typeof meta.tokenUrl === 'string' ? meta.tokenUrl : '';
|
|
1073
1523
|
const clientId = typeof meta.clientId === 'string' ? meta.clientId : '';
|
|
1074
|
-
|
|
1524
|
+
const managed = this.managedRefOf(meta) ?? undefined;
|
|
1525
|
+
// A managed row has no client id of its own (the broker's app is the
|
|
1526
|
+
// client); everything else must name one, or no flow can run from it.
|
|
1527
|
+
if (!authorizationUrl || !tokenUrl || (!clientId && !managed)) {
|
|
1075
1528
|
throw new SecretOAuthError('OAuth secret is missing provider configuration');
|
|
1076
1529
|
}
|
|
1077
1530
|
return {
|
|
1078
1531
|
authorizationUrl,
|
|
1079
1532
|
tokenUrl,
|
|
1080
1533
|
clientId,
|
|
1534
|
+
managed,
|
|
1081
1535
|
scopes: Array.isArray(meta.scopes) ? meta.scopes.map(String) : undefined,
|
|
1082
1536
|
authParams:
|
|
1083
1537
|
meta.authParams && typeof meta.authParams === 'object'
|
|
@@ -1099,16 +1553,27 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
1099
1553
|
...(p.pkce ? { pkce: true } : {}),
|
|
1100
1554
|
...(p.publicClient ? { publicClient: true } : {}),
|
|
1101
1555
|
...(p.resource ? { resource: p.resource } : {}),
|
|
1556
|
+
...(p.managed ? { managed: { connectorId: p.managed.connectorId, scopeSet: p.managed.scopeSet } } : {}),
|
|
1557
|
+
...(p.account ? { account: p.account } : {}),
|
|
1102
1558
|
};
|
|
1103
1559
|
}
|
|
1104
1560
|
|
|
1105
|
-
|
|
1561
|
+
/**
|
|
1562
|
+
* @param opts.managed Accept a MANAGED provider (no client id of its own,
|
|
1563
|
+
* a connector reference instead). Only the managed sign-in paths pass it:
|
|
1564
|
+
* everywhere else a provider needs a client id, and a `managed` field a
|
|
1565
|
+
* caller slipped in is dropped rather than making the row one.
|
|
1566
|
+
*/
|
|
1567
|
+
private requireProvider(p: unknown, opts: { managed?: boolean } = {}): OAuthProviderConfig {
|
|
1106
1568
|
if (!p || typeof p !== 'object') throw new InvalidSecretError('provider is required');
|
|
1107
1569
|
const cfg = p as Record<string, unknown>;
|
|
1108
1570
|
const authorizationUrl = this.requireUrl(cfg.authorizationUrl, 'authorizationUrl');
|
|
1109
1571
|
const tokenUrl = this.requireUrl(cfg.tokenUrl, 'tokenUrl');
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1572
|
+
const managed = opts.managed ? (this.managedRefOf(cfg) ?? undefined) : undefined;
|
|
1573
|
+
// A managed sign-in has no client of its own: whatever id a caller sends
|
|
1574
|
+
// beside the connector reference is not stored, so no row can claim one.
|
|
1575
|
+
const clientId = managed ? '' : typeof cfg.clientId === 'string' ? cfg.clientId.trim() : '';
|
|
1576
|
+
if (!clientId && !managed) throw new InvalidSecretError('clientId is required');
|
|
1112
1577
|
const scopes = Array.isArray(cfg.scopes) ? cfg.scopes.map(String) : undefined;
|
|
1113
1578
|
const authParams =
|
|
1114
1579
|
cfg.authParams && typeof cfg.authParams === 'object'
|
|
@@ -1129,6 +1594,8 @@ export class DbSecretsVaultService implements ISecretsVaultService {
|
|
|
1129
1594
|
typeof cfg.resource === 'string' && cfg.resource.trim()
|
|
1130
1595
|
? this.requireUrl(cfg.resource, 'resource')
|
|
1131
1596
|
: undefined,
|
|
1597
|
+
managed,
|
|
1598
|
+
account: typeof cfg.account === 'string' && cfg.account.trim() ? cfg.account.trim().slice(0, MAX_LABEL_LEN) : undefined,
|
|
1132
1599
|
};
|
|
1133
1600
|
}
|
|
1134
1601
|
|