@bevel-software/platform-core-backend 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-guide/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 +72 -57
- 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/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +11 -0
- package/dist/modules/database/connection.js.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 +51 -26
- package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -1
- package/dist/modules/kb-fs/file-change-notifier.js +6 -8
- package/dist/modules/kb-fs/file-change-notifier.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts +9 -16
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +31 -39
- 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 +5 -7
- 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 +26 -8
- package/dist/modules/version-control/git/git.service.d.ts.map +1 -0
- package/dist/modules/{workflow → version-control}/git/git.service.js +106 -55
- 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} +33 -6
- 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} +20 -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 -46
- 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} +93 -107
- 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/fire-and-forget.d.ts +14 -0
- package/dist/shared/fire-and-forget.d.ts.map +1 -0
- package/dist/shared/fire-and-forget.js +23 -0
- package/dist/shared/fire-and-forget.js.map +1 -0
- 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__/capturing-logger.ts +36 -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__/real-access.ts +16 -0
- package/src/__tests__/real-auth.ts +13 -0
- package/src/__tests__/real-readers.ts +27 -0
- package/src/__tests__/real-version-control.ts +22 -0
- 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__/commit-announcements-wired-once.test.ts +53 -0
- 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 +83 -65
- 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 +23 -23
- 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 +44 -44
- package/src/modules/access/__tests__/roles-admin.security.test.ts +14 -13
- package/src/modules/access/__tests__/roles-admin.service.test.ts +34 -34
- package/src/modules/access/__tests__/roles-capabilities.test.ts +29 -29
- package/src/modules/access/__tests__/roles.routes.test.ts +11 -11
- package/src/modules/access/__tests__/synced-groups-committer.test.ts +14 -14
- package/src/modules/access/__tests__/user-access-removal.service.test.ts +33 -33
- 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__/connection.pg.test.ts +55 -0
- package/src/modules/database/__tests__/connection.test.ts +30 -0
- package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -32
- package/src/modules/database/connection.ts +12 -0
- 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 +48 -62
- 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__/file-change-notifier.test.ts +23 -4
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +264 -305
- package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +103 -40
- 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 +55 -32
- package/src/modules/kb-fs/locking-filesystem.ts +34 -48
- 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 +59 -59
- 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 +7 -9
- 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__/plugin-rename.service.test.ts +46 -21
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +31 -31
- 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} +100 -102
- 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} +85 -75
- 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/version-control/git/__tests__/git.service.commitLanded.test.ts +199 -0
- 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 +99 -22
- 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 +112 -59
- 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} +44 -5
- 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} +96 -109
- 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__/fire-and-forget.test.ts +39 -0
- 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/fire-and-forget.ts +26 -0
- 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
|
@@ -3,12 +3,13 @@ import { isDeepStrictEqual } from 'node:util';
|
|
|
3
3
|
import { logger } from '../../shared/logging.js';
|
|
4
4
|
const log = logger('vault');
|
|
5
5
|
import { and, desc, eq, inArray, isNull, like, or } from 'drizzle-orm';
|
|
6
|
-
import { secrets } from '../database/index.js';
|
|
6
|
+
import { pgErrorCode, secrets } from '../database/index.js';
|
|
7
7
|
import { TokenCrypto } from '../../shared/token-crypto.js';
|
|
8
8
|
import { assertSafeFetchUrl } from '../../shared/ssrf.js';
|
|
9
9
|
import { readCapped } from '../../shared/read-capped.js';
|
|
10
10
|
import { standardTokenErrorCode } from '../../shared/oauth-token-error.js';
|
|
11
11
|
import { isKeyInNamespace, InvalidSecretError, SecretNotFoundError, SecretOAuthError, } from './secrets-vault.contract.js';
|
|
12
|
+
import { timingSafeStringEqual } from '../auth/index.js';
|
|
12
13
|
const MAX_KEY_LEN = 200;
|
|
13
14
|
const MAX_LABEL_LEN = 200;
|
|
14
15
|
const MAX_VALUE_LEN = 100_000;
|
|
@@ -16,6 +17,32 @@ const MAX_VALUE_LEN = 100_000;
|
|
|
16
17
|
const REFRESH_SKEW_MS = 60_000;
|
|
17
18
|
/** Upper bound on a token-endpoint round-trip, so a hung provider can't block callers. */
|
|
18
19
|
const TOKEN_REQUEST_TIMEOUT_MS = 10_000;
|
|
20
|
+
/** Upper bound on telling a provider a managed sign-in is over, so a deletion never waits on it. */
|
|
21
|
+
const REVOKE_TIMEOUT_MS = 10_000;
|
|
22
|
+
/** Upper bound on a broker renewing a managed token: past it the stale token is served and the next call asks again. */
|
|
23
|
+
const REFRESH_TIMEOUT_MS = 15_000;
|
|
24
|
+
/** How many times a guarded replacement re-reads a row that changed under its revoke before giving up. */
|
|
25
|
+
const REPLACE_ATTEMPTS = 3;
|
|
26
|
+
/** How many times a handoff re-reads a row a refresh rotated under it (the nonce rides along) before refusing. */
|
|
27
|
+
const HANDOFF_ATTEMPTS = 3;
|
|
28
|
+
/** `work`, or a rejection once `ms` have passed: a handler that never settles must not hold the vault. */
|
|
29
|
+
async function bounded(work, ms, what) {
|
|
30
|
+
let timer;
|
|
31
|
+
try {
|
|
32
|
+
return await Promise.race([
|
|
33
|
+
work,
|
|
34
|
+
new Promise((_, reject) => {
|
|
35
|
+
timer = setTimeout(() => reject(new Error(`${what} did not settle within ${ms}ms`)), ms);
|
|
36
|
+
}),
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
if (timer)
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** How often a managed sign-in's opening is retried against a row that keeps changing under it. */
|
|
45
|
+
const BEGIN_MANAGED_ATTEMPTS = 3;
|
|
19
46
|
/** How much of a refusing token endpoint's body is read for its error code: an OAuth error object, never a whole error page. */
|
|
20
47
|
const TOKEN_ERROR_BODY_MAX_BYTES = 2048;
|
|
21
48
|
// Hexis's own sentences for a refused token request: what to change, in our
|
|
@@ -111,6 +138,19 @@ export class DbSecretsVaultService {
|
|
|
111
138
|
now;
|
|
112
139
|
scopeOf;
|
|
113
140
|
cryptoInstance = null;
|
|
141
|
+
/**
|
|
142
|
+
* How a MANAGED sign-in's tokens are renewed and revoked — through the
|
|
143
|
+
* broker and the provider respectively. Setter-injected: both depend on
|
|
144
|
+
* the connector catalog, which is built after this service. Without them a
|
|
145
|
+
* managed token that expires is served stale (a transient, retried on the
|
|
146
|
+
* next call) and a disconnect deletes without revoking.
|
|
147
|
+
*/
|
|
148
|
+
managedRefresher;
|
|
149
|
+
managedRevoker;
|
|
150
|
+
setManagedTokenHandlers(handlers) {
|
|
151
|
+
this.managedRefresher = handlers.refresher;
|
|
152
|
+
this.managedRevoker = handlers.revoker;
|
|
153
|
+
}
|
|
114
154
|
constructor(db,
|
|
115
155
|
/** 32-byte AES key (hex/base64); empty disables secret read/write with a clear error. */
|
|
116
156
|
encKey, now = Date.now,
|
|
@@ -178,15 +218,18 @@ export class DbSecretsVaultService {
|
|
|
178
218
|
}
|
|
179
219
|
const label = this.normaliseLabel(input.label);
|
|
180
220
|
const valueEncrypted = this.crypto().encrypt(input.value);
|
|
221
|
+
// The caller's row may hold a managed sign-in: its tokens are told to the
|
|
222
|
+
// provider before this value replaces them, as on every replacement.
|
|
181
223
|
// Upsert on (user_id, key): re-saving a key replaces its value in place.
|
|
182
|
-
const
|
|
224
|
+
const row = await this.revokedReplacement(and(eq(secrets.userId, userId), eq(secrets.key, key)), (guard) => this.db
|
|
183
225
|
.insert(secrets)
|
|
184
226
|
.values({ userId, key, kind: 'static', label, valueEncrypted, oauthMeta: null })
|
|
185
227
|
.onConflictDoUpdate({
|
|
186
228
|
target: [secrets.userId, secrets.key],
|
|
187
229
|
set: { kind: 'static', label, valueEncrypted, oauthMeta: null, updatedAt: new Date() },
|
|
230
|
+
...(guard ? { setWhere: guard } : {}),
|
|
188
231
|
})
|
|
189
|
-
.returning();
|
|
232
|
+
.returning());
|
|
190
233
|
this.notifyMutation(userId);
|
|
191
234
|
return this.toSummary(row);
|
|
192
235
|
}
|
|
@@ -198,7 +241,9 @@ export class DbSecretsVaultService {
|
|
|
198
241
|
const blob = { clientSecret: provider.clientSecret };
|
|
199
242
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
200
243
|
const oauthMeta = this.publicProviderMeta(provider);
|
|
201
|
-
|
|
244
|
+
// A managed sign-in under this key loses its tokens here: the provider is
|
|
245
|
+
// told first, as on every replacement.
|
|
246
|
+
const row = await this.revokedReplacement(and(eq(secrets.userId, userId), eq(secrets.key, key)), (guard) => this.db
|
|
202
247
|
.insert(secrets)
|
|
203
248
|
.values({ userId, key, kind: 'oauth', label, valueEncrypted, oauthMeta })
|
|
204
249
|
.onConflictDoUpdate({
|
|
@@ -206,17 +251,15 @@ export class DbSecretsVaultService {
|
|
|
206
251
|
// Replacing an oauth secret's provider config drops any existing tokens
|
|
207
252
|
// (they belonged to the old client) — the user must re-authorize.
|
|
208
253
|
set: { kind: 'oauth', label, valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
254
|
+
...(guard ? { setWhere: guard } : {}),
|
|
209
255
|
})
|
|
210
|
-
.returning();
|
|
256
|
+
.returning());
|
|
211
257
|
this.notifyMutation(userId);
|
|
212
258
|
return this.toSummary(row);
|
|
213
259
|
}
|
|
214
260
|
async remove(userId, id) {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
.where(and(eq(secrets.id, id), eq(secrets.userId, userId)))
|
|
218
|
-
.returning({ id: secrets.id });
|
|
219
|
-
if (res.length === 0)
|
|
261
|
+
const gone = await this.revokeThenDelete(and(eq(secrets.id, id), eq(secrets.userId, userId)));
|
|
262
|
+
if (gone.length === 0)
|
|
220
263
|
throw new SecretNotFoundError(id);
|
|
221
264
|
this.notifyMutation(userId);
|
|
222
265
|
}
|
|
@@ -230,38 +273,186 @@ export class DbSecretsVaultService {
|
|
|
230
273
|
}
|
|
231
274
|
const label = this.normaliseLabel(input.label);
|
|
232
275
|
const valueEncrypted = this.crypto().encrypt(input.value);
|
|
276
|
+
// A shared row that held a managed workspace connection is told to its
|
|
277
|
+
// provider before this value replaces it; the tokens would otherwise go
|
|
278
|
+
// unrevoked with the row's kind.
|
|
233
279
|
// Upsert the ONE shared row for this key (user_id NULL). The conflict target
|
|
234
280
|
// is the partial unique index `secrets_shared_key_unq` — hence `targetWhere`.
|
|
235
|
-
const
|
|
281
|
+
const row = await this.revokedReplacement(and(isNull(secrets.userId), eq(secrets.key, key)), (guard) => this.db
|
|
236
282
|
.insert(secrets)
|
|
237
283
|
.values({ userId: null, key, kind: 'static', label, valueEncrypted, oauthMeta: null })
|
|
238
284
|
.onConflictDoUpdate({
|
|
239
285
|
target: secrets.key,
|
|
240
286
|
targetWhere: isNull(secrets.userId),
|
|
241
287
|
set: { kind: 'static', label, valueEncrypted, oauthMeta: null, updatedAt: new Date() },
|
|
288
|
+
...(guard ? { setWhere: guard } : {}),
|
|
242
289
|
})
|
|
243
|
-
.returning();
|
|
290
|
+
.returning());
|
|
244
291
|
this.notifyMutation(null);
|
|
245
292
|
return this.toSummary(row);
|
|
246
293
|
}
|
|
247
294
|
async removeShared(key) {
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
.where(and(isNull(secrets.userId), eq(secrets.key, key)))
|
|
251
|
-
.returning({ id: secrets.id });
|
|
252
|
-
if (res.length === 0)
|
|
295
|
+
const gone = await this.revokeThenDelete(and(isNull(secrets.userId), eq(secrets.key, key)));
|
|
296
|
+
if (gone.length === 0)
|
|
253
297
|
throw new SecretNotFoundError(key);
|
|
254
298
|
this.notifyMutation(null);
|
|
255
299
|
}
|
|
256
300
|
async removeUserByKey(userId, key) {
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
.where(and(eq(secrets.userId, userId), eq(secrets.key, key)))
|
|
260
|
-
.returning({ id: secrets.id });
|
|
261
|
-
if (res.length === 0)
|
|
301
|
+
const gone = await this.revokeThenDelete(and(eq(secrets.userId, userId), eq(secrets.key, key)));
|
|
302
|
+
if (gone.length === 0)
|
|
262
303
|
throw new SecretNotFoundError(key);
|
|
263
304
|
this.notifyMutation(userId);
|
|
264
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Delete the rows `where` names, each told to its provider first when it
|
|
308
|
+
* is a managed sign-in with tokens (see {@link revokeRow}). The delete is
|
|
309
|
+
* guarded on the ciphertext the revoke read: a handoff or a refresh landing
|
|
310
|
+
* in between has replaced the tokens, and deleting THOSE unrevoked is the
|
|
311
|
+
* leak the revoke exists to prevent. A miss re-reads the row and goes
|
|
312
|
+
* again, bounded; what could not be settled in those passes is left
|
|
313
|
+
* standing rather than deleted blind.
|
|
314
|
+
*/
|
|
315
|
+
async revokeThenDelete(where) {
|
|
316
|
+
// No revoker, nothing to read first: one plain delete, as every removal
|
|
317
|
+
// was before managed sign-ins, and no miss to re-read for.
|
|
318
|
+
if (!this.managedRevoker) {
|
|
319
|
+
return this.db.delete(secrets).where(where).returning({ id: secrets.id, userId: secrets.userId, kind: secrets.kind });
|
|
320
|
+
}
|
|
321
|
+
const gone = [];
|
|
322
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
323
|
+
const rows = await this.db.select().from(secrets).where(where);
|
|
324
|
+
if (rows.length === 0)
|
|
325
|
+
break;
|
|
326
|
+
// Every row's revocation at once, so a bulk delete (a namespace, a
|
|
327
|
+
// person's connections) waits one revocation's bound, not one per row.
|
|
328
|
+
await Promise.all(rows.map((row) => this.revokeRow(row)));
|
|
329
|
+
let missed = false;
|
|
330
|
+
for (const row of rows) {
|
|
331
|
+
const deleted = await this.db
|
|
332
|
+
.delete(secrets)
|
|
333
|
+
.where(and(eq(secrets.id, row.id), eq(secrets.valueEncrypted, row.valueEncrypted)))
|
|
334
|
+
.returning({ id: secrets.id, userId: secrets.userId, kind: secrets.kind });
|
|
335
|
+
if (deleted.length === 0)
|
|
336
|
+
missed = true;
|
|
337
|
+
else
|
|
338
|
+
gone.push(...deleted);
|
|
339
|
+
}
|
|
340
|
+
if (!missed)
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
return gone;
|
|
344
|
+
}
|
|
345
|
+
async removeManagedRowsOfUser(userId, within) {
|
|
346
|
+
if (within)
|
|
347
|
+
return this.removeManagedRowsLocked(userId, within);
|
|
348
|
+
// Every oauth row of the user whose meta names a managed app. Read
|
|
349
|
+
// first (the managed mark lives in JSON the WHERE cannot see), revoked,
|
|
350
|
+
// then deleted by id; the per-user filter on the delete is what keeps a
|
|
351
|
+
// row read for this user from ever deleting another's.
|
|
352
|
+
const rows = await this.db
|
|
353
|
+
.select()
|
|
354
|
+
.from(secrets)
|
|
355
|
+
.where(and(eq(secrets.userId, userId), eq(secrets.kind, 'oauth')));
|
|
356
|
+
const managed = rows.filter((r) => this.managedRefOf(r.oauthMeta) !== null);
|
|
357
|
+
if (managed.length === 0)
|
|
358
|
+
return 0;
|
|
359
|
+
const deleted = await this.revokeThenDelete(and(eq(secrets.userId, userId), inArray(secrets.id, managed.map((r) => r.id))));
|
|
360
|
+
if (deleted.length > 0)
|
|
361
|
+
this.notifyMutation(userId);
|
|
362
|
+
// A row the guarded delete could not settle (its tokens kept changing
|
|
363
|
+
// under the revoke) is still here, unrevoked. The caller is told rather
|
|
364
|
+
// than left to believe the person holds nothing: an erasure refuses and
|
|
365
|
+
// is retried, a switch-off logs it and the row is refused on use.
|
|
366
|
+
if (deleted.length < managed.length) {
|
|
367
|
+
throw new SecretOAuthError(`${managed.length - deleted.length} managed sign-in(s) kept changing while being revoked and were not removed; try again`);
|
|
368
|
+
}
|
|
369
|
+
return deleted.length;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* The same removal inside a caller's transaction (an account erasure, just
|
|
373
|
+
* before the user row goes): the rows are LOCKED for the transaction, so a
|
|
374
|
+
* handoff or a refresh arriving now waits and then finds no row, which is
|
|
375
|
+
* what makes the revoke final. No guard is needed under the lock; the
|
|
376
|
+
* revoke stays bounded (see {@link revokeRow}), so the transaction never
|
|
377
|
+
* waits on a provider longer than that.
|
|
378
|
+
*/
|
|
379
|
+
async removeManagedRowsLocked(userId, tx) {
|
|
380
|
+
const rows = await tx
|
|
381
|
+
.select()
|
|
382
|
+
.from(secrets)
|
|
383
|
+
.where(and(eq(secrets.userId, userId), eq(secrets.kind, 'oauth')))
|
|
384
|
+
.for('update');
|
|
385
|
+
const managed = rows.filter((r) => this.managedRefOf(r.oauthMeta) !== null);
|
|
386
|
+
if (managed.length === 0)
|
|
387
|
+
return 0;
|
|
388
|
+
await Promise.all(managed.map((row) => this.revokeRow(row)));
|
|
389
|
+
await tx.delete(secrets).where(and(eq(secrets.userId, userId), inArray(secrets.id, managed.map((r) => r.id))));
|
|
390
|
+
this.notifyMutation(userId);
|
|
391
|
+
return managed.length;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Replace what `where` names with `write`, a managed sign-in's tokens told
|
|
395
|
+
* to their provider first (see {@link revokeRow}; best effort, never holding
|
|
396
|
+
* the user's decision). The write is guarded on the ciphertext the revoke
|
|
397
|
+
* read (`setWhere`): a refresh or a handoff landing in between has rotated
|
|
398
|
+
* the tokens, and writing over THOSE unrevoked is the leak the revoke exists
|
|
399
|
+
* to prevent — as the guarded delete of {@link revokeThenDelete}. A miss
|
|
400
|
+
* re-reads and goes again, bounded; a row that kept changing is left as it
|
|
401
|
+
* is and the caller is told. With no revoker wired there is nothing to read
|
|
402
|
+
* first: one plain write, as every replacement was before managed sign-ins.
|
|
403
|
+
*/
|
|
404
|
+
async revokedReplacement(where, write) {
|
|
405
|
+
if (!this.managedRevoker)
|
|
406
|
+
return (await write())[0];
|
|
407
|
+
for (let attempt = 0; attempt < REPLACE_ATTEMPTS; attempt++) {
|
|
408
|
+
const rows = await this.db.select().from(secrets).where(where);
|
|
409
|
+
await Promise.all(rows.map((row) => this.revokeRow(row)));
|
|
410
|
+
// The row as it was read, or no row at all: a conflict with anything
|
|
411
|
+
// else (a row written meanwhile) is a miss, and the next pass reads it.
|
|
412
|
+
const guard = rows.length > 0
|
|
413
|
+
? inArray(secrets.valueEncrypted, rows.map((row) => row.valueEncrypted))
|
|
414
|
+
: eq(secrets.valueEncrypted, '');
|
|
415
|
+
const written = await write(guard);
|
|
416
|
+
if (written.length > 0)
|
|
417
|
+
return written[0];
|
|
418
|
+
}
|
|
419
|
+
throw new SecretOAuthError('the sign-in kept changing while being replaced; try again');
|
|
420
|
+
}
|
|
421
|
+
async revokeRow(row) {
|
|
422
|
+
if (!this.managedRevoker || row.kind !== 'oauth')
|
|
423
|
+
return;
|
|
424
|
+
const ref = this.managedRefOf(row.oauthMeta);
|
|
425
|
+
if (!ref)
|
|
426
|
+
return;
|
|
427
|
+
const tokens = this.readTokensSafe(row.valueEncrypted);
|
|
428
|
+
if (!tokens?.access_token)
|
|
429
|
+
return;
|
|
430
|
+
// Bounded: the delete that follows is the user's decision, and a revoker
|
|
431
|
+
// that never settles must not hold it.
|
|
432
|
+
try {
|
|
433
|
+
await bounded(this.managedRevoker.revoke(ref, { access_token: tokens.access_token, refresh_token: tokens.refresh_token }), REVOKE_TIMEOUT_MS, 'revocation');
|
|
434
|
+
}
|
|
435
|
+
catch (err) {
|
|
436
|
+
// The key and the connector name the sign-in. The error itself stays
|
|
437
|
+
// out: a revoker's message is its own, and this line is the one place a
|
|
438
|
+
// token could otherwise reach a log from this path.
|
|
439
|
+
log.warn('revoking a managed sign-in with its provider failed; deleting it here anyway', {
|
|
440
|
+
key: row.key,
|
|
441
|
+
connector: ref.connectorId,
|
|
442
|
+
failure: err instanceof Error ? err.name : typeof err,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/** The managed reference a row's public meta carries, or null for an ordinary sign-in. */
|
|
447
|
+
managedRefOf(raw) {
|
|
448
|
+
const meta = (raw ?? {});
|
|
449
|
+
const m = meta.managed;
|
|
450
|
+
if (!m || typeof m !== 'object')
|
|
451
|
+
return null;
|
|
452
|
+
const connectorId = typeof m.connectorId === 'string' ? m.connectorId : '';
|
|
453
|
+
const scopeSet = typeof m.scopeSet === 'string' ? m.scopeSet : '';
|
|
454
|
+
return connectorId && scopeSet ? { connectorId, scopeSet } : null;
|
|
455
|
+
}
|
|
265
456
|
async countNamespace(prefix) {
|
|
266
457
|
return tally(await this.namespaceRows(prefix));
|
|
267
458
|
}
|
|
@@ -282,10 +473,10 @@ export class DbSecretsVaultService {
|
|
|
282
473
|
const rows = await this.namespaceRows(prefix);
|
|
283
474
|
if (rows.length === 0)
|
|
284
475
|
break;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
476
|
+
// A managed sign-in under the namespace is told to its provider
|
|
477
|
+
// before the delete, for the same reason the per-row removals do —
|
|
478
|
+
// and guarded the same way, when a revoker is there to need it.
|
|
479
|
+
const deleted = await this.revokeThenDelete(inArray(secrets.id, rows.map((r) => r.id)));
|
|
289
480
|
gone.push(...deleted);
|
|
290
481
|
}
|
|
291
482
|
}
|
|
@@ -335,6 +526,7 @@ export class DbSecretsVaultService {
|
|
|
335
526
|
userId: secrets.userId,
|
|
336
527
|
kind: secrets.kind,
|
|
337
528
|
valueEncrypted: secrets.valueEncrypted,
|
|
529
|
+
oauthMeta: secrets.oauthMeta,
|
|
338
530
|
})
|
|
339
531
|
.from(secrets)
|
|
340
532
|
.where(and(inArray(secrets.key, keys), or(isNull(secrets.userId), eq(secrets.userId, userId))));
|
|
@@ -343,6 +535,15 @@ export class DbSecretsVaultService {
|
|
|
343
535
|
const adminRow = rows.find((r) => r.key === key && r.userId === null);
|
|
344
536
|
// Decrypt+parse the oauth row's token set once, then derive both fields from it.
|
|
345
537
|
const tokens = userRow?.kind === 'oauth' ? this.readTokensSafe(userRow.valueEncrypted) : undefined;
|
|
538
|
+
// The shared row holds tokens only for a managed workspace connection;
|
|
539
|
+
// an ordinary sign-in's shared row is the client secret and reads as
|
|
540
|
+
// "no tokens" here, which is the truthful answer for it.
|
|
541
|
+
const adminTokens = adminRow?.kind === 'oauth' ? this.readTokensSafe(adminRow.valueEncrypted) : undefined;
|
|
542
|
+
const adminMeta = (adminRow?.oauthMeta ?? null);
|
|
543
|
+
const accountOf = (meta) => {
|
|
544
|
+
const a = meta?.account;
|
|
545
|
+
return typeof a === 'string' && a ? a : undefined;
|
|
546
|
+
};
|
|
346
547
|
return {
|
|
347
548
|
key,
|
|
348
549
|
adminConfigured: !!adminRow,
|
|
@@ -356,9 +557,154 @@ export class DbSecretsVaultService {
|
|
|
356
557
|
// Only meaningful for an oauth row: the scopes the caller's token was granted,
|
|
357
558
|
// so a coverage check can tell whether it still covers the tool's live scopes.
|
|
358
559
|
grantedScopes: userRow?.kind === 'oauth' ? tokens?.scope : undefined,
|
|
560
|
+
adminAuthorized: adminRow?.kind === 'oauth' ? Boolean(adminTokens?.access_token) : undefined,
|
|
561
|
+
adminGrantedScopes: adminRow?.kind === 'oauth' ? adminTokens?.scope : undefined,
|
|
562
|
+
userAccount: userRow?.kind === 'oauth' ? accountOf(userRow.oauthMeta) : undefined,
|
|
563
|
+
adminAccount: adminRow?.kind === 'oauth' ? accountOf(adminMeta) : undefined,
|
|
564
|
+
// An owner's own app on the shared row: a client id without the managed mark.
|
|
565
|
+
adminBringsOwnApp: adminRow?.kind === 'oauth'
|
|
566
|
+
? this.managedRefOf(adminMeta) === null && typeof adminMeta?.clientId === 'string' && adminMeta.clientId !== ''
|
|
567
|
+
: undefined,
|
|
359
568
|
};
|
|
360
569
|
});
|
|
361
570
|
}
|
|
571
|
+
async beginManagedConnection(input) {
|
|
572
|
+
const key = this.requireKey(input.key);
|
|
573
|
+
const userId = input.userId === null ? null : this.requireUserId(input.userId);
|
|
574
|
+
const ref = input.provider.managed;
|
|
575
|
+
if (!ref)
|
|
576
|
+
throw new InvalidSecretError('a managed sign-in needs its connector reference');
|
|
577
|
+
if (typeof input.nonce !== 'string' || input.nonce.length < 16)
|
|
578
|
+
throw new InvalidSecretError('nonce is required');
|
|
579
|
+
const oauthMeta = this.publicProviderMeta(this.requireProvider(input.provider, { managed: true }));
|
|
580
|
+
const where = userId === null ? and(isNull(secrets.userId), eq(secrets.key, key)) : and(eq(secrets.userId, userId), eq(secrets.key, key));
|
|
581
|
+
// Read-modify-write on a LIVE credential: a refresh running concurrently
|
|
582
|
+
// may rotate the tokens between the read and the write, and writing our
|
|
583
|
+
// copy over them would leave the row holding a dead refresh token. So the
|
|
584
|
+
// update is guarded on the ciphertext read, and a miss re-reads and
|
|
585
|
+
// rebuilds from the fresh row; bounded, so a row that keeps changing
|
|
586
|
+
// fails loudly rather than forever. The same rule `beginOAuth` follows.
|
|
587
|
+
for (let attempt = 0; attempt < BEGIN_MANAGED_ATTEMPTS; attempt++) {
|
|
588
|
+
const [existing] = await this.db.select().from(secrets).where(where).limit(1);
|
|
589
|
+
// Keep what the SAME connection already holds (its tokens, so an
|
|
590
|
+
// abandoned reconnect costs nothing). The same connection means the same
|
|
591
|
+
// connector and scope set: a row of another connector's, or of another
|
|
592
|
+
// set's, holds a token for something else, and a declaration that
|
|
593
|
+
// changed must not hand it to the new one. Start clean over anything
|
|
594
|
+
// else too — a static value or an owner-app sign-in under this key.
|
|
595
|
+
const storedRef = existing?.kind === 'oauth' ? this.managedRefOf(existing.oauthMeta) : null;
|
|
596
|
+
const sameConnection = storedRef !== null && storedRef.connectorId === ref.connectorId && storedRef.scopeSet === ref.scopeSet;
|
|
597
|
+
let kept = {};
|
|
598
|
+
if (existing && sameConnection) {
|
|
599
|
+
try {
|
|
600
|
+
kept = this.readBlob(existing.valueEncrypted);
|
|
601
|
+
}
|
|
602
|
+
catch {
|
|
603
|
+
kept = {};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const next = { tokens: kept.tokens, pendingNonce: input.nonce };
|
|
607
|
+
const valueEncrypted = this.crypto().encrypt(JSON.stringify(next));
|
|
608
|
+
const account = sameConnection ? existing.oauthMeta?.account : undefined;
|
|
609
|
+
const meta = typeof account === 'string' && account ? { ...oauthMeta, account } : oauthMeta;
|
|
610
|
+
if (!existing) {
|
|
611
|
+
try {
|
|
612
|
+
await this.db.insert(secrets).values({ userId, key, kind: 'oauth', label: null, valueEncrypted, oauthMeta: meta });
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
catch (err) {
|
|
616
|
+
// Another start of the same sign-in inserted the row first: the
|
|
617
|
+
// next pass finds it and updates it, as it would any existing row.
|
|
618
|
+
if (pgErrorCode(err) === '23505')
|
|
619
|
+
continue; // unique_violation
|
|
620
|
+
throw err;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
// A row that held ANOTHER connection's tokens is about to lose them:
|
|
624
|
+
// the provider is told first, as on every other replacement.
|
|
625
|
+
if (!sameConnection && storedRef)
|
|
626
|
+
await this.revokeRow(existing);
|
|
627
|
+
const written = await this.db
|
|
628
|
+
.update(secrets)
|
|
629
|
+
.set({ kind: 'oauth', valueEncrypted, oauthMeta: meta, updatedAt: new Date() })
|
|
630
|
+
.where(and(eq(secrets.id, existing.id), eq(secrets.valueEncrypted, existing.valueEncrypted)))
|
|
631
|
+
.returning({ id: secrets.id });
|
|
632
|
+
if (Array.isArray(written) && written.length === 0)
|
|
633
|
+
continue;
|
|
634
|
+
// A row that held something ELSE (a value, another connection's
|
|
635
|
+
// tokens, an owner's app) no longer does: caches keyed on it are told.
|
|
636
|
+
// The same connection's tokens carried over unchanged tell nobody.
|
|
637
|
+
if (!kept.tokens)
|
|
638
|
+
this.notifyMutation(userId);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
throw new SecretOAuthError('The sign-in could not be opened: its stored credential kept changing. Try again.');
|
|
642
|
+
}
|
|
643
|
+
async completeManagedConnection(input) {
|
|
644
|
+
const key = this.requireKey(input.key);
|
|
645
|
+
const userId = input.userId === null ? null : this.requireUserId(input.userId);
|
|
646
|
+
const where = userId === null ? and(isNull(secrets.userId), eq(secrets.key, key)) : and(eq(secrets.userId, userId), eq(secrets.key, key));
|
|
647
|
+
// Every refusal below is the same words: a caller that can tell "no such
|
|
648
|
+
// sign-in" from "wrong nonce" from "already used" has learned which rows
|
|
649
|
+
// exist, and the handoff route is reachable with the broker secret alone.
|
|
650
|
+
const refused = () => new SecretOAuthError('No managed sign-in is waiting for this handoff');
|
|
651
|
+
const tokens = input.tokens;
|
|
652
|
+
if (typeof tokens?.access_token !== 'string' || !tokens.access_token) {
|
|
653
|
+
throw new InvalidSecretError('the handoff carries no access token');
|
|
654
|
+
}
|
|
655
|
+
// Read, check the nonce, write guarded on what was read; and again when
|
|
656
|
+
// the write misses. A miss with the nonce still on the row is a refresh
|
|
657
|
+
// that rotated the row's tokens meanwhile (a refresh carries the nonce
|
|
658
|
+
// across, see `inFlightOf`), not a second handoff: the broker delivered
|
|
659
|
+
// these tokens once, and refusing them would lose a valid sign-in for
|
|
660
|
+
// good. A row whose nonce is gone or changed refuses on the re-read.
|
|
661
|
+
for (let attempt = 0;; attempt++) {
|
|
662
|
+
const [row] = await this.db.select().from(secrets).where(where).limit(1);
|
|
663
|
+
if (!row || row.kind !== 'oauth' || !this.managedRefOf(row.oauthMeta))
|
|
664
|
+
throw refused();
|
|
665
|
+
let blob;
|
|
666
|
+
try {
|
|
667
|
+
blob = this.readBlob(row.valueEncrypted);
|
|
668
|
+
}
|
|
669
|
+
catch {
|
|
670
|
+
throw refused();
|
|
671
|
+
}
|
|
672
|
+
if (!blob.pendingNonce || typeof input.nonce !== 'string' || !timingSafeStringEqual(input.nonce, blob.pendingNonce)) {
|
|
673
|
+
throw refused();
|
|
674
|
+
}
|
|
675
|
+
const meta = { ...(row.oauthMeta ?? {}) };
|
|
676
|
+
// A broker that echoes no `scope` delivered what the sign-in asked for:
|
|
677
|
+
// the scopes the row was opened with stand in, as `pendingScopes` does
|
|
678
|
+
// for the code exchange. Stored as unknown, the grant would count as
|
|
679
|
+
// covering nothing, and the sign-in would read as under-scoped for good.
|
|
680
|
+
// An EMPTY scope is not "none echoed": it is an empty grant, kept as such.
|
|
681
|
+
const requested = Array.isArray(meta.scopes) && meta.scopes.length > 0 ? meta.scopes.map(String).join(' ') : undefined;
|
|
682
|
+
const stored = {
|
|
683
|
+
access_token: tokens.access_token,
|
|
684
|
+
refresh_token: typeof tokens.refresh_token === 'string' ? tokens.refresh_token : undefined,
|
|
685
|
+
expires_at: typeof tokens.expires_at === 'number' ? tokens.expires_at : undefined,
|
|
686
|
+
token_type: typeof tokens.token_type === 'string' ? tokens.token_type : undefined,
|
|
687
|
+
scope: typeof tokens.scope === 'string' ? tokens.scope : requested,
|
|
688
|
+
};
|
|
689
|
+
// The nonce is one-time: it leaves with the handoff it was minted for,
|
|
690
|
+
// so the same handoff presented twice finds nothing to match.
|
|
691
|
+
const next = { tokens: stored };
|
|
692
|
+
if (typeof input.account === 'string' && input.account.trim())
|
|
693
|
+
meta.account = input.account.trim().slice(0, MAX_LABEL_LEN);
|
|
694
|
+
// Guarded on the ciphertext read above: two handoffs racing for one
|
|
695
|
+
// nonce cannot both land, whichever the provider sent first wins.
|
|
696
|
+
const landed = await this.db
|
|
697
|
+
.update(secrets)
|
|
698
|
+
.set({ valueEncrypted: this.crypto().encrypt(JSON.stringify(next)), oauthMeta: meta, updatedAt: new Date() })
|
|
699
|
+
.where(and(eq(secrets.id, row.id), eq(secrets.valueEncrypted, row.valueEncrypted)))
|
|
700
|
+
.returning({ id: secrets.id });
|
|
701
|
+
if (!(Array.isArray(landed) && landed.length === 0))
|
|
702
|
+
break;
|
|
703
|
+
if (attempt + 1 >= HANDOFF_ATTEMPTS)
|
|
704
|
+
throw refused();
|
|
705
|
+
}
|
|
706
|
+
this.notifyMutation(userId);
|
|
707
|
+
}
|
|
362
708
|
/**
|
|
363
709
|
* Decrypt+parse an oauth row's token set once, or undefined if the blob can't be
|
|
364
710
|
* read — a corrupted row or rotated encKey must degrade to "not authorized", not throw.
|
|
@@ -495,14 +841,18 @@ export class DbSecretsVaultService {
|
|
|
495
841
|
const blob = { clientSecret: provider.clientSecret };
|
|
496
842
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
497
843
|
const oauthMeta = this.publicProviderMeta(provider);
|
|
498
|
-
|
|
844
|
+
// An owner's own app replacing a managed workspace connection: the
|
|
845
|
+
// connection's tokens are revoked with the provider before they go.
|
|
846
|
+
await this.revokedReplacement(and(isNull(secrets.userId), eq(secrets.key, key)), (guard) => this.db
|
|
499
847
|
.insert(secrets)
|
|
500
848
|
.values({ userId: null, key, kind: 'oauth', label: null, valueEncrypted, oauthMeta })
|
|
501
849
|
.onConflictDoUpdate({
|
|
502
850
|
target: secrets.key,
|
|
503
851
|
targetWhere: isNull(secrets.userId),
|
|
504
852
|
set: { kind: 'oauth', valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
505
|
-
|
|
853
|
+
...(guard ? { setWhere: guard } : {}),
|
|
854
|
+
})
|
|
855
|
+
.returning({ id: secrets.id }));
|
|
506
856
|
this.notifyMutation(null);
|
|
507
857
|
}
|
|
508
858
|
async beginToolOAuthByKey(input) {
|
|
@@ -532,8 +882,11 @@ export class DbSecretsVaultService {
|
|
|
532
882
|
}
|
|
533
883
|
// The save's own validation (https, no private or local address), BEFORE
|
|
534
884
|
// the caller's row is written or a URL built: a declaration that fails it
|
|
535
|
-
// sends nothing anywhere.
|
|
536
|
-
|
|
885
|
+
// sends nothing anywhere. A MANAGED declaration names no client of its
|
|
886
|
+
// own; reaching here means an owner pinned the deployment's own app on
|
|
887
|
+
// the shared row, and that app's id is the one the flow runs with.
|
|
888
|
+
const clientId = input.provider.clientId || this.readMeta(shared.oauthMeta).clientId;
|
|
889
|
+
meta = this.requireProvider({ ...input.provider, clientId, publicClient: false });
|
|
537
890
|
oauthMeta = this.publicProviderMeta(meta);
|
|
538
891
|
// A MOVED PROPERTY, decided on the ticket (OAuth-Sign-In-Follows-The-Live-
|
|
539
892
|
// Declaration, 2026-10-27): the secret used to be bound to the token
|
|
@@ -610,14 +963,20 @@ export class DbSecretsVaultService {
|
|
|
610
963
|
pendingScopes,
|
|
611
964
|
};
|
|
612
965
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
613
|
-
|
|
966
|
+
// A managed sign-in on the caller's row (an owner pinned their own app
|
|
967
|
+
// after the person connected through the broker) is replaced by this
|
|
968
|
+
// start: its tokens are told to the provider first, and the write is
|
|
969
|
+
// guarded on what the revoke read. Tokens carried over above were minted
|
|
970
|
+
// under THIS configuration, never a managed one.
|
|
971
|
+
const row = await this.revokedReplacement(and(eq(secrets.userId, userId), eq(secrets.key, key)), (guard) => this.db
|
|
614
972
|
.insert(secrets)
|
|
615
973
|
.values({ userId, key, kind: 'oauth', label: shared.label, valueEncrypted, oauthMeta })
|
|
616
974
|
.onConflictDoUpdate({
|
|
617
975
|
target: [secrets.userId, secrets.key],
|
|
618
976
|
set: { kind: 'oauth', valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
977
|
+
...(guard ? { setWhere: guard } : {}),
|
|
619
978
|
})
|
|
620
|
-
.returning();
|
|
979
|
+
.returning());
|
|
621
980
|
// Build the consent URL exactly as beginOAuth does, from the configuration
|
|
622
981
|
// this start uses, with the requested scopes chosen above.
|
|
623
982
|
const url = new URL(meta.authorizationUrl);
|
|
@@ -695,10 +1054,16 @@ export class DbSecretsVaultService {
|
|
|
695
1054
|
return result.accessToken;
|
|
696
1055
|
}
|
|
697
1056
|
async forceRefresh(userId, key) {
|
|
1057
|
+
// The caller's own row, or — for a variable the manual declares shared
|
|
1058
|
+
// (a managed workspace connection) — the one shared row every caller
|
|
1059
|
+
// resolves. Same tier rule as `resolve`, so a 401 against a shared
|
|
1060
|
+
// token repairs the row the call actually used.
|
|
1061
|
+
const scope = (await this.scopeOf?.(key)) ?? 'admin';
|
|
1062
|
+
const scopeFilter = scope === 'user' ? eq(secrets.userId, userId) : isNull(secrets.userId);
|
|
698
1063
|
const [row] = await this.db
|
|
699
1064
|
.select()
|
|
700
1065
|
.from(secrets)
|
|
701
|
-
.where(and(
|
|
1066
|
+
.where(and(scopeFilter, eq(secrets.key, key)))
|
|
702
1067
|
.limit(1);
|
|
703
1068
|
if (!row || row.kind !== 'oauth')
|
|
704
1069
|
return 'skipped';
|
|
@@ -739,18 +1104,38 @@ export class DbSecretsVaultService {
|
|
|
739
1104
|
return this.wipeTokens(row, blob);
|
|
740
1105
|
}
|
|
741
1106
|
const meta = this.readMeta(row.oauthMeta);
|
|
742
|
-
const body = new URLSearchParams({
|
|
743
|
-
grant_type: 'refresh_token',
|
|
744
|
-
refresh_token: tokens.refresh_token,
|
|
745
|
-
client_id: meta.clientId,
|
|
746
|
-
});
|
|
747
|
-
if (blob.clientSecret)
|
|
748
|
-
body.set('client_secret', blob.clientSecret);
|
|
749
|
-
if (meta.resource)
|
|
750
|
-
body.set('resource', meta.resource);
|
|
751
1107
|
let refreshed;
|
|
752
1108
|
try {
|
|
753
|
-
|
|
1109
|
+
if (meta.managed) {
|
|
1110
|
+
// A managed sign-in renews through the broker, which holds the
|
|
1111
|
+
// client secret this deployment never sees. No broker wired is an
|
|
1112
|
+
// outage from here, not a dead grant: the stale token is served and
|
|
1113
|
+
// the next call asks again.
|
|
1114
|
+
if (!this.managedRefresher)
|
|
1115
|
+
return { outcome: 'transient' };
|
|
1116
|
+
// Bounded like a revocation: a broker that never answers is an outage
|
|
1117
|
+
// (the stale token is served) and must not hold every resolution.
|
|
1118
|
+
const delivered = await bounded(this.managedRefresher.refresh(meta.managed, tokens.refresh_token), REFRESH_TIMEOUT_MS, 'the broker refresh');
|
|
1119
|
+
refreshed = {
|
|
1120
|
+
access_token: delivered.access_token,
|
|
1121
|
+
refresh_token: delivered.refresh_token,
|
|
1122
|
+
expires_at: delivered.expires_at,
|
|
1123
|
+
token_type: delivered.token_type,
|
|
1124
|
+
scope: delivered.scope,
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
else {
|
|
1128
|
+
const body = new URLSearchParams({
|
|
1129
|
+
grant_type: 'refresh_token',
|
|
1130
|
+
refresh_token: tokens.refresh_token,
|
|
1131
|
+
client_id: meta.clientId,
|
|
1132
|
+
});
|
|
1133
|
+
if (blob.clientSecret)
|
|
1134
|
+
body.set('client_secret', blob.clientSecret);
|
|
1135
|
+
if (meta.resource)
|
|
1136
|
+
body.set('resource', meta.resource);
|
|
1137
|
+
refreshed = await this.tokenRequest(meta.tokenUrl, body);
|
|
1138
|
+
}
|
|
754
1139
|
}
|
|
755
1140
|
catch (err) {
|
|
756
1141
|
// A DEFINITIVE provider rejection (400/401 — invalid_grant and friends)
|
|
@@ -771,7 +1156,12 @@ export class DbSecretsVaultService {
|
|
|
771
1156
|
: result.outcome === 'refreshed'
|
|
772
1157
|
? 'the row had been rotated by a concurrent refresh meanwhile; the rotated sign-in is kept'
|
|
773
1158
|
: 'the row had been removed or replaced meanwhile; nothing wiped';
|
|
774
|
-
|
|
1159
|
+
// A managed row's refusal came from the refresher, whose words are its
|
|
1160
|
+
// own (a port may put the refresh token it was handed in them): only
|
|
1161
|
+
// the status it reported is logged. The vault's own token request
|
|
1162
|
+
// writes fixed sentences, which are safe to repeat.
|
|
1163
|
+
const why = meta.managed ? `the broker refused it (HTTP ${err.providerStatus ?? 'unknown'})` : err.message;
|
|
1164
|
+
log.warn(`refresh refused for "${row.key}", ${did}: ${why}`);
|
|
775
1165
|
return result;
|
|
776
1166
|
}
|
|
777
1167
|
return { outcome: 'transient' };
|
|
@@ -785,7 +1175,10 @@ export class DbSecretsVaultService {
|
|
|
785
1175
|
// value, empty included, is the provider's word.
|
|
786
1176
|
if (refreshed.scope === undefined)
|
|
787
1177
|
refreshed.scope = tokens.scope;
|
|
788
|
-
|
|
1178
|
+
// A sign-in in flight on this row (a managed reconnect waiting for its
|
|
1179
|
+
// handoff, a consent waiting for its exchange) survives the refresh: the
|
|
1180
|
+
// nonce or verifier it minted is what that handoff must match.
|
|
1181
|
+
const next = { clientSecret: blob.clientSecret, tokens: refreshed, ...this.inFlightOf(blob) };
|
|
789
1182
|
// Optimistic concurrency: only persist if the stored ciphertext is unchanged,
|
|
790
1183
|
// so a concurrent refresh on the same row doesn't clobber (Microsoft pattern).
|
|
791
1184
|
const stored = await this.db
|
|
@@ -831,9 +1224,23 @@ export class DbSecretsVaultService {
|
|
|
831
1224
|
return undefined;
|
|
832
1225
|
}
|
|
833
1226
|
}
|
|
834
|
-
/**
|
|
1227
|
+
/**
|
|
1228
|
+
* What a row holds for a sign-in IN FLIGHT: the one-time nonce a managed
|
|
1229
|
+
* handoff must echo, the PKCE verifier and requested scopes a code exchange
|
|
1230
|
+
* must present. A refresh or a wipe rewrites the row's tokens, never these:
|
|
1231
|
+
* dropping them would refuse the handoff or the exchange that is about to
|
|
1232
|
+
* land, for a reason the person could not see.
|
|
1233
|
+
*/
|
|
1234
|
+
inFlightOf(blob) {
|
|
1235
|
+
return {
|
|
1236
|
+
...(blob.pendingNonce ? { pendingNonce: blob.pendingNonce } : {}),
|
|
1237
|
+
...(blob.pendingVerifier ? { pendingVerifier: blob.pendingVerifier } : {}),
|
|
1238
|
+
...(blob.pendingScopes ? { pendingScopes: blob.pendingScopes } : {}),
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
/** Drop a dead grant's token set, keeping the client secret and any sign-in in flight. See {@link refreshRow}. */
|
|
835
1242
|
async wipeTokens(row, blob) {
|
|
836
|
-
const next = { clientSecret: blob.clientSecret };
|
|
1243
|
+
const next = { clientSecret: blob.clientSecret, ...this.inFlightOf(blob) };
|
|
837
1244
|
// Guarded on the ciphertext we read, so a concurrent refresh that
|
|
838
1245
|
// already persisted ROTATED tokens can't be wiped by our stale
|
|
839
1246
|
// failure. When that guard trips (0 rows), the failure was against a
|
|
@@ -866,7 +1273,7 @@ export class DbSecretsVaultService {
|
|
|
866
1273
|
const blob = { clientSecret: provider.clientSecret };
|
|
867
1274
|
const valueEncrypted = this.crypto().encrypt(JSON.stringify(blob));
|
|
868
1275
|
const oauthMeta = this.publicProviderMeta(provider);
|
|
869
|
-
await this.db
|
|
1276
|
+
await this.revokedReplacement(and(isNull(secrets.userId), eq(secrets.key, key)), (guard) => this.db
|
|
870
1277
|
.insert(secrets)
|
|
871
1278
|
.values({ userId: null, key, kind: 'oauth', label, valueEncrypted, oauthMeta })
|
|
872
1279
|
// Preserve an existing row's label on re-upsert (matches
|
|
@@ -876,7 +1283,9 @@ export class DbSecretsVaultService {
|
|
|
876
1283
|
target: secrets.key,
|
|
877
1284
|
targetWhere: isNull(secrets.userId),
|
|
878
1285
|
set: { kind: 'oauth', valueEncrypted, oauthMeta, updatedAt: new Date() },
|
|
879
|
-
|
|
1286
|
+
...(guard ? { setWhere: guard } : {}),
|
|
1287
|
+
})
|
|
1288
|
+
.returning({ id: secrets.id }));
|
|
880
1289
|
this.notifyMutation(null);
|
|
881
1290
|
}
|
|
882
1291
|
async getSharedOAuthProvider(key) {
|
|
@@ -970,13 +1379,17 @@ export class DbSecretsVaultService {
|
|
|
970
1379
|
const authorizationUrl = typeof meta.authorizationUrl === 'string' ? meta.authorizationUrl : '';
|
|
971
1380
|
const tokenUrl = typeof meta.tokenUrl === 'string' ? meta.tokenUrl : '';
|
|
972
1381
|
const clientId = typeof meta.clientId === 'string' ? meta.clientId : '';
|
|
973
|
-
|
|
1382
|
+
const managed = this.managedRefOf(meta) ?? undefined;
|
|
1383
|
+
// A managed row has no client id of its own (the broker's app is the
|
|
1384
|
+
// client); everything else must name one, or no flow can run from it.
|
|
1385
|
+
if (!authorizationUrl || !tokenUrl || (!clientId && !managed)) {
|
|
974
1386
|
throw new SecretOAuthError('OAuth secret is missing provider configuration');
|
|
975
1387
|
}
|
|
976
1388
|
return {
|
|
977
1389
|
authorizationUrl,
|
|
978
1390
|
tokenUrl,
|
|
979
1391
|
clientId,
|
|
1392
|
+
managed,
|
|
980
1393
|
scopes: Array.isArray(meta.scopes) ? meta.scopes.map(String) : undefined,
|
|
981
1394
|
authParams: meta.authParams && typeof meta.authParams === 'object'
|
|
982
1395
|
? meta.authParams
|
|
@@ -996,16 +1409,27 @@ export class DbSecretsVaultService {
|
|
|
996
1409
|
...(p.pkce ? { pkce: true } : {}),
|
|
997
1410
|
...(p.publicClient ? { publicClient: true } : {}),
|
|
998
1411
|
...(p.resource ? { resource: p.resource } : {}),
|
|
1412
|
+
...(p.managed ? { managed: { connectorId: p.managed.connectorId, scopeSet: p.managed.scopeSet } } : {}),
|
|
1413
|
+
...(p.account ? { account: p.account } : {}),
|
|
999
1414
|
};
|
|
1000
1415
|
}
|
|
1001
|
-
|
|
1416
|
+
/**
|
|
1417
|
+
* @param opts.managed Accept a MANAGED provider (no client id of its own,
|
|
1418
|
+
* a connector reference instead). Only the managed sign-in paths pass it:
|
|
1419
|
+
* everywhere else a provider needs a client id, and a `managed` field a
|
|
1420
|
+
* caller slipped in is dropped rather than making the row one.
|
|
1421
|
+
*/
|
|
1422
|
+
requireProvider(p, opts = {}) {
|
|
1002
1423
|
if (!p || typeof p !== 'object')
|
|
1003
1424
|
throw new InvalidSecretError('provider is required');
|
|
1004
1425
|
const cfg = p;
|
|
1005
1426
|
const authorizationUrl = this.requireUrl(cfg.authorizationUrl, 'authorizationUrl');
|
|
1006
1427
|
const tokenUrl = this.requireUrl(cfg.tokenUrl, 'tokenUrl');
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1428
|
+
const managed = opts.managed ? (this.managedRefOf(cfg) ?? undefined) : undefined;
|
|
1429
|
+
// A managed sign-in has no client of its own: whatever id a caller sends
|
|
1430
|
+
// beside the connector reference is not stored, so no row can claim one.
|
|
1431
|
+
const clientId = managed ? '' : typeof cfg.clientId === 'string' ? cfg.clientId.trim() : '';
|
|
1432
|
+
if (!clientId && !managed)
|
|
1009
1433
|
throw new InvalidSecretError('clientId is required');
|
|
1010
1434
|
const scopes = Array.isArray(cfg.scopes) ? cfg.scopes.map(String) : undefined;
|
|
1011
1435
|
const authParams = cfg.authParams && typeof cfg.authParams === 'object'
|
|
@@ -1025,6 +1449,8 @@ export class DbSecretsVaultService {
|
|
|
1025
1449
|
resource: typeof cfg.resource === 'string' && cfg.resource.trim()
|
|
1026
1450
|
? this.requireUrl(cfg.resource, 'resource')
|
|
1027
1451
|
: undefined,
|
|
1452
|
+
managed,
|
|
1453
|
+
account: typeof cfg.account === 'string' && cfg.account.trim() ? cfg.account.trim().slice(0, MAX_LABEL_LEN) : undefined,
|
|
1028
1454
|
};
|
|
1029
1455
|
}
|
|
1030
1456
|
requireUrl(value, field) {
|