@bevel-software/platform-core-backend 0.14.0 → 0.19.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/THIRD-PARTY-NOTICES.md +8 -8
- package/dist/core/catalog-cache-invalidation.d.ts +45 -0
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -0
- package/dist/core/catalog-cache-invalidation.js +76 -0
- package/dist/core/catalog-cache-invalidation.js.map +1 -0
- package/dist/core/catalog-revision.d.ts +39 -0
- package/dist/core/catalog-revision.d.ts.map +1 -0
- package/dist/core/catalog-revision.js +94 -0
- package/dist/core/catalog-revision.js.map +1 -0
- package/dist/core/create-core-server.d.ts +7 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +279 -64
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +66 -2
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +290 -111
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +138 -0
- package/dist/core/lifecycle.d.ts.map +1 -0
- package/dist/core/lifecycle.js +218 -0
- package/dist/core/lifecycle.js.map +1 -0
- package/dist/core/public-config.d.ts +82 -0
- package/dist/core/public-config.d.ts.map +1 -0
- package/dist/core/public-config.js +78 -0
- package/dist/core/public-config.js.map +1 -0
- package/dist/core/readiness.d.ts +86 -0
- package/dist/core/readiness.d.ts.map +1 -0
- package/dist/core/readiness.js +56 -0
- package/dist/core/readiness.js.map +1 -0
- package/dist/core-config.d.ts +30 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +64 -2
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +231 -15
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +83 -12
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +815 -212
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +71 -5
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +161 -13
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-view.d.ts +147 -0
- package/dist/modules/access/access-view.d.ts.map +1 -0
- package/dist/modules/access/access-view.js +211 -0
- package/dist/modules/access/access-view.js.map +1 -0
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +312 -84
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/admin-locked-commit.d.ts +2 -3
- package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
- package/dist/modules/access/admin-locked-commit.js +9 -8
- package/dist/modules/access/admin-locked-commit.js.map +1 -1
- package/dist/modules/access/admin-route-helpers.d.ts.map +1 -1
- package/dist/modules/access/admin-route-helpers.js +4 -4
- package/dist/modules/access/admin-route-helpers.js.map +1 -1
- package/dist/modules/access/change-read-gate.d.ts +53 -0
- package/dist/modules/access/change-read-gate.d.ts.map +1 -0
- package/dist/modules/access/change-read-gate.js +134 -0
- package/dist/modules/access/change-read-gate.js.map +1 -0
- package/dist/modules/access/creator-access.d.ts +33 -39
- package/dist/modules/access/creator-access.d.ts.map +1 -1
- package/dist/modules/access/creator-access.js +92 -119
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +3 -1
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/access/groups-admin.service.d.ts +3 -3
- package/dist/modules/access/groups-admin.service.js +2 -2
- package/dist/modules/access/reference-scan.d.ts +1 -1
- package/dist/modules/access/reference-scan.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.js +5 -4
- package/dist/modules/access/reference-scan.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +72 -13
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +71 -13
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access/synced-groups-committer.d.ts.map +1 -1
- package/dist/modules/access/synced-groups-committer.js +8 -6
- package/dist/modules/access/synced-groups-committer.js.map +1 -1
- package/dist/modules/access/synced-groups-writer.d.ts.map +1 -1
- package/dist/modules/access/synced-groups-writer.js +3 -8
- package/dist/modules/access/synced-groups-writer.js.map +1 -1
- package/dist/modules/access/user-access-removal.service.d.ts +209 -0
- package/dist/modules/access/user-access-removal.service.d.ts.map +1 -0
- package/dist/modules/access/user-access-removal.service.js +662 -0
- package/dist/modules/access/user-access-removal.service.js.map +1 -0
- package/dist/modules/access-model/access-errors.d.ts +32 -1
- package/dist/modules/access-model/access-errors.d.ts.map +1 -1
- package/dist/modules/access-model/access-errors.js +38 -11
- package/dist/modules/access-model/access-errors.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +100 -25
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +162 -48
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/access-splice.d.ts.map +1 -1
- package/dist/modules/access-model/access-splice.js +18 -18
- package/dist/modules/access-model/access-splice.js.map +1 -1
- package/dist/modules/access-model/access-template.d.ts +22 -0
- package/dist/modules/access-model/access-template.d.ts.map +1 -0
- package/dist/modules/access-model/access-template.js +42 -0
- package/dist/modules/access-model/access-template.js.map +1 -0
- package/dist/modules/access-model/change-gate.d.ts +65 -0
- package/dist/modules/access-model/change-gate.d.ts.map +1 -0
- package/dist/modules/access-model/change-gate.js +25 -0
- package/dist/modules/access-model/change-gate.js.map +1 -0
- package/dist/modules/access-model/creator.d.ts +13 -16
- package/dist/modules/access-model/creator.d.ts.map +1 -1
- package/dist/modules/access-model/creator.js.map +1 -1
- package/dist/modules/access-model/frontmatter-lines.d.ts +27 -0
- package/dist/modules/access-model/frontmatter-lines.d.ts.map +1 -0
- package/dist/modules/access-model/frontmatter-lines.js +74 -0
- package/dist/modules/access-model/frontmatter-lines.js.map +1 -0
- package/dist/modules/access-model/group-files.d.ts.map +1 -1
- package/dist/modules/access-model/group-files.js +4 -1
- package/dist/modules/access-model/group-files.js.map +1 -1
- package/dist/modules/access-model/plugin-principals.d.ts +51 -0
- package/dist/modules/access-model/plugin-principals.d.ts.map +1 -0
- package/dist/modules/access-model/plugin-principals.js +119 -0
- package/dist/modules/access-model/plugin-principals.js.map +1 -0
- package/dist/modules/access-model/roles-yaml-guard.d.ts +73 -2
- package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
- package/dist/modules/access-model/roles-yaml-guard.js +153 -3
- package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
- package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
- package/dist/modules/admin/admin-access.service.js +5 -2
- package/dist/modules/admin/admin-access.service.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +21 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.js +38 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -0
- package/dist/modules/agent-instructions/compose.d.ts +71 -0
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -0
- package/dist/modules/agent-instructions/compose.js +234 -0
- package/dist/modules/agent-instructions/compose.js.map +1 -0
- package/dist/modules/agent-instructions/index.d.ts +4 -0
- package/dist/modules/agent-instructions/index.d.ts.map +1 -0
- package/dist/modules/agent-instructions/index.js +4 -0
- package/dist/modules/agent-instructions/index.js.map +1 -0
- package/dist/modules/agent-instructions/read-preamble.d.ts +37 -0
- package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -0
- package/dist/modules/agent-instructions/read-preamble.js +81 -0
- package/dist/modules/agent-instructions/read-preamble.js.map +1 -0
- package/dist/modules/auth/account-erasure.service.d.ts +30 -4
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +96 -10
- package/dist/modules/auth/account-erasure.service.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts +6 -3
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +109 -10
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts +10 -3
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +9 -4
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +89 -5
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +160 -25
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts +46 -3
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +99 -21
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +14 -2
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +56 -7
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/advisory-lock.d.ts +103 -0
- package/dist/modules/database/advisory-lock.d.ts.map +1 -0
- package/dist/modules/database/advisory-lock.js +320 -0
- package/dist/modules/database/advisory-lock.js.map +1 -0
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +9 -1
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +672 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +171 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +26 -6
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.js +4 -2
- package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
- package/dist/modules/diff/diff.routes.d.ts.map +1 -1
- package/dist/modules/diff/diff.routes.js +2 -4
- package/dist/modules/diff/diff.routes.js.map +1 -1
- package/dist/modules/diff/diff.service.d.ts +4 -1
- package/dist/modules/diff/diff.service.d.ts.map +1 -1
- package/dist/modules/diff/diff.service.js +79 -81
- package/dist/modules/diff/diff.service.js.map +1 -1
- package/dist/modules/kb-fs/bevel-ignore.d.ts +35 -0
- package/dist/modules/kb-fs/bevel-ignore.d.ts.map +1 -0
- package/dist/modules/kb-fs/bevel-ignore.js +71 -0
- package/dist/modules/kb-fs/bevel-ignore.js.map +1 -0
- package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -1
- package/dist/modules/kb-fs/file-change-notifier.js +3 -1
- package/dist/modules/kb-fs/file-change-notifier.js.map +1 -1
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +37 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.js +104 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts +113 -18
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +353 -76
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/node-fs.d.ts +23 -0
- package/dist/modules/kb-fs/node-fs.d.ts.map +1 -0
- package/dist/modules/kb-fs/node-fs.js +158 -0
- package/dist/modules/kb-fs/node-fs.js.map +1 -0
- package/dist/modules/kb-fs/read-only-filesystem.d.ts +10 -9
- package/dist/modules/kb-fs/read-only-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/read-only-filesystem.js +18 -9
- package/dist/modules/kb-fs/read-only-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +100 -3
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +195 -3
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-sync/index.d.ts +6 -0
- package/dist/modules/kb-sync/index.d.ts.map +1 -0
- package/dist/modules/kb-sync/index.js +5 -0
- package/dist/modules/kb-sync/index.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +77 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.js +2 -0
- package/dist/modules/kb-sync/kb-sync.interface.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts +70 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.js +137 -0
- package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts +45 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.js +185 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -0
- package/dist/modules/kb-sync/sync-auth.d.ts +59 -0
- package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-auth.js +62 -0
- package/dist/modules/kb-sync/sync-auth.js.map +1 -0
- package/dist/modules/kb-sync/sync-payload.d.ts +32 -0
- package/dist/modules/kb-sync/sync-payload.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-payload.js +105 -0
- package/dist/modules/kb-sync/sync-payload.js.map +1 -0
- package/dist/modules/marketplace/git-http.routes.d.ts +42 -0
- package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/git-http.routes.js +192 -0
- package/dist/modules/marketplace/git-http.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +33 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +88 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts +59 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js +96 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +126 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +192 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts +47 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js +252 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +121 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js +213 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/index.d.ts +6 -0
- package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/index.js +6 -0
- package/dist/modules/marketplace/github-facade/index.js.map +1 -0
- package/dist/modules/marketplace/index.d.ts +4 -0
- package/dist/modules/marketplace/index.d.ts.map +1 -0
- package/dist/modules/marketplace/index.js +4 -0
- package/dist/modules/marketplace/index.js.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts +135 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.js +318 -0
- package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -0
- package/dist/modules/mcp/downstream-pool.d.ts +107 -0
- package/dist/modules/mcp/downstream-pool.d.ts.map +1 -0
- package/dist/modules/mcp/downstream-pool.js +221 -0
- package/dist/modules/mcp/downstream-pool.js.map +1 -0
- package/dist/modules/mcp/downstream-token-refresh.d.ts +77 -0
- package/dist/modules/mcp/downstream-token-refresh.d.ts.map +1 -0
- package/dist/modules/mcp/downstream-token-refresh.js +166 -0
- package/dist/modules/mcp/downstream-token-refresh.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli-args.d.ts +78 -0
- package/dist/modules/mcp/first-call-probe.cli-args.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli-args.js +151 -0
- package/dist/modules/mcp/first-call-probe.cli-args.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli.d.ts +2 -0
- package/dist/modules/mcp/first-call-probe.cli.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli.js +62 -0
- package/dist/modules/mcp/first-call-probe.cli.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.d.ts +165 -0
- package/dist/modules/mcp/first-call-probe.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.js +298 -0
- package/dist/modules/mcp/first-call-probe.js.map +1 -0
- package/dist/modules/mcp/manual-failure-memo.d.ts +42 -17
- package/dist/modules/mcp/manual-failure-memo.d.ts.map +1 -1
- package/dist/modules/mcp/manual-failure-memo.js +79 -23
- package/dist/modules/mcp/manual-failure-memo.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +9 -5
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +24 -16
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +4 -4
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +83 -179
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +218 -61
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +713 -159
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +8 -2
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +14 -8
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +16 -2
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.d.ts +13 -2
- package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.js +3 -1
- package/dist/modules/mcp/oauth/oauth-state.js.map +1 -1
- package/dist/modules/mcp/surface-log-throttle.d.ts +59 -0
- package/dist/modules/mcp/surface-log-throttle.d.ts.map +1 -0
- package/dist/modules/mcp/surface-log-throttle.js +94 -0
- package/dist/modules/mcp/surface-log-throttle.js.map +1 -0
- package/dist/modules/plugins/compile/compile-marketplace.d.ts +104 -0
- package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -0
- package/dist/modules/plugins/compile/compile-marketplace.js +323 -0
- package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +73 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +135 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +37 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +158 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +65 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js +204 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +39 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js +110 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -0
- package/dist/modules/plugins/discovery/native.source.d.ts +16 -0
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/native.source.js +120 -0
- package/dist/modules/plugins/discovery/native.source.js.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts +110 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.js +2 -0
- package/dist/modules/plugins/discovery/plugin-source.js.map +1 -0
- package/dist/modules/plugins/index.d.ts +15 -3
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +13 -2
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts +250 -0
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -0
- package/dist/modules/plugins/join-request-jobs.service.js +602 -0
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -0
- package/dist/modules/plugins/join-request-records.store.d.ts +161 -0
- package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -0
- package/dist/modules/plugins/join-request-records.store.js +179 -0
- package/dist/modules/plugins/join-request-records.store.js.map +1 -0
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +4 -2
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +69 -0
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.js +163 -0
- package/dist/modules/plugins/plugin-links.js.map +1 -0
- package/dist/modules/plugins/plugin-links.service.d.ts +111 -0
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.service.js +295 -0
- package/dist/modules/plugins/plugin-links.service.js.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts +129 -21
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +320 -75
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +71 -0
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-rename.service.js +254 -0
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -0
- package/dist/modules/plugins/plugins.contract.d.ts +78 -4
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +44 -5
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +297 -115
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +82 -3
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +178 -48
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/plugins/plugins.tools.d.ts +25 -0
- package/dist/modules/plugins/plugins.tools.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.tools.js +82 -0
- package/dist/modules/plugins/plugins.tools.js.map +1 -0
- package/dist/modules/plugins/teams.routes.d.ts +42 -0
- package/dist/modules/plugins/teams.routes.d.ts.map +1 -0
- package/dist/modules/plugins/teams.routes.js +79 -0
- package/dist/modules/plugins/teams.routes.js.map +1 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +40 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +202 -37
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +1 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +5 -3
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js +3 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +78 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.js +40 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +106 -52
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/connection-check.d.ts +118 -0
- package/dist/modules/settings/connection-check.d.ts.map +1 -0
- package/dist/modules/settings/connection-check.js +287 -0
- package/dist/modules/settings/connection-check.js.map +1 -0
- package/dist/modules/settings/deployment-settings.service.d.ts +157 -2
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +412 -35
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/git-root-folders.d.ts +54 -0
- package/dist/modules/settings/git-root-folders.d.ts.map +1 -0
- package/dist/modules/settings/git-root-folders.js +130 -0
- package/dist/modules/settings/git-root-folders.js.map +1 -0
- package/dist/modules/settings/oidc-check.d.ts +85 -0
- package/dist/modules/settings/oidc-check.d.ts.map +1 -0
- package/dist/modules/settings/oidc-check.js +172 -0
- package/dist/modules/settings/oidc-check.js.map +1 -0
- package/dist/modules/settings/setup.routes.d.ts +57 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +408 -108
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/allowed-tools-check.d.ts +132 -0
- package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -0
- package/dist/modules/skills/allowed-tools-check.js +260 -0
- package/dist/modules/skills/allowed-tools-check.js.map +1 -0
- package/dist/modules/skills/index.d.ts +4 -1
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +2 -0
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +6 -11
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +24 -71
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +38 -0
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -0
- package/dist/modules/skills/skill-access-requests.routes.js +171 -0
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -0
- package/dist/modules/skills/skills.contract.d.ts +67 -2
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +35 -3
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +57 -4
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +31 -3
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +236 -65
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts +4 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +41 -6
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts +18 -0
- package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -0
- package/dist/modules/tool-auth/connection-key-rejection.js +20 -0
- package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts +14 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts.map +1 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.js +67 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.js.map +1 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +66 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.js +2 -1
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +23 -4
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +79 -19
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +5 -2
- package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.js +5 -2
- package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
- package/dist/modules/tool-auth/key-or-session.middleware.d.ts +23 -0
- package/dist/modules/tool-auth/key-or-session.middleware.d.ts.map +1 -0
- package/dist/modules/tool-auth/key-or-session.middleware.js +39 -0
- package/dist/modules/tool-auth/key-or-session.middleware.js.map +1 -0
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +7 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +8 -5
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts +14 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +28 -6
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +22 -5
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +6 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js +7 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-manuals/index.d.ts +2 -1
- package/dist/modules/tool-manuals/index.d.ts.map +1 -1
- package/dist/modules/tool-manuals/index.js +1 -0
- package/dist/modules/tool-manuals/index.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +36 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +136 -96
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +10 -2
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -15
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts +50 -0
- package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/pending-tools.service.js +149 -0
- package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-delete.service.d.ts +135 -0
- package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/tool-delete.service.js +303 -0
- package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +161 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.js +9 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +19 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +127 -57
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +121 -2
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +415 -92
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +78 -6
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +88 -54
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/event-bus.d.ts +10 -4
- package/dist/modules/workflow/event-bus.d.ts.map +1 -1
- package/dist/modules/workflow/event-bus.js +14 -11
- package/dist/modules/workflow/event-bus.js.map +1 -1
- package/dist/modules/workflow/events.routes.d.ts +17 -2
- package/dist/modules/workflow/events.routes.d.ts.map +1 -1
- package/dist/modules/workflow/events.routes.js +31 -12
- package/dist/modules/workflow/events.routes.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +22 -4
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +27 -4
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/change-request-link.d.ts +20 -0
- package/dist/modules/workflow/git/change-request-link.d.ts.map +1 -0
- package/dist/modules/workflow/git/change-request-link.js +34 -0
- package/dist/modules/workflow/git/change-request-link.js.map +1 -0
- package/dist/modules/workflow/git/git-version.d.ts.map +1 -1
- package/dist/modules/workflow/git/git-version.js +3 -1
- package/dist/modules/workflow/git/git-version.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +303 -6
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +753 -170
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +36 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -0
- package/dist/modules/workflow/git/node-git-runner.js +273 -0
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts +57 -2
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +165 -25
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +11 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +24 -4
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +15 -8
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +107 -58
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +3 -1
- package/dist/modules/workflow/recovery-bot.js.map +1 -1
- package/dist/modules/workflow/review-workflow/approval-lock.d.ts +60 -0
- package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +1 -0
- package/dist/modules/workflow/review-workflow/approval-lock.js +116 -0
- package/dist/modules/workflow/review-workflow/approval-lock.js.map +1 -0
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +54 -7
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +64 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +269 -90
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/sanitize-error.d.ts +5 -2
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
- package/dist/modules/workflow/sanitize-error.js +8 -4
- package/dist/modules/workflow/sanitize-error.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts +2 -0
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +259 -29
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +217 -14
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +1142 -109
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/empty-dirs.d.ts +15 -0
- package/dist/modules/workspace/empty-dirs.d.ts.map +1 -0
- package/dist/modules/workspace/empty-dirs.js +90 -0
- package/dist/modules/workspace/empty-dirs.js.map +1 -0
- package/dist/modules/workspace/file-readers/content-mode.d.ts +50 -0
- package/dist/modules/workspace/file-readers/content-mode.d.ts.map +1 -0
- package/dist/modules/workspace/file-readers/content-mode.js +55 -0
- package/dist/modules/workspace/file-readers/content-mode.js.map +1 -0
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +4 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js +4 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.types.d.ts +5 -2
- package/dist/modules/workspace/file-readers/doc-extract.types.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.types.js +7 -4
- package/dist/modules/workspace/file-readers/doc-extract.types.js.map +1 -1
- package/dist/modules/workspace/file-readers/document-reader.d.ts +2 -0
- package/dist/modules/workspace/file-readers/document-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/document-reader.js +5 -0
- package/dist/modules/workspace/file-readers/document-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/extract-docx.d.ts +6 -0
- package/dist/modules/workspace/file-readers/extract-docx.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/extract-docx.js +6 -0
- package/dist/modules/workspace/file-readers/extract-docx.js.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.d.ts +24 -5
- package/dist/modules/workspace/file-readers/file-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.js +11 -6
- package/dist/modules/workspace/file-readers/file-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.js +6 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.js.map +1 -1
- package/dist/modules/workspace/file-readers/image-reader.d.ts +7 -5
- package/dist/modules/workspace/file-readers/image-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/image-reader.js +9 -5
- package/dist/modules/workspace/file-readers/image-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/ooxml-text.d.ts +3 -0
- package/dist/modules/workspace/file-readers/ooxml-text.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/ooxml-text.js +39 -2
- package/dist/modules/workspace/file-readers/ooxml-text.js.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.d.ts +29 -1
- package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.js +57 -3
- package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
- package/dist/modules/workspace/git-internals.middleware.d.ts +28 -0
- package/dist/modules/workspace/git-internals.middleware.d.ts.map +1 -0
- package/dist/modules/workspace/git-internals.middleware.js +93 -0
- package/dist/modules/workspace/git-internals.middleware.js.map +1 -0
- package/dist/modules/workspace/not-found.d.ts +80 -0
- package/dist/modules/workspace/not-found.d.ts.map +1 -0
- package/dist/modules/workspace/not-found.js +112 -0
- package/dist/modules/workspace/not-found.js.map +1 -0
- package/dist/modules/workspace/session-ontology.gate.d.ts +1 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.js +1 -1
- package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
- package/dist/modules/workspace/startup/beside-checkout.d.ts +42 -0
- package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -0
- package/dist/modules/workspace/startup/beside-checkout.js +110 -0
- package/dist/modules/workspace/startup/beside-checkout.js.map +1 -0
- package/dist/modules/workspace/startup/kb-git.d.ts +7 -12
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-git.js +34 -35
- package/dist/modules/workspace/startup/kb-git.js.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +95 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +257 -55
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +45 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +417 -385
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +48 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js +183 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +51 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +158 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.d.ts +64 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.js +97 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +41 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +85 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +3 -1
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js +4 -12
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +6 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +174 -54
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +96 -24
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +521 -126
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +94 -0
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/template-source.js +160 -0
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts +14 -2
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +657 -246
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +323 -12
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +1006 -257
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +42 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +1590 -146
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +76 -0
- package/dist/modules/workspace/write-denial.d.ts.map +1 -0
- package/dist/modules/workspace/write-denial.js +138 -0
- package/dist/modules/workspace/write-denial.js.map +1 -0
- package/dist/shared/canonical-file-identity.d.ts +64 -0
- package/dist/shared/canonical-file-identity.d.ts.map +1 -0
- package/dist/shared/canonical-file-identity.js +92 -0
- package/dist/shared/canonical-file-identity.js.map +1 -0
- package/dist/shared/domain-errors.d.ts +128 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +166 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/email-identity.d.ts +32 -0
- package/dist/shared/email-identity.d.ts.map +1 -0
- package/dist/shared/email-identity.js +37 -0
- package/dist/shared/email-identity.js.map +1 -0
- package/dist/shared/fs-errors.d.ts +8 -0
- package/dist/shared/fs-errors.d.ts.map +1 -0
- package/dist/shared/fs-errors.js +11 -0
- package/dist/shared/fs-errors.js.map +1 -0
- package/dist/shared/fs-walk.d.ts +20 -6
- package/dist/shared/fs-walk.d.ts.map +1 -1
- package/dist/shared/fs-walk.js +35 -27
- package/dist/shared/fs-walk.js.map +1 -1
- package/dist/shared/fs.contract.d.ts +206 -0
- package/dist/shared/fs.contract.d.ts.map +1 -0
- package/dist/shared/fs.contract.js +79 -0
- package/dist/shared/fs.contract.js.map +1 -0
- package/dist/shared/git-failure.d.ts +80 -0
- package/dist/shared/git-failure.d.ts.map +1 -0
- package/dist/shared/git-failure.js +139 -0
- package/dist/shared/git-failure.js.map +1 -0
- package/dist/shared/git-internals.d.ts +17 -0
- package/dist/shared/git-internals.d.ts.map +1 -0
- package/dist/shared/git-internals.js +123 -0
- package/dist/shared/git-internals.js.map +1 -0
- package/dist/shared/git.contract.d.ts +157 -0
- package/dist/shared/git.contract.d.ts.map +1 -0
- package/dist/shared/git.contract.js +103 -0
- package/dist/shared/git.contract.js.map +1 -0
- package/dist/shared/http-errors.d.ts +33 -0
- package/dist/shared/http-errors.d.ts.map +1 -0
- package/dist/shared/http-errors.js +35 -0
- package/dist/shared/http-errors.js.map +1 -0
- package/dist/shared/kb-walk.d.ts +52 -0
- package/dist/shared/kb-walk.d.ts.map +1 -0
- package/dist/shared/kb-walk.js +76 -0
- package/dist/shared/kb-walk.js.map +1 -0
- package/dist/shared/logger.contract.d.ts +63 -0
- package/dist/shared/logger.contract.d.ts.map +1 -0
- package/dist/shared/logger.contract.js +126 -0
- package/dist/shared/logger.contract.js.map +1 -0
- package/dist/shared/logging.d.ts +31 -0
- package/dist/shared/logging.d.ts.map +1 -0
- package/dist/shared/logging.js +92 -0
- package/dist/shared/logging.js.map +1 -0
- package/dist/shared/path-containment.d.ts +21 -0
- package/dist/shared/path-containment.d.ts.map +1 -0
- package/dist/shared/path-containment.js +34 -0
- package/dist/shared/path-containment.js.map +1 -0
- package/dist/shared/path-order.d.ts +18 -0
- package/dist/shared/path-order.d.ts.map +1 -0
- package/dist/shared/path-order.js +35 -0
- package/dist/shared/path-order.js.map +1 -0
- package/dist/shared/pending-proposals.d.ts +71 -0
- package/dist/shared/pending-proposals.d.ts.map +1 -0
- package/dist/shared/pending-proposals.js +98 -0
- package/dist/shared/pending-proposals.js.map +1 -0
- package/dist/shared/printable.d.ts +22 -0
- package/dist/shared/printable.d.ts.map +1 -0
- package/dist/shared/printable.js +65 -0
- package/dist/shared/printable.js.map +1 -0
- package/dist/shared/redact-secret.d.ts +30 -0
- package/dist/shared/redact-secret.d.ts.map +1 -0
- package/dist/shared/redact-secret.js +114 -0
- package/dist/shared/redact-secret.js.map +1 -0
- package/dist/shared/rename-no-replace.d.ts +119 -0
- package/dist/shared/rename-no-replace.d.ts.map +1 -0
- package/dist/shared/rename-no-replace.js +323 -0
- package/dist/shared/rename-no-replace.js.map +1 -0
- package/dist/shared/ssrf.d.ts.map +1 -1
- package/dist/shared/ssrf.js +49 -18
- package/dist/shared/ssrf.js.map +1 -1
- package/dist/shared/ttl-cache.d.ts +28 -1
- package/dist/shared/ttl-cache.d.ts.map +1 -1
- package/dist/shared/ttl-cache.js +33 -1
- package/dist/shared/ttl-cache.js.map +1 -1
- package/dist/version.d.ts +10 -6
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +10 -6
- package/dist/version.js.map +1 -1
- package/kb-template/.bevelignore +4 -3
- package/kb-template/AGENTS.md +364 -54
- package/kb-template/KnowledgeBase/How to get started.md +1 -1
- package/kb-template/Skills/.gitkeep +0 -0
- package/kb-template/access.md +5 -3
- package/kb-template/mcp-description.md +27 -0
- package/migrations/0005_github_facade.sql +25 -0
- package/migrations/0006_api_tokens_revoked_by.sql +1 -0
- package/migrations/0007_github_facade_registered.sql +1 -0
- package/migrations/0008_change_request_apply_failure.sql +4 -0
- package/migrations/0009_change_request_apply_failure_kind.sql +13 -0
- package/migrations/0010_plugin_join_requests.sql +15 -0
- package/migrations/0011_plugin_join_request_claim.sql +11 -0
- package/migrations/meta/0005_snapshot.json +1717 -0
- package/migrations/meta/0006_snapshot.json +1723 -0
- package/migrations/meta/0007_snapshot.json +1729 -0
- package/migrations/meta/0008_snapshot.json +1753 -0
- package/migrations/meta/0009_snapshot.json +1759 -0
- package/migrations/meta/0010_snapshot.json +1872 -0
- package/migrations/meta/0011_snapshot.json +1884 -0
- package/migrations/meta/_journal.json +49 -0
- package/package.json +8 -7
- package/src/__tests__/core-config.domain.test.ts +67 -2
- package/src/__tests__/kb-layout-config.test.ts +295 -0
- package/src/__tests__/open-change-gate.ts +15 -0
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +226 -0
- package/src/core/__tests__/catalog-revision.test.ts +190 -0
- package/src/core/__tests__/lifecycle.test.ts +369 -0
- package/src/core/__tests__/public-config.test.ts +30 -0
- package/src/core/__tests__/readiness.test.ts +109 -0
- package/src/core/catalog-cache-invalidation.ts +111 -0
- package/src/core/catalog-revision.ts +110 -0
- package/src/core/create-core-server.ts +358 -63
- package/src/core/create-core-services.ts +449 -107
- package/src/core/lifecycle.ts +312 -0
- package/src/core/public-config.ts +86 -0
- package/src/core/readiness.ts +123 -0
- package/src/core-config.ts +70 -4
- package/src/index.ts +15 -0
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +362 -0
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +2 -1
- package/src/modules/access/__tests__/access-control.atref-cache.test.ts +46 -22
- package/src/modules/access/__tests__/access-control.deny-dependents.test.ts +215 -0
- package/src/modules/access/__tests__/access-control.download-read.test.ts +268 -0
- package/src/modules/access/__tests__/access-control.everyone-read.test.ts +145 -0
- package/src/modules/access/__tests__/access-control.group-read.test.ts +163 -0
- package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +88 -0
- package/src/modules/access/__tests__/access-control.platform-restore.test.ts +152 -0
- package/src/modules/access/__tests__/access-control.prospective.test.ts +189 -0
- package/src/modules/access/__tests__/access-control.read-batch.test.ts +2 -1
- package/src/modules/access/__tests__/access-control.service.test.ts +142 -81
- package/src/modules/access/__tests__/access-groups.test.ts +47 -14
- package/src/modules/access/__tests__/access-md-format.test.ts +2 -1
- package/src/modules/access/__tests__/access-mutation.service.test.ts +52 -4
- package/src/modules/access/__tests__/access-own-read-grant.test.ts +2 -0
- package/src/modules/access/__tests__/access-personal-plugin.test.ts +153 -0
- package/src/modules/access/__tests__/access-plugin-principals.test.ts +385 -0
- package/src/modules/access/__tests__/access-restrict-here.test.ts +450 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +139 -0
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +394 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -2
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +3 -2
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +234 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +216 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +7 -2
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +360 -0
- package/src/modules/access/__tests__/change-read-gate.test.ts +272 -0
- package/src/modules/access/__tests__/creator-access.test.ts +81 -83
- package/src/modules/access/__tests__/grant-sources.test.ts +3 -2
- package/src/modules/access/__tests__/groups-admin.service.test.ts +14 -5
- package/src/modules/access/__tests__/roles-admin.security.test.ts +196 -0
- package/src/modules/access/__tests__/roles-admin.service.test.ts +127 -2
- package/src/modules/access/__tests__/roles-capabilities.test.ts +5 -4
- package/src/modules/access/__tests__/roles.routes.test.ts +5 -2
- package/src/modules/access/__tests__/user-access-removal.service.test.ts +402 -0
- package/src/modules/access/__tests__/users-db-double.ts +63 -0
- package/src/modules/access/access-control.interface.ts +269 -18
- package/src/modules/access/access-control.service.ts +943 -226
- package/src/modules/access/access-mutation.service.ts +182 -13
- package/src/modules/access/access-view.ts +316 -0
- package/src/modules/access/access.routes.ts +385 -105
- package/src/modules/access/admin-locked-commit.ts +12 -14
- package/src/modules/access/admin-route-helpers.ts +4 -4
- package/src/modules/access/change-read-gate.ts +148 -0
- package/src/modules/access/creator-access.ts +89 -124
- package/src/modules/access/directory-sync-bot.ts +4 -1
- package/src/modules/access/groups-admin.service.ts +3 -3
- package/src/modules/access/reference-scan.ts +5 -5
- package/src/modules/access/roles-admin.service.ts +94 -15
- package/src/modules/access/synced-groups-committer.ts +10 -15
- package/src/modules/access/synced-groups-writer.ts +3 -12
- package/src/modules/access/user-access-removal.service.ts +711 -0
- package/src/modules/access-model/__tests__/access-grammar.test.ts +18 -0
- package/src/modules/access-model/__tests__/access-splice.test.ts +63 -1
- package/src/modules/access-model/__tests__/frontmatter-fences.test.ts +71 -0
- package/src/modules/access-model/__tests__/plugin-token-canonical.test.ts +40 -0
- package/src/modules/access-model/__tests__/roles-yaml-guard.test.ts +131 -0
- package/src/modules/access-model/access-errors.ts +56 -15
- package/src/modules/access-model/access-grammar.ts +198 -49
- package/src/modules/access-model/access-splice.ts +18 -18
- package/src/modules/access-model/access-template.ts +44 -0
- package/src/modules/access-model/change-gate.ts +80 -0
- package/src/modules/access-model/creator.ts +18 -21
- package/src/modules/access-model/frontmatter-lines.ts +98 -0
- package/src/modules/access-model/group-files.ts +4 -0
- package/src/modules/access-model/plugin-principals.ts +130 -0
- package/src/modules/access-model/roles-yaml-guard.ts +182 -4
- package/src/modules/admin/admin-access.service.ts +6 -2
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +100 -0
- package/src/modules/agent-instructions/__tests__/compose.test.ts +256 -0
- package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +135 -0
- package/src/modules/agent-instructions/agent-instructions.routes.ts +41 -0
- package/src/modules/agent-instructions/compose.ts +271 -0
- package/src/modules/agent-instructions/index.ts +14 -0
- package/src/modules/agent-instructions/read-preamble.ts +88 -0
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +209 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +178 -2
- package/src/modules/auth/__tests__/auth.middleware.test.ts +78 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +338 -5
- package/src/modules/auth/__tests__/oidc-auth-provider.test.ts +352 -12
- package/src/modules/auth/__tests__/onboarding-done.route.test.ts +3 -1
- package/src/modules/auth/account-erasure.service.ts +117 -10
- package/src/modules/auth/account.routes.ts +115 -14
- package/src/modules/auth/auth.routes.ts +20 -7
- package/src/modules/auth/auth.service.ts +187 -28
- package/src/modules/auth/oidc-auth-provider.ts +120 -22
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +109 -1
- package/src/modules/code-mode/code-mode.tool.ts +13 -2
- package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +78 -0
- package/src/modules/connection-probe/connection-probe.service.ts +59 -7
- package/src/modules/database/__tests__/advisory-lease.test.ts +152 -0
- package/src/modules/database/__tests__/advisory-lock.test.ts +147 -0
- package/src/modules/database/advisory-lock.ts +346 -0
- package/src/modules/database/connection.ts +9 -1
- package/src/modules/database/core-schema.ts +177 -0
- package/src/modules/database/migrate.ts +27 -6
- package/src/modules/declared-variables/declared-variables.routes.ts +6 -3
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +3 -0
- package/src/modules/diff/__tests__/diff.service.test.ts +3 -0
- package/src/modules/diff/diff.routes.ts +2 -4
- package/src/modules/diff/diff.service.ts +75 -79
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +472 -9
- package/src/modules/kb-fs/__tests__/node-fs.test.ts +409 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +210 -1
- package/src/modules/{workspace → kb-fs}/bevel-ignore.ts +22 -15
- package/src/modules/kb-fs/file-change-notifier.ts +4 -1
- package/src/modules/kb-fs/git-guarded-filesystem.ts +123 -0
- package/src/modules/kb-fs/locking-filesystem.ts +399 -109
- package/src/modules/kb-fs/node-fs.ts +165 -0
- package/src/modules/kb-fs/read-only-filesystem.ts +18 -9
- package/src/modules/kb-fs/repo-path.ts +213 -3
- package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +297 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +295 -0
- package/src/modules/kb-sync/__tests__/sync-auth.test.ts +118 -0
- package/src/modules/kb-sync/__tests__/sync-payload.test.ts +148 -0
- package/src/modules/kb-sync/index.ts +19 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +79 -0
- package/src/modules/kb-sync/kb-sync.routes.ts +200 -0
- package/src/modules/kb-sync/kb-sync.service.ts +204 -0
- package/src/modules/kb-sync/sync-auth.ts +118 -0
- package/src/modules/kb-sync/sync-payload.ts +143 -0
- package/src/modules/marketplace/__tests__/github-facade.test.ts +575 -0
- package/src/modules/marketplace/__tests__/marketplace-git.test.ts +180 -0
- package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +47 -0
- package/src/modules/marketplace/git-http.routes.ts +196 -0
- package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +110 -0
- package/src/modules/marketplace/github-facade/github-facade-codes.store.ts +145 -0
- package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +263 -0
- package/src/modules/marketplace/github-facade/github-facade.routes.ts +294 -0
- package/src/modules/marketplace/github-facade/github-facade.service.ts +282 -0
- package/src/modules/marketplace/github-facade/index.ts +26 -0
- package/src/modules/marketplace/index.ts +3 -0
- package/src/modules/marketplace/marketplace-repo.service.ts +352 -0
- package/src/modules/mcp/__tests__/downstream-pool.test.ts +278 -0
- package/src/modules/mcp/__tests__/downstream-token-refresh.test.ts +178 -0
- package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +156 -0
- package/src/modules/mcp/__tests__/first-call-probe.cli-args.test.ts +178 -0
- package/src/modules/mcp/__tests__/first-call-probe.test.ts +414 -0
- package/src/modules/mcp/__tests__/manual-failure-memo.test.ts +98 -6
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +8 -5
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +1 -3
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +1055 -59
- package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +182 -0
- package/src/modules/mcp/__tests__/mcp.service.test.ts +274 -6
- package/src/modules/mcp/__tests__/surface-log-throttle.test.ts +169 -0
- package/src/modules/mcp/downstream-pool.ts +256 -0
- package/src/modules/mcp/downstream-token-refresh.ts +170 -0
- package/src/modules/mcp/first-call-probe.cli-args.ts +159 -0
- package/src/modules/mcp/first-call-probe.cli.ts +60 -0
- package/src/modules/mcp/first-call-probe.ts +418 -0
- package/src/modules/mcp/manual-failure-memo.ts +81 -23
- package/src/modules/mcp/mcp-auth.middleware.ts +25 -16
- package/src/modules/mcp/mcp.routes.ts +376 -482
- package/src/modules/mcp/mcp.service.ts +846 -176
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +8 -2
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +28 -6
- package/src/modules/mcp/oauth/oauth-state.ts +15 -3
- package/src/modules/mcp/surface-log-throttle.ts +120 -0
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +166 -0
- package/src/modules/plugins/__tests__/bundle-dialect.test.ts +165 -0
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +423 -0
- package/src/modules/plugins/__tests__/fake-join-request-store.ts +189 -0
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +608 -0
- package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +241 -0
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +201 -16
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +284 -0
- package/src/modules/plugins/__tests__/plugin-names.test.ts +77 -0
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +330 -20
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +456 -0
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +441 -43
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +131 -0
- package/src/modules/plugins/__tests__/teams.routes.test.ts +248 -0
- package/src/modules/plugins/compile/compile-marketplace.ts +500 -0
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +151 -0
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +365 -0
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +161 -0
- package/src/modules/plugins/discovery/bundle-dialect/registry.ts +250 -0
- package/src/modules/plugins/discovery/kb-plugin-source.ts +117 -0
- package/src/modules/plugins/discovery/native.source.ts +140 -0
- package/src/modules/plugins/discovery/plugin-source.ts +113 -0
- package/src/modules/plugins/index.ts +25 -2
- package/src/modules/plugins/join-request-jobs.service.ts +675 -0
- package/src/modules/plugins/join-request-records.store.ts +353 -0
- package/src/modules/plugins/join-requests.service.ts +7 -4
- package/src/modules/plugins/plugin-links.service.ts +350 -0
- package/src/modules/plugins/plugin-links.ts +187 -0
- package/src/modules/plugins/plugin-provision.service.ts +356 -78
- package/src/modules/plugins/plugin-rename.service.ts +310 -0
- package/src/modules/plugins/plugins.contract.ts +79 -4
- package/src/modules/plugins/plugins.routes.ts +320 -123
- package/src/modules/plugins/plugins.service.ts +184 -57
- package/src/modules/plugins/plugins.tools.ts +88 -0
- package/src/modules/plugins/teams.routes.ts +129 -0
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +359 -15
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +248 -0
- package/src/modules/secrets-vault/__tests__/namespace-secrets.test.ts +202 -0
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +217 -35
- package/src/modules/secrets-vault/index.ts +1 -0
- package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +6 -3
- package/src/modules/secrets-vault/secrets-variable-loader.ts +4 -4
- package/src/modules/secrets-vault/secrets-vault.contract.ts +93 -0
- package/src/modules/secrets-vault/secrets-vault.routes.ts +99 -38
- package/src/modules/settings/__tests__/connection-check.test.ts +233 -0
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +240 -1
- package/src/modules/settings/__tests__/git-root-folders.test.ts +247 -0
- package/src/modules/settings/__tests__/oidc-check.test.ts +185 -0
- package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +119 -0
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +170 -0
- package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +555 -0
- package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +77 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +667 -16
- package/src/modules/settings/connection-check.ts +351 -0
- package/src/modules/settings/deployment-settings.service.ts +478 -39
- package/src/modules/settings/git-root-folders.ts +171 -0
- package/src/modules/settings/oidc-check.ts +229 -0
- package/src/modules/settings/setup.routes.ts +480 -110
- package/src/modules/skills/__tests__/allowed-tools-check.test.ts +224 -0
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +210 -0
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -3
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +128 -0
- package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +91 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +308 -2
- package/src/modules/skills/allowed-tools-check.ts +352 -0
- package/src/modules/skills/index.ts +5 -0
- package/src/modules/skills/pending-skills.service.ts +26 -85
- package/src/modules/skills/skill-access-requests.routes.ts +203 -0
- package/src/modules/skills/skills.contract.ts +67 -3
- package/src/modules/skills/skills.routes.ts +75 -4
- package/src/modules/skills/skills.service.ts +258 -66
- package/src/modules/skills/skills.tools.ts +43 -5
- package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +192 -0
- package/src/modules/tool-auth/__tests__/connection-keys-admin.routes.test.ts +116 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +124 -0
- package/src/modules/tool-auth/connection-key-rejection.ts +25 -0
- package/src/modules/tool-auth/connection-keys-admin.routes.ts +76 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +73 -1
- package/src/modules/tool-auth/external-api-key.service.ts +95 -29
- package/src/modules/tool-auth/internal-token.service.ts +5 -2
- package/src/modules/tool-auth/key-or-session.middleware.ts +45 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +16 -7
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +228 -0
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +33 -4
- package/src/modules/tool-helpers/tool-context.ts +46 -7
- package/src/modules/tool-helpers/tool-handler.ts +23 -5
- package/src/modules/tool-helpers/tool.contract.ts +5 -0
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +24 -1
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +411 -0
- package/src/modules/tool-manuals/__tests__/tool-delete.route.test.ts +133 -0
- package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +469 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +21 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +11 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +6 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +254 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +577 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +6 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.pending.route.test.ts +102 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +285 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +178 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +114 -2
- package/src/modules/tool-manuals/index.ts +3 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +164 -95
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +11 -14
- package/src/modules/tool-manuals/pending-tools.service.ts +179 -0
- package/src/modules/tool-manuals/tool-delete.service.ts +401 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +165 -0
- package/src/modules/tool-manuals/tool-manuals.routes.ts +138 -54
- package/src/modules/tool-manuals/tool-manuals.service.ts +440 -104
- package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -6
- package/src/modules/workflow/__tests__/apply-failure.test.ts +1038 -0
- package/src/modules/workflow/__tests__/event-bus.test.ts +61 -9
- package/src/modules/workflow/__tests__/fake-file-lock-db.ts +199 -0
- package/src/modules/workflow/__tests__/file-lock.service.path-identity.test.ts +237 -0
- package/src/modules/workflow/__tests__/format-affected-owners.test.ts +18 -0
- package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +220 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.concurrency.test.ts +187 -0
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +3 -1
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +10 -0
- package/src/modules/workflow/__tests__/workflow.routes.folder-proposals.test.ts +128 -0
- package/src/modules/workflow/__tests__/workflow.routes.fork-point-file.test.ts +149 -0
- package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +305 -0
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +2 -0
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +3 -0
- package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +2 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +296 -27
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +564 -0
- package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +135 -0
- package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +172 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +133 -2
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +442 -0
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +281 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +158 -7
- package/src/modules/workflow/agent-tools/workflow.tools.ts +93 -64
- package/src/modules/workflow/event-bus.ts +26 -28
- package/src/modules/workflow/events.routes.ts +56 -21
- package/src/modules/workflow/file-lock.service.ts +27 -4
- package/src/modules/workflow/git/__tests__/change-request-link.test.ts +43 -0
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +1 -0
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +238 -1
- package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +245 -0
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +35 -0
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +149 -0
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +47 -4
- package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +177 -0
- package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +253 -0
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +114 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +69 -2
- package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +121 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +79 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-update.test.ts +111 -0
- package/src/modules/workflow/git/change-request-link.ts +35 -0
- package/src/modules/workflow/git/git-version.ts +5 -2
- package/src/modules/workflow/git/git.service.ts +922 -199
- package/src/modules/workflow/git/node-git-runner.ts +315 -0
- package/src/modules/workflow/git/pull-request.service.ts +188 -27
- package/src/modules/workflow/pending-commits.service.ts +26 -7
- package/src/modules/workflow/pending-commits.worker.ts +114 -69
- package/src/modules/workflow/recovery-bot.ts +4 -1
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +180 -1
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
- package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +520 -0
- package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +84 -0
- package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +89 -77
- package/src/modules/workflow/review-workflow/approval-lock.ts +122 -0
- package/src/modules/workflow/review-workflow/review-workflow.interface.ts +59 -7
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +309 -105
- package/src/modules/workflow/sanitize-error.ts +9 -4
- package/src/modules/workflow/workflow.routes.ts +273 -29
- package/src/modules/workflow/workflow.service.ts +1341 -174
- package/src/modules/workspace/__tests__/empty-dirs.test.ts +44 -0
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +301 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +792 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +464 -0
- package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +139 -0
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +189 -37
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +51 -11
- package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +161 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +109 -10
- package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +624 -0
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +409 -0
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +327 -0
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +31 -13
- package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +109 -0
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +94 -0
- package/src/modules/workspace/__tests__/workspace.service.missing-branch.test.ts +28 -0
- package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +218 -0
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +98 -13
- package/src/modules/workspace/__tests__/workspace.service.test.ts +811 -42
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +197 -0
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +128 -0
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +212 -0
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +2632 -110
- package/src/modules/workspace/__tests__/write-denial.test.ts +75 -0
- package/src/modules/workspace/empty-dirs.ts +79 -0
- package/src/modules/workspace/file-readers/README.md +32 -0
- package/src/modules/workspace/file-readers/__tests__/doc-extract.test.ts +59 -2
- package/src/modules/workspace/file-readers/__tests__/file-reader.registry.test.ts +133 -4
- package/src/modules/workspace/file-readers/__tests__/fixtures/soft-break.docx +0 -0
- package/src/modules/workspace/file-readers/__tests__/fixtures/tab.docx +0 -0
- package/src/modules/workspace/file-readers/content-mode.ts +87 -0
- package/src/modules/workspace/file-readers/doc-extract.service.ts +4 -1
- package/src/modules/workspace/file-readers/doc-extract.types.ts +8 -4
- package/src/modules/workspace/file-readers/document-reader.ts +6 -0
- package/src/modules/workspace/file-readers/extract-docx.ts +6 -0
- package/src/modules/workspace/file-readers/file-reader.registry.ts +9 -1
- package/src/modules/workspace/file-readers/file-reader.ts +27 -6
- package/src/modules/workspace/file-readers/image-reader.ts +10 -5
- package/src/modules/workspace/file-readers/ooxml-text.ts +38 -2
- package/src/modules/workspace/file-readers/text-reader.ts +61 -4
- package/src/modules/workspace/git-internals.middleware.ts +86 -0
- package/src/modules/workspace/not-found.ts +119 -0
- package/src/modules/workspace/session-ontology.gate.ts +1 -1
- package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +181 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +361 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +53 -16
- package/src/modules/workspace/startup/beside-checkout.ts +112 -0
- package/src/modules/workspace/startup/kb-git.ts +43 -36
- package/src/modules/workspace/startup/kb-startup-runner.ts +303 -63
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +1405 -48
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +460 -417
- package/src/modules/workspace/startup/steps/personal-spaces.step.ts +191 -0
- package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +169 -0
- package/src/modules/workspace/startup/steps/plugin-layout.ts +110 -0
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +90 -0
- package/src/modules/workspace/startup/steps/roles-yaml.step.ts +6 -12
- package/src/modules/workspace/startup/steps/seed-tree.ts +177 -54
- package/src/modules/workspace/startup/steps/template-files.step.ts +581 -126
- package/src/modules/workspace/startup/steps/template-source.ts +168 -0
- package/src/modules/workspace/workspace.routes.ts +691 -269
- package/src/modules/workspace/workspace.service.ts +1071 -292
- package/src/modules/workspace/workspace.tools.ts +1787 -180
- package/src/modules/workspace/write-denial.ts +189 -0
- package/src/shared/__tests__/canonical-file-identity.test.ts +154 -0
- package/src/shared/__tests__/email-identity.test.ts +35 -0
- package/src/shared/__tests__/filename.test.ts +0 -0
- package/src/shared/__tests__/frontmatter-id.test.ts +5 -0
- package/src/shared/__tests__/git-failure.test.ts +198 -0
- package/src/shared/__tests__/git-internals.test.ts +115 -0
- package/src/shared/__tests__/http-errors.test.ts +42 -0
- package/src/shared/__tests__/logging.test.ts +185 -0
- package/src/shared/__tests__/path-containment.test.ts +60 -0
- package/src/shared/__tests__/path-order.test.ts +24 -0
- package/src/shared/__tests__/pending-proposals.readAt.test.ts +102 -0
- package/src/shared/__tests__/printable.test.ts +64 -0
- package/src/shared/__tests__/redact-secret.test.ts +122 -0
- package/src/shared/__tests__/rename-no-replace.test.ts +466 -0
- package/src/shared/__tests__/ssrf.test.ts +20 -0
- package/src/shared/__tests__/ttl-cache.test.ts +55 -0
- package/src/shared/canonical-file-identity.ts +92 -0
- package/src/shared/domain-errors.ts +178 -0
- package/src/shared/email-identity.ts +38 -0
- package/src/shared/fs.contract.ts +225 -0
- package/src/shared/git-failure.ts +196 -0
- package/src/shared/git-internals.ts +124 -0
- package/src/shared/git.contract.ts +170 -0
- package/src/shared/http-errors.ts +36 -0
- package/src/shared/logger.contract.ts +141 -0
- package/src/shared/logging.ts +96 -0
- package/src/shared/path-containment.ts +34 -0
- package/src/shared/path-order.ts +33 -0
- package/src/shared/pending-proposals.ts +161 -0
- package/src/shared/printable.ts +68 -0
- package/src/shared/redact-secret.ts +118 -0
- package/src/shared/rename-no-replace.ts +386 -0
- package/src/shared/ssrf.ts +44 -16
- package/src/shared/ttl-cache.ts +33 -1
- package/src/version.ts +10 -6
- package/src/modules/diff/diff-paths.ts +0 -9
- package/src/modules/mcp/__tests__/mcp-session-store.test.ts +0 -151
- package/src/modules/mcp/__tests__/mcp.routes.session.test.ts +0 -226
- package/src/modules/mcp/mcp-session-store.ts +0 -162
- package/src/shared/__tests__/fs-walk.test.ts +0 -42
- package/src/shared/fs-walk.ts +0 -30
- package/src/shared/hash-email.ts +0 -14
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import { promisify } from 'node:util';
|
|
3
|
+
import { logger } from '../../../shared/logging.js';
|
|
5
4
|
import type {
|
|
6
5
|
AuthUser,
|
|
7
6
|
BranchInfo,
|
|
8
7
|
CommitAttribution,
|
|
9
8
|
IGitService,
|
|
9
|
+
RemoteSyncPullResult,
|
|
10
10
|
PrFileStatus,
|
|
11
11
|
PullRequestFile,
|
|
12
12
|
ShareChangesRequest,
|
|
13
13
|
WorkingTreeStatus,
|
|
14
14
|
} from '@bevel-software/platform-shared';
|
|
15
|
+
import { isFolderPlaceholder } from '@bevel-software/platform-shared';
|
|
15
16
|
import type { WorkspaceService } from '../../workspace/workspace.service.js';
|
|
16
17
|
import type { WorkflowHooks, CommitValidationContext } from '../workflow-hooks.js';
|
|
17
18
|
import type { IAccessControl } from '../../access/access-control.interface.js';
|
|
18
19
|
import { AccessDeniedError } from '../../access-model/access-errors.js';
|
|
19
20
|
import { WorkspaceMutex } from '../../kb-fs/mutex.js';
|
|
21
|
+
import { isAbsence } from '../../../shared/fs.contract.js';
|
|
20
22
|
import { assertInsideRepo } from '../../kb-fs/repo-path.js';
|
|
21
23
|
import { cloneTrackingConfigArgs, SAFE_IMPLICIT_FETCH_ARGS } from '../../kb-fs/clone-config.js';
|
|
22
24
|
import {
|
|
@@ -29,22 +31,28 @@ import {
|
|
|
29
31
|
} from '../../kb-fs/branch-name.js';
|
|
30
32
|
import {
|
|
31
33
|
BranchAuthorshipError,
|
|
34
|
+
WorkflowDomainError,
|
|
32
35
|
WorkflowValidationError,
|
|
33
36
|
ProtectedBranchError,
|
|
34
37
|
PullRebaseConflictError,
|
|
38
|
+
RemoteBranchGoneError,
|
|
39
|
+
isMissingRemoteBranchFailure,
|
|
35
40
|
} from '../../../shared/domain-errors.js';
|
|
41
|
+
import {
|
|
42
|
+
GitRunError,
|
|
43
|
+
isGitTimeout,
|
|
44
|
+
redactGitToken,
|
|
45
|
+
type GitRunOptions,
|
|
46
|
+
type GitRunResult,
|
|
47
|
+
type IGitRunner,
|
|
48
|
+
} from '../../../shared/git.contract.js';
|
|
49
|
+
import { NodeGitRunner } from './node-git-runner.js';
|
|
50
|
+
import { printable } from '../../../shared/printable.js';
|
|
51
|
+
import { sanitizeError } from '../sanitize-error.js';
|
|
36
52
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
interface GitRunResult {
|
|
40
|
-
stdout: string;
|
|
41
|
-
stderr: string;
|
|
42
|
-
}
|
|
53
|
+
const log = logger('git');
|
|
54
|
+
const crLog = logger('cr');
|
|
43
55
|
|
|
44
|
-
function redact(msg: string): string {
|
|
45
|
-
const token = process.env.GITHUB_TOKEN;
|
|
46
|
-
return token ? msg.replaceAll(token, '***') : msg;
|
|
47
|
-
}
|
|
48
56
|
|
|
49
57
|
/**
|
|
50
58
|
* Fallback committer identity. Every workflow commit overrides the author via
|
|
@@ -240,24 +248,22 @@ async function synthesizeUntrackedSideDiff(
|
|
|
240
248
|
? synthesizeNewFileDiff(relativePath, text)
|
|
241
249
|
: synthesizeDeletedFileDiff(relativePath, text);
|
|
242
250
|
} catch (err) {
|
|
243
|
-
if ((err
|
|
251
|
+
if (isAbsence(err)) return '';
|
|
244
252
|
throw err;
|
|
245
253
|
}
|
|
246
254
|
}
|
|
247
255
|
|
|
248
256
|
/**
|
|
249
|
-
* `stat` for the stray check: false only when nothing can be there
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* committed.
|
|
257
|
+
* `stat` for the stray check: false only when nothing can be there — the
|
|
258
|
+
* disk's own definition of absence. Permission and I/O failures propagate:
|
|
259
|
+
* treating them as "absent" would let an unreadable stray pass as committed.
|
|
253
260
|
*/
|
|
254
261
|
async function existsForCommitCheck(absolutePath: string): Promise<boolean> {
|
|
255
262
|
try {
|
|
256
263
|
await fs.stat(absolutePath);
|
|
257
264
|
return true;
|
|
258
265
|
} catch (err) {
|
|
259
|
-
|
|
260
|
-
if (code === 'ENOENT' || code === 'ENOTDIR') return false;
|
|
266
|
+
if (isAbsence(err)) return false;
|
|
261
267
|
throw err;
|
|
262
268
|
}
|
|
263
269
|
}
|
|
@@ -293,6 +299,14 @@ export class GitService implements IGitService {
|
|
|
293
299
|
private readonly kbDirName: string,
|
|
294
300
|
private readonly mutex: WorkspaceMutex = new WorkspaceMutex(),
|
|
295
301
|
private readonly accessControl: IAccessControl | null = null,
|
|
302
|
+
/**
|
|
303
|
+
* How git is actually run — see `shared/git.contract.ts`. The composition
|
|
304
|
+
* root passes the one runner, carrying the deployment's configured
|
|
305
|
+
* deadline; the default here is the same runner on its default deadline,
|
|
306
|
+
* so a directly constructed service (every suite in this module) still has
|
|
307
|
+
* one rather than none.
|
|
308
|
+
*/
|
|
309
|
+
private readonly gitRunner: IGitRunner = new NodeGitRunner(),
|
|
296
310
|
) {}
|
|
297
311
|
|
|
298
312
|
/**
|
|
@@ -309,6 +323,19 @@ export class GitService implements IGitService {
|
|
|
309
323
|
this.pendingCommitsProbe = probe;
|
|
310
324
|
}
|
|
311
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Late-bound by the composition root: notified with the branch names every
|
|
328
|
+
* `listBranches` returns. A listing is the one moment the platform sees
|
|
329
|
+
* origin's set of branches, and the workspace layer needs that memory to
|
|
330
|
+
* tell a branch that was deleted from a name that never existed — see
|
|
331
|
+
* `WorkspaceService.noteBranchesListed`.
|
|
332
|
+
*/
|
|
333
|
+
private onBranchesListed: ((names: string[]) => void) | null = null;
|
|
334
|
+
|
|
335
|
+
setBranchesListedListener(listener: (names: string[]) => void): void {
|
|
336
|
+
this.onBranchesListed = listener;
|
|
337
|
+
}
|
|
338
|
+
|
|
312
339
|
/**
|
|
313
340
|
* Mark a workspace as freshly fetched. Called by `WorkspaceService` right
|
|
314
341
|
* after a branch's clone is created — `git clone` already downloaded every
|
|
@@ -324,6 +351,22 @@ export class GitService implements IGitService {
|
|
|
324
351
|
this.lastImplicitFetchOk.set(workspaceId, true);
|
|
325
352
|
}
|
|
326
353
|
|
|
354
|
+
/**
|
|
355
|
+
* The most recent attempt any workspace made to reach the remote, and
|
|
356
|
+
* whether it succeeded — for the readiness answer. Null before the first
|
|
357
|
+
* attempt of this process's life. Read off the per-workspace fetch record
|
|
358
|
+
* above rather than kept separately, so it cannot disagree with it.
|
|
359
|
+
*/
|
|
360
|
+
lastRemoteContact(): { at: number; ok: boolean } | null {
|
|
361
|
+
let latest: { at: number; ok: boolean } | null = null;
|
|
362
|
+
for (const [workspaceId, at] of this.lastImplicitFetchAt) {
|
|
363
|
+
if (latest === null || at > latest.at) {
|
|
364
|
+
latest = { at, ok: this.lastImplicitFetchOk.get(workspaceId) ?? false };
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return latest;
|
|
368
|
+
}
|
|
369
|
+
|
|
327
370
|
/**
|
|
328
371
|
* Run the registered ADVISORY commit-validation hooks at a commit site.
|
|
329
372
|
* Preserves the semantics of the injected validator this replaced: a
|
|
@@ -340,11 +383,11 @@ export class GitService implements IGitService {
|
|
|
340
383
|
try {
|
|
341
384
|
const report = await hook(ctx);
|
|
342
385
|
if (report && report.mustFix.length > 0) {
|
|
343
|
-
|
|
386
|
+
log.warn(formatWarning(report));
|
|
344
387
|
}
|
|
345
388
|
} catch (validatorErr) {
|
|
346
389
|
// Validator failure is non-fatal — advisory only.
|
|
347
|
-
|
|
390
|
+
log.warn('validator crashed (advisory only, ignoring):', { err: validatorErr });
|
|
348
391
|
}
|
|
349
392
|
}
|
|
350
393
|
}
|
|
@@ -517,6 +560,17 @@ export class GitService implements IGitService {
|
|
|
517
560
|
}
|
|
518
561
|
}),
|
|
519
562
|
);
|
|
563
|
+
// Every name this listing showed — origin's refs and the local heads
|
|
564
|
+
// alike, since a branch checked out here is one the platform has
|
|
565
|
+
// plainly heard of. A listener that misbehaves must not turn a good
|
|
566
|
+
// listing into a failed one.
|
|
567
|
+
try {
|
|
568
|
+
this.onBranchesListed?.(infos.map((info) => info.name));
|
|
569
|
+
} catch (err) {
|
|
570
|
+
log.error(`branches-listed listener failed for workspace ${workspaceId}:`, {
|
|
571
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
572
|
+
});
|
|
573
|
+
}
|
|
520
574
|
return infos;
|
|
521
575
|
});
|
|
522
576
|
}
|
|
@@ -572,7 +626,7 @@ export class GitService implements IGitService {
|
|
|
572
626
|
this.lastImplicitFetchOk.set(workspaceId, true);
|
|
573
627
|
} catch (err) {
|
|
574
628
|
const msg = err instanceof Error ? err.message : String(err);
|
|
575
|
-
|
|
629
|
+
log.warn(`implicit fetch failed for workspace ${workspaceId}: ${msg}`);
|
|
576
630
|
// Stamp the TTL on failure too so a hard-down origin doesn't make
|
|
577
631
|
// every subsequent listBranches retry the (still-failing) fetch and
|
|
578
632
|
// pile up 10s timeouts. Stale local refs are better than spinning.
|
|
@@ -935,19 +989,13 @@ export class GitService implements IGitService {
|
|
|
935
989
|
}
|
|
936
990
|
if (trackedAtHead) {
|
|
937
991
|
await this.git(cwd, ['checkout', 'HEAD', '--', repoRelativePath]).catch((err) => {
|
|
938
|
-
|
|
939
|
-
`[git] discardPath checkout failed for "${repoRelativePath}":`,
|
|
940
|
-
err instanceof Error ? err.message : err,
|
|
941
|
-
);
|
|
992
|
+
log.warn(`discardPath checkout failed for "${repoRelativePath}":`, { err });
|
|
942
993
|
});
|
|
943
994
|
} else {
|
|
944
995
|
// Untracked new file — remove from working tree.
|
|
945
996
|
const fileAbs = path.join(cwd, repoRelativePath);
|
|
946
997
|
await fs.rm(fileAbs, { force: true }).catch((err) => {
|
|
947
|
-
|
|
948
|
-
`[git] discardPath rm failed for "${repoRelativePath}":`,
|
|
949
|
-
err instanceof Error ? err.message : err,
|
|
950
|
-
);
|
|
998
|
+
log.warn(`discardPath rm failed for "${repoRelativePath}":`, { err });
|
|
951
999
|
});
|
|
952
1000
|
}
|
|
953
1001
|
});
|
|
@@ -963,8 +1011,30 @@ export class GitService implements IGitService {
|
|
|
963
1011
|
assertValidRelativePath(relativePath);
|
|
964
1012
|
assertValidAuthor(user);
|
|
965
1013
|
const repoRelativePath = this.stripRepoPrefix(relativePath);
|
|
966
|
-
return this.mutex.run(workspaceId, async () =>
|
|
967
|
-
|
|
1014
|
+
return this.mutex.run(workspaceId, async () =>
|
|
1015
|
+
this.commitFileUnlocked(
|
|
1016
|
+
workspaceId,
|
|
1017
|
+
await this.repoDir(workspaceId),
|
|
1018
|
+
user,
|
|
1019
|
+
relativePath,
|
|
1020
|
+
repoRelativePath,
|
|
1021
|
+
summary,
|
|
1022
|
+
skipValidator,
|
|
1023
|
+
),
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/** `commitFile`'s body, for a caller that already holds the workspace's reservation. */
|
|
1028
|
+
private async commitFileUnlocked(
|
|
1029
|
+
workspaceId: string,
|
|
1030
|
+
cwd: string,
|
|
1031
|
+
user: AuthUser,
|
|
1032
|
+
relativePath: string,
|
|
1033
|
+
repoRelativePath: string,
|
|
1034
|
+
summary?: string,
|
|
1035
|
+
skipValidator?: boolean,
|
|
1036
|
+
): Promise<CommitAttribution | null> {
|
|
1037
|
+
{
|
|
968
1038
|
const branch = await this.currentBranch(cwd);
|
|
969
1039
|
|
|
970
1040
|
// Path-scoped status — is this specific file dirty? `--porcelain` on
|
|
@@ -1035,7 +1105,7 @@ export class GitService implements IGitService {
|
|
|
1035
1105
|
subject: subj ?? subject,
|
|
1036
1106
|
committedAt: committedAt?.trim() ?? new Date().toISOString(),
|
|
1037
1107
|
};
|
|
1038
|
-
}
|
|
1108
|
+
}
|
|
1039
1109
|
}
|
|
1040
1110
|
|
|
1041
1111
|
/**
|
|
@@ -1192,8 +1262,19 @@ export class GitService implements IGitService {
|
|
|
1192
1262
|
user: AuthUser,
|
|
1193
1263
|
opts?: { systemAuthorized?: boolean },
|
|
1194
1264
|
): Promise<void> {
|
|
1195
|
-
return this.mutex.run(workspaceId, async () =>
|
|
1196
|
-
|
|
1265
|
+
return this.mutex.run(workspaceId, async () =>
|
|
1266
|
+
this.pushUnlocked(workspaceId, await this.repoDir(workspaceId), user, opts),
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/** `push`'s body, for a caller that already holds the workspace's reservation. */
|
|
1271
|
+
private async pushUnlocked(
|
|
1272
|
+
workspaceId: string,
|
|
1273
|
+
cwd: string,
|
|
1274
|
+
user: AuthUser,
|
|
1275
|
+
opts?: { systemAuthorized?: boolean },
|
|
1276
|
+
): Promise<void> {
|
|
1277
|
+
{
|
|
1197
1278
|
const branch = await this.currentBranch(cwd);
|
|
1198
1279
|
|
|
1199
1280
|
// Access gate fires only when pushing to a protected branch. Pushing
|
|
@@ -1222,7 +1303,7 @@ export class GitService implements IGitService {
|
|
|
1222
1303
|
}
|
|
1223
1304
|
|
|
1224
1305
|
await this.git(cwd, ['push', '-u', 'origin', branch]);
|
|
1225
|
-
}
|
|
1306
|
+
}
|
|
1226
1307
|
}
|
|
1227
1308
|
|
|
1228
1309
|
// `forcePush` method removed. The workflow layer no longer auto-force-pushes
|
|
@@ -1355,6 +1436,34 @@ export class GitService implements IGitService {
|
|
|
1355
1436
|
targetBranch: string,
|
|
1356
1437
|
commit: { subject: string; body: string },
|
|
1357
1438
|
user: AuthUser,
|
|
1439
|
+
opts: {
|
|
1440
|
+
/**
|
|
1441
|
+
* Refuse before touching the clone when the target's working tree holds
|
|
1442
|
+
* edits that are not published yet. A change request's merge runs in a
|
|
1443
|
+
* repo-global clone where nothing but a previous attempt can be dirty;
|
|
1444
|
+
* this method is also the agent `merge_branch` path, which runs in the
|
|
1445
|
+
* TARGET BRANCH'S OWN workspace — the one the file tools read and write.
|
|
1446
|
+
* There the `reset --hard` below would silently destroy a save.
|
|
1447
|
+
*
|
|
1448
|
+
* Checked HERE, inside the reservation, and not by the caller: a save
|
|
1449
|
+
* landing between a caller's check and this reset is exactly the window
|
|
1450
|
+
* that has to be closed.
|
|
1451
|
+
*/
|
|
1452
|
+
requireCleanTarget?: boolean;
|
|
1453
|
+
/**
|
|
1454
|
+
* Decide whether `user` may land this merge, given the target commit it
|
|
1455
|
+
* is actually built on and every path it writes. Throwing refuses, and
|
|
1456
|
+
* refuses before anything is committed or pushed.
|
|
1457
|
+
*
|
|
1458
|
+
* Also here rather than in the caller, and for the same reason: the
|
|
1459
|
+
* caller's workspace `HEAD` can be behind origin (a best-effort pull
|
|
1460
|
+
* that failed leaves it there), so authorizing against it would read
|
|
1461
|
+
* roles that the commit being published has already changed. The fetch
|
|
1462
|
+
* below is what makes the ref current, and a fetch that fails throws
|
|
1463
|
+
* instead of falling back to the stale one.
|
|
1464
|
+
*/
|
|
1465
|
+
authorize?: (target: { sha: string; changedPaths: string[] }) => Promise<void>;
|
|
1466
|
+
} = {},
|
|
1358
1467
|
): Promise<{ kind: 'merged'; sha: string } | { kind: 'conflicts'; paths: string[] }> {
|
|
1359
1468
|
assertValidBranchName(sourceBranch);
|
|
1360
1469
|
assertValidBranchName(targetBranch);
|
|
@@ -1368,6 +1477,17 @@ export class GitService implements IGitService {
|
|
|
1368
1477
|
// unknown" on a clone that lacks it).
|
|
1369
1478
|
await this.git(cwd, ['config', 'user.name', BOT_NAME]);
|
|
1370
1479
|
await this.git(cwd, ['config', 'user.email', BOT_EMAIL]);
|
|
1480
|
+
if (opts.requireCleanTarget) {
|
|
1481
|
+
const { stdout: dirty } = await this.git(cwd, ['status', '--porcelain=v1', '-z']);
|
|
1482
|
+
const unpushed = await this.hasUnpushedCommitsAt(cwd);
|
|
1483
|
+
if (parsePorcelainZ(dirty).length > 0 || unpushed) {
|
|
1484
|
+
throw new WorkflowDomainError(
|
|
1485
|
+
`"${targetBranch}" has edits that are not shared yet. Try the merge again once they are saved.`,
|
|
1486
|
+
409,
|
|
1487
|
+
{ kind: 'merge-target-busy', targetBranch },
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1371
1491
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
|
|
1372
1492
|
// Explicit destination refspecs, because the two `origin/<branch>`
|
|
1373
1493
|
// reads below MUST see the tips this fetch just retrieved. A bare
|
|
@@ -1395,6 +1515,20 @@ export class GitService implements IGitService {
|
|
|
1395
1515
|
await this.git(cwd, ['checkout', targetBranch]);
|
|
1396
1516
|
await this.git(cwd, ['reset', '--hard', `origin/${targetBranch}`]);
|
|
1397
1517
|
|
|
1518
|
+
if (opts.authorize) {
|
|
1519
|
+
// The tip this attempt merges into, and the paths it writes against
|
|
1520
|
+
// that tip. Re-read every attempt: a retry happens BECAUSE the
|
|
1521
|
+
// target moved, which can move its roles and its file set too.
|
|
1522
|
+
const { stdout: targetSha } = await this.git(cwd, ['rev-parse', `origin/${targetBranch}`]);
|
|
1523
|
+
const changedPaths = await this.prChangedPathsAt(
|
|
1524
|
+
cwd,
|
|
1525
|
+
`origin/${targetBranch}`,
|
|
1526
|
+
`origin/${sourceBranch}`,
|
|
1527
|
+
{ forAccessCheck: true },
|
|
1528
|
+
);
|
|
1529
|
+
await opts.authorize({ sha: targetSha.trim(), changedPaths });
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1398
1532
|
// `--no-commit --no-ff` so we author the merge commit as the human and
|
|
1399
1533
|
// never fast-forward past the merge record.
|
|
1400
1534
|
try {
|
|
@@ -1406,7 +1540,7 @@ export class GitService implements IGitService {
|
|
|
1406
1540
|
// Not a conflict — surface the underlying git error so the real cause
|
|
1407
1541
|
// (e.g. missing identity, unrelated histories) is diagnosable.
|
|
1408
1542
|
throw new Error(
|
|
1409
|
-
`git merge failed without detectable conflicts: ${
|
|
1543
|
+
`git merge failed without detectable conflicts: ${redactGitToken(
|
|
1410
1544
|
err instanceof Error ? err.message : String(err),
|
|
1411
1545
|
)}`,
|
|
1412
1546
|
);
|
|
@@ -1546,70 +1680,252 @@ export class GitService implements IGitService {
|
|
|
1546
1680
|
return `refs/remotes/origin/${branch}`;
|
|
1547
1681
|
}
|
|
1548
1682
|
|
|
1549
|
-
|
|
1683
|
+
/** A commit-free tree, for diffing against a HEAD that did not exist yet. */
|
|
1684
|
+
private static readonly EMPTY_TREE_SHA = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
|
|
1685
|
+
|
|
1686
|
+
/**
|
|
1687
|
+
* The checked-out branch by its symbolic ref, which an UNBORN HEAD (a clone
|
|
1688
|
+
* of an empty upstream) still has — `rev-parse --abbrev-ref HEAD` does not
|
|
1689
|
+
* resolve there, so the tolerant tree reads below would never be reached.
|
|
1690
|
+
*/
|
|
1691
|
+
private async checkedOutBranch(cwd: string): Promise<string> {
|
|
1692
|
+
const { stdout } = await this.git(cwd, ['symbolic-ref', '--short', 'HEAD']);
|
|
1693
|
+
return stdout.trim();
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* Whether origin still has `branch`. `ls-remote --exit-code` answers with
|
|
1698
|
+
* exit status 2 for "no such ref" — the one failure that is a fact about the
|
|
1699
|
+
* branch rather than about reaching the host, which propagates.
|
|
1700
|
+
*/
|
|
1701
|
+
async remoteBranchExists(workspaceId: string, branch: string): Promise<boolean> {
|
|
1702
|
+
assertValidBranchName(branch);
|
|
1703
|
+
const cwd = await this.repoDir(workspaceId);
|
|
1704
|
+
try {
|
|
1705
|
+
await this.git(cwd, ['ls-remote', '--exit-code', '--heads', 'origin', `refs/heads/${branch}`]);
|
|
1706
|
+
return true;
|
|
1707
|
+
} catch (err) {
|
|
1708
|
+
if ((err as { exitCode?: number }).exitCode === 2) return false;
|
|
1709
|
+
throw err;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
/** Whether origin has ANY branch — an empty repository has none. */
|
|
1714
|
+
private async remoteHasAnyBranch(cwd: string): Promise<boolean> {
|
|
1715
|
+
const { stdout } = await this.git(cwd, ['ls-remote', '--heads', 'origin']);
|
|
1716
|
+
return stdout.trim().length > 0;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
/** `git rev-parse --verify` that answers null for a rev that does not resolve (an unborn HEAD). */
|
|
1720
|
+
private async revParseOrNull(cwd: string, rev: string): Promise<string | null> {
|
|
1721
|
+
try {
|
|
1722
|
+
const { stdout } = await this.git(cwd, ['rev-parse', '--verify', '--quiet', rev]);
|
|
1723
|
+
return stdout.trim() || null;
|
|
1724
|
+
} catch (err) {
|
|
1725
|
+
// Null is "no such rev"; a deadline is not that answer (see GitRunError).
|
|
1726
|
+
if (isGitTimeout(err)) throw err;
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
/**
|
|
1732
|
+
* Refresh `refs/remotes/origin/<branch>` for a sync, naming the one failure
|
|
1733
|
+
* that is not "origin unreachable": the branch no longer exists there.
|
|
1734
|
+
*/
|
|
1735
|
+
private async refreshRemoteBranchRefForSync(cwd: string, branch: string): Promise<string> {
|
|
1736
|
+
try {
|
|
1737
|
+
return await this.refreshRemoteBranchRef(cwd, branch);
|
|
1738
|
+
} catch (err) {
|
|
1739
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1740
|
+
if (isMissingRemoteBranchFailure(msg)) throw new RemoteBranchGoneError(branch);
|
|
1741
|
+
throw err;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* Rebase the checked-out branch onto `remoteRef`, turning a content
|
|
1747
|
+
* conflict into the typed error the workflow layer queues recovery for.
|
|
1748
|
+
* Shared by `pull` and `syncFromRemote` so the two cannot drift.
|
|
1749
|
+
*/
|
|
1750
|
+
private async rebaseOntoRemote(cwd: string, branch: string, remoteRef: string): Promise<void> {
|
|
1751
|
+
// An UNBORN HEAD (a clone of an upstream that was empty when cloned) has
|
|
1752
|
+
// no commit to replay and `git rebase` refuses to start from it, so the
|
|
1753
|
+
// branch is moved to the remote commit instead. That is only safe while
|
|
1754
|
+
// the index is EMPTY: files staged in an unborn clone are tracked, and a
|
|
1755
|
+
// hard reset would destroy them where the normal path autostashes — and
|
|
1756
|
+
// `git stash` cannot run without an initial commit. So a non-empty index
|
|
1757
|
+
// is refused as a conflict, naming the staged paths, and takes the same
|
|
1758
|
+
// recovery path a rebase conflict does; nothing on disk is touched.
|
|
1759
|
+
if ((await this.revParseOrNull(cwd, 'HEAD')) === null) {
|
|
1760
|
+
// `-z`: NUL-delimited and unquoted. Line output quotes a path with a
|
|
1761
|
+
// non-ASCII or control character ("Entw\303\274rfe.md"), and that
|
|
1762
|
+
// spelling would name a file that does not exist to whoever recovers.
|
|
1763
|
+
const { stdout: stagedOut } = await this.git(cwd, ['ls-files', '--cached', '-z']);
|
|
1764
|
+
const staged = stagedOut.split('\0').filter(Boolean);
|
|
1765
|
+
if (staged.length > 0) {
|
|
1766
|
+
throw new PullRebaseConflictError(
|
|
1767
|
+
branch,
|
|
1768
|
+
staged,
|
|
1769
|
+
'The clone has files staged before its first commit; they cannot be replayed onto the remote branch automatically.',
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1772
|
+
await this.git(cwd, ['reset', '--hard', remoteRef]);
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
try {
|
|
1776
|
+
// `--autostash` is load-bearing for the cooperative push recovery.
|
|
1777
|
+
// Under save=share the working tree is *supposed* to stay clean, but
|
|
1778
|
+
// in practice it can carry modified tracked artifacts (e.g. the
|
|
1779
|
+
// validator regenerates a dashboard HTML on every commit) that the
|
|
1780
|
+
// single-file commit didn't pick up. A rebase without it aborts
|
|
1781
|
+
// outright on those — "cannot rebase: You have unstaged
|
|
1782
|
+
// changes" — which is exactly the failure that strands a diverged
|
|
1783
|
+
// branch in an unrecoverable loop (the retry push stays non-fast-
|
|
1784
|
+
// forward forever). Autostash stashes those modifications, rebases
|
|
1785
|
+
// the local commits onto origin, then reapplies them, so the retry
|
|
1786
|
+
// push can fast-forward. Untracked files don't block rebase and are
|
|
1787
|
+
// left untouched.
|
|
1788
|
+
await this.git(cwd, ['rebase', '--autostash', remoteRef]);
|
|
1789
|
+
} catch (err) {
|
|
1790
|
+
// Capture the contested paths BEFORE the abort wipes the rebase
|
|
1791
|
+
// state — `--diff-filter=U` lists exactly the files whose replay
|
|
1792
|
+
// conflicted. Best-effort: an empty list just means this wasn't a
|
|
1793
|
+
// content conflict (or the probe itself failed) and the raw error
|
|
1794
|
+
// is surfaced unchanged.
|
|
1795
|
+
let conflictedPaths: string[] = [];
|
|
1796
|
+
try {
|
|
1797
|
+
const { stdout } = await this.git(cwd, ['diff', '--name-only', '--diff-filter=U']);
|
|
1798
|
+
conflictedPaths = stdout.split('\n').map((s) => s.trim()).filter(Boolean);
|
|
1799
|
+
} catch {
|
|
1800
|
+
// fall through with an empty list
|
|
1801
|
+
}
|
|
1802
|
+
// A failed rebase leaves the repo in a "REBASE_HEAD" / detached-apply state,
|
|
1803
|
+
// which makes every subsequent git command behave unexpectedly. Return the
|
|
1804
|
+
// working tree to a clean state before surfacing the original error.
|
|
1805
|
+
let abortSucceeded = true;
|
|
1806
|
+
await this.git(cwd, ['rebase', '--abort']).catch(() => {
|
|
1807
|
+
abortSucceeded = false;
|
|
1808
|
+
});
|
|
1809
|
+
if (abortSucceeded && conflictedPaths.length > 0) {
|
|
1810
|
+
// Typed so the workflow layer can queue background recovery — this
|
|
1811
|
+
// divergence never resolves on its own (every retry pull hits the
|
|
1812
|
+
// same conflict) and the unpushed local commits are someone's saved
|
|
1813
|
+
// content stuck violating save=share.
|
|
1814
|
+
//
|
|
1815
|
+
// Gated on the abort succeeding: a failed abort means either no
|
|
1816
|
+
// rebase was ever active (the unmerged paths pre-date this pull —
|
|
1817
|
+
// some earlier operation left the index broken) or the clone is in
|
|
1818
|
+
// a state git itself can't unwind. Queueing recovery there would
|
|
1819
|
+
// point the worker's commitFile at a repo mid-conflict, risking a
|
|
1820
|
+
// commit full of conflict markers — surface the raw error instead
|
|
1821
|
+
// and leave diagnosis to a human.
|
|
1822
|
+
throw new PullRebaseConflictError(
|
|
1823
|
+
branch,
|
|
1824
|
+
conflictedPaths,
|
|
1825
|
+
err instanceof Error ? err.message : String(err),
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
throw err;
|
|
1829
|
+
}
|
|
1830
|
+
// `--autostash` reapplies the stashed edits AFTER the rebase, and when that
|
|
1831
|
+
// reapply conflicts git exits 0: the clone is left with an unmerged index,
|
|
1832
|
+
// the edits kept in `stash@{0}`, and nothing above has thrown. Read the
|
|
1833
|
+
// index rather than the exit status. The rebase itself landed, so the
|
|
1834
|
+
// branch is at the remote commit; resetting to it drops only the
|
|
1835
|
+
// half-applied stash (still in the stash list) and lets the conflict
|
|
1836
|
+
// travel the same typed path a rebase conflict does.
|
|
1837
|
+
const { stdout: unmergedOut } = await this.git(cwd, ['diff', '--name-only', '--diff-filter=U']);
|
|
1838
|
+
const unmerged = unmergedOut.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
1839
|
+
if (unmerged.length > 0) {
|
|
1840
|
+
await this.git(cwd, ['reset', '--hard', 'HEAD']);
|
|
1841
|
+
throw new PullRebaseConflictError(
|
|
1842
|
+
branch,
|
|
1843
|
+
unmerged,
|
|
1844
|
+
'Reapplying the autostashed working-tree edits conflicted; the edits are kept in git stash.',
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
/**
|
|
1850
|
+
* Repo-relative paths whose content differs between two commits. Rename-
|
|
1851
|
+
* aware: a renamed file reports both its old and new path, so a viewer
|
|
1852
|
+
* holding the OLD path learns it is gone. Caller holds the mutex.
|
|
1853
|
+
*/
|
|
1854
|
+
private async changedPathsBetween(cwd: string, fromSha: string, toSha: string): Promise<string[]> {
|
|
1855
|
+
const { stdout } = await this.git(cwd, [
|
|
1856
|
+
'diff', '--name-status', '-M', '--no-color', '-z', fromSha, toSha,
|
|
1857
|
+
]);
|
|
1858
|
+
const fields = stdout.split('\0');
|
|
1859
|
+
const paths = new Set<string>();
|
|
1860
|
+
for (let i = 0; i < fields.length; ) {
|
|
1861
|
+
const status = fields[i];
|
|
1862
|
+
if (!status) break;
|
|
1863
|
+
if (status.startsWith('R') || status.startsWith('C')) {
|
|
1864
|
+
if (fields[i + 1]) paths.add(fields[i + 1]);
|
|
1865
|
+
if (fields[i + 2]) paths.add(fields[i + 2]);
|
|
1866
|
+
i += 3;
|
|
1867
|
+
} else {
|
|
1868
|
+
if (fields[i + 1]) paths.add(fields[i + 1]);
|
|
1869
|
+
i += 2;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
return [...paths];
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
async syncFromRemote(workspaceId: string): Promise<RemoteSyncPullResult> {
|
|
1876
|
+
// ONE mutex section for everything the sync reports about this clone.
|
|
1877
|
+
// Bracketing `pull` with separate HEAD reads and a diff took the hold
|
|
1878
|
+
// four times, and a save landing between any two of them was announced
|
|
1879
|
+
// back to its author as a "Git sync" change with the wrong sha.
|
|
1550
1880
|
return this.mutex.run(workspaceId, async () => {
|
|
1551
1881
|
const cwd = await this.repoDir(workspaceId);
|
|
1552
|
-
const branch = await this.
|
|
1882
|
+
const branch = await this.checkedOutBranch(cwd);
|
|
1883
|
+
const before = await this.revParseOrNull(cwd, 'HEAD');
|
|
1884
|
+
const treeBefore = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
|
|
1885
|
+
let remoteRef: string;
|
|
1553
1886
|
try {
|
|
1554
|
-
|
|
1555
|
-
// `refreshRemoteBranchRef` for why both halves are load-bearing.
|
|
1556
|
-
//
|
|
1557
|
-
// `--autostash` is load-bearing for the cooperative push recovery.
|
|
1558
|
-
// Under save=share the working tree is *supposed* to stay clean, but
|
|
1559
|
-
// in practice it can carry modified tracked artifacts (e.g. the
|
|
1560
|
-
// validator regenerates a dashboard HTML on every commit) that the
|
|
1561
|
-
// single-file commit didn't pick up. A rebase without it aborts
|
|
1562
|
-
// outright on those — "cannot rebase: You have unstaged
|
|
1563
|
-
// changes" — which is exactly the failure that strands a diverged
|
|
1564
|
-
// branch in an unrecoverable loop (the retry push stays non-fast-
|
|
1565
|
-
// forward forever). Autostash stashes those modifications, rebases
|
|
1566
|
-
// the local commits onto origin, then reapplies them, so the retry
|
|
1567
|
-
// push can fast-forward. Untracked files don't block rebase and are
|
|
1568
|
-
// left untouched.
|
|
1569
|
-
const remoteRef = await this.refreshRemoteBranchRef(cwd, branch);
|
|
1570
|
-
await this.git(cwd, ['rebase', '--autostash', remoteRef]);
|
|
1887
|
+
remoteRef = await this.refreshRemoteBranchRefForSync(cwd, branch);
|
|
1571
1888
|
} catch (err) {
|
|
1572
|
-
//
|
|
1573
|
-
//
|
|
1574
|
-
//
|
|
1575
|
-
//
|
|
1576
|
-
// is
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
const { stdout } = await this.git(cwd, ['diff', '--name-only', '--diff-filter=U']);
|
|
1580
|
-
conflictedPaths = stdout.split('\n').map((s) => s.trim()).filter(Boolean);
|
|
1581
|
-
} catch {
|
|
1582
|
-
// fall through with an empty list
|
|
1583
|
-
}
|
|
1584
|
-
// A failed rebase leaves the repo in a "REBASE_HEAD" / detached-apply state,
|
|
1585
|
-
// which makes every subsequent git command behave unexpectedly. Return the
|
|
1586
|
-
// working tree to a clean state before surfacing the original error.
|
|
1587
|
-
let abortSucceeded = true;
|
|
1588
|
-
await this.git(cwd, ['rebase', '--abort']).catch(() => {
|
|
1589
|
-
abortSucceeded = false;
|
|
1590
|
-
});
|
|
1591
|
-
if (abortSucceeded && conflictedPaths.length > 0) {
|
|
1592
|
-
// Typed so the workflow layer can queue background recovery — this
|
|
1593
|
-
// divergence never resolves on its own (every retry pull hits the
|
|
1594
|
-
// same conflict) and the unpushed local commits are someone's saved
|
|
1595
|
-
// content stuck violating save=share.
|
|
1596
|
-
//
|
|
1597
|
-
// Gated on the abort succeeding: a failed abort means either no
|
|
1598
|
-
// rebase was ever active (the unmerged paths pre-date this pull —
|
|
1599
|
-
// some earlier operation left the index broken) or the clone is in
|
|
1600
|
-
// a state git itself can't unwind. Queueing recovery there would
|
|
1601
|
-
// point the worker's commitFile at a repo mid-conflict, risking a
|
|
1602
|
-
// commit full of conflict markers — surface the raw error instead
|
|
1603
|
-
// and leave diagnosis to a human.
|
|
1604
|
-
throw new PullRebaseConflictError(
|
|
1605
|
-
branch,
|
|
1606
|
-
conflictedPaths,
|
|
1607
|
-
err instanceof Error ? err.message : String(err),
|
|
1608
|
-
);
|
|
1889
|
+
// An unborn clone whose branch origin does not have either: on a FRESH
|
|
1890
|
+
// deployment (origin has no branch at all) nobody has pushed yet —
|
|
1891
|
+
// nothing to sync, nothing to retire. But an origin that does have
|
|
1892
|
+
// branches and lacks this one has had this branch created and deleted
|
|
1893
|
+
// since the clone was made, and the clone is as stale as any other.
|
|
1894
|
+
if (err instanceof RemoteBranchGoneError && before === null && !(await this.remoteHasAnyBranch(cwd))) {
|
|
1895
|
+
return { before: null, after: null, treeChanged: false, changedPaths: [] };
|
|
1609
1896
|
}
|
|
1610
1897
|
throw err;
|
|
1611
1898
|
}
|
|
1899
|
+
await this.rebaseOntoRemote(cwd, branch, remoteRef);
|
|
1900
|
+
this.accessControl?.invalidate(workspaceId);
|
|
1901
|
+
const after = await this.revParseOrNull(cwd, 'HEAD');
|
|
1902
|
+
const treeAfter = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
|
|
1903
|
+
const treeChanged = treeAfter !== treeBefore;
|
|
1904
|
+
const changedPaths =
|
|
1905
|
+
treeChanged && after
|
|
1906
|
+
? await this.changedPathsBetween(cwd, before ?? GitService.EMPTY_TREE_SHA, after)
|
|
1907
|
+
: [];
|
|
1908
|
+
return { before, after, treeChanged, changedPaths };
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
async pull(workspaceId: string): Promise<{ treeChanged: boolean }> {
|
|
1913
|
+
return this.mutex.run(workspaceId, async () => {
|
|
1914
|
+
const cwd = await this.repoDir(workspaceId);
|
|
1915
|
+
const branch = await this.checkedOutBranch(cwd);
|
|
1916
|
+
// For `treeChanged` (see IGitService.pull): TREE ids, not commit ids —
|
|
1917
|
+
// a pull that lands only content-identical commits (an empty commit)
|
|
1918
|
+
// must not read as a content change. Tolerant of an unborn HEAD (a
|
|
1919
|
+
// clone of an empty upstream): "" before + a tree after correctly
|
|
1920
|
+
// reads as changed, "" both sides as not.
|
|
1921
|
+
const treeBefore = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
|
|
1922
|
+
// Refresh WITHOUT `git pull`, and without touching `FETCH_HEAD` — see
|
|
1923
|
+
// `refreshRemoteBranchRef` for why both halves are load-bearing.
|
|
1924
|
+
const remoteRef = await this.refreshRemoteBranchRef(cwd, branch);
|
|
1925
|
+
await this.rebaseOntoRemote(cwd, branch, remoteRef);
|
|
1612
1926
|
this.accessControl?.invalidate(workspaceId);
|
|
1927
|
+
const treeAfter = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
|
|
1928
|
+
return { treeChanged: treeAfter !== treeBefore };
|
|
1613
1929
|
});
|
|
1614
1930
|
}
|
|
1615
1931
|
|
|
@@ -1633,20 +1949,129 @@ export class GitService implements IGitService {
|
|
|
1633
1949
|
* only ever see settled states.
|
|
1634
1950
|
*/
|
|
1635
1951
|
async hasUnpushedCommits(workspaceId: string): Promise<boolean> {
|
|
1636
|
-
return this.mutex.run(workspaceId, async () =>
|
|
1637
|
-
const cwd = await this.repoDir(workspaceId);
|
|
1638
|
-
const branch = await this.currentBranch(cwd);
|
|
1639
|
-
try {
|
|
1640
|
-
const { stdout } = await this.git(cwd, [
|
|
1641
|
-
'rev-list', '--count', `refs/remotes/origin/${branch}..HEAD`,
|
|
1642
|
-
]);
|
|
1643
|
-
return Number(stdout.trim()) > 0;
|
|
1644
|
-
} catch {
|
|
1645
|
-
return true;
|
|
1646
|
-
}
|
|
1647
|
-
});
|
|
1952
|
+
return this.mutex.run(workspaceId, async () => this.hasUnpushedCommitsAt(await this.repoDir(workspaceId)));
|
|
1648
1953
|
}
|
|
1649
1954
|
|
|
1955
|
+
/**
|
|
1956
|
+
* `hasUnpushedCommits` without the workspace reservation — for a caller that
|
|
1957
|
+
* already holds one (`mergeChangeRequest`, which has to ask inside the same
|
|
1958
|
+
* reservation as the reset that would discard the answer). Fails closed: a
|
|
1959
|
+
* question that cannot be answered counts as "yes, unpushed".
|
|
1960
|
+
*/
|
|
1961
|
+
private async hasUnpushedCommitsAt(cwd: string): Promise<boolean> {
|
|
1962
|
+
const branch = await this.currentBranch(cwd);
|
|
1963
|
+
try {
|
|
1964
|
+
const { stdout } = await this.git(cwd, [
|
|
1965
|
+
'rev-list', '--count', `refs/remotes/origin/${branch}..HEAD`,
|
|
1966
|
+
]);
|
|
1967
|
+
return Number(stdout.trim()) > 0;
|
|
1968
|
+
} catch {
|
|
1969
|
+
return true;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/**
|
|
1974
|
+
* Revert paths in several workspaces and publish the result as ONE git
|
|
1975
|
+
* operation. Every workspace involved is reserved together (`runAll`), and
|
|
1976
|
+
* reading each HEAD, restoring and committing every path, pushing each
|
|
1977
|
+
* branch and — when a step fails — undoing, all happen inside that single
|
|
1978
|
+
* reservation: no other git operation on those workspaces can land between
|
|
1979
|
+
* the HEAD an undo returns to and the commits it undoes.
|
|
1980
|
+
*
|
|
1981
|
+
* - Each workspace's HEAD is read first; one that can't be read throws
|
|
1982
|
+
* before anything changes.
|
|
1983
|
+
* - Every path in every workspace is restored from its `ref` and committed
|
|
1984
|
+
* (`subject`) before anything is pushed. A failed restore or commit
|
|
1985
|
+
* undoes all of them and rethrows: nothing is pushed.
|
|
1986
|
+
* - Branches are then pushed in order. A failed push undoes that workspace
|
|
1987
|
+
* and every later one and is reported in `failed`; the earlier pushes
|
|
1988
|
+
* stand, since a remote can't be un-pushed.
|
|
1989
|
+
*
|
|
1990
|
+
* An undo never rewrites history: each touched path is restored from the
|
|
1991
|
+
* HEAD recorded at the start and committed (`undoSubject`), so unpushed work
|
|
1992
|
+
* the checkout already had stays exactly as it was. Each path is undone on
|
|
1993
|
+
* its own, so one that fails doesn't leave the rest reverted.
|
|
1994
|
+
*/
|
|
1995
|
+
async revertPathsAndPush(
|
|
1996
|
+
user: AuthUser,
|
|
1997
|
+
plans: {
|
|
1998
|
+
workspaceId: string;
|
|
1999
|
+
paths: { path: string; ref: string; subject: string; undoSubject: string }[];
|
|
2000
|
+
}[],
|
|
2001
|
+
): Promise<{ pushed: string[]; failed: { workspaceId: string; error: unknown } | null }> {
|
|
2002
|
+
assertValidAuthor(user);
|
|
2003
|
+
for (const plan of plans) for (const entry of plan.paths) assertValidRelativePath(entry.path);
|
|
2004
|
+
return this.mutex.runAll(
|
|
2005
|
+
plans.map((plan) => plan.workspaceId),
|
|
2006
|
+
async () => {
|
|
2007
|
+
const steps: {
|
|
2008
|
+
workspaceId: string;
|
|
2009
|
+
cwd: string;
|
|
2010
|
+
head: string;
|
|
2011
|
+
paths: (typeof plans)[number]['paths'];
|
|
2012
|
+
}[] = [];
|
|
2013
|
+
for (const plan of plans) {
|
|
2014
|
+
const cwd = await this.repoDir(plan.workspaceId);
|
|
2015
|
+
const { stdout } = await this.git(cwd, ['rev-parse', '--verify', 'HEAD^{commit}']);
|
|
2016
|
+
steps.push({ workspaceId: plan.workspaceId, cwd, head: stdout.trim(), paths: plan.paths });
|
|
2017
|
+
}
|
|
2018
|
+
const undo = async (unpushed: typeof steps) => {
|
|
2019
|
+
for (const step of unpushed) {
|
|
2020
|
+
for (const entry of step.paths) {
|
|
2021
|
+
try {
|
|
2022
|
+
await this.restorePathUnlocked(step.cwd, step.head, entry.path);
|
|
2023
|
+
await this.commitFileUnlocked(
|
|
2024
|
+
step.workspaceId,
|
|
2025
|
+
step.cwd,
|
|
2026
|
+
user,
|
|
2027
|
+
entry.path,
|
|
2028
|
+
entry.path,
|
|
2029
|
+
entry.undoSubject,
|
|
2030
|
+
true, // skipValidator — this puts back the checkout's own version
|
|
2031
|
+
);
|
|
2032
|
+
} catch (err) {
|
|
2033
|
+
log.warn(
|
|
2034
|
+
`undo of ${printable(entry.path)} failed in workspace ${printable(step.workspaceId)}: ${printable(sanitizeError(err))}`,
|
|
2035
|
+
);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
try {
|
|
2041
|
+
for (const step of steps) {
|
|
2042
|
+
for (const entry of step.paths) {
|
|
2043
|
+
await this.restorePathUnlocked(step.cwd, entry.ref, entry.path);
|
|
2044
|
+
await this.commitFileUnlocked(
|
|
2045
|
+
step.workspaceId,
|
|
2046
|
+
step.cwd,
|
|
2047
|
+
user,
|
|
2048
|
+
entry.path,
|
|
2049
|
+
entry.path,
|
|
2050
|
+
entry.subject,
|
|
2051
|
+
true, // skipValidator — this restores an already-validated version
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
} catch (err) {
|
|
2056
|
+
await undo(steps);
|
|
2057
|
+
throw err;
|
|
2058
|
+
}
|
|
2059
|
+
const pushed: string[] = [];
|
|
2060
|
+
for (const [i, step] of steps.entries()) {
|
|
2061
|
+
try {
|
|
2062
|
+
await this.pushUnlocked(step.workspaceId, step.cwd, user);
|
|
2063
|
+
} catch (error) {
|
|
2064
|
+
await undo(steps.slice(i));
|
|
2065
|
+
return { pushed, failed: { workspaceId: step.workspaceId, error } };
|
|
2066
|
+
}
|
|
2067
|
+
pushed.push(step.workspaceId);
|
|
2068
|
+
}
|
|
2069
|
+
return { pushed, failed: null };
|
|
2070
|
+
},
|
|
2071
|
+
);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
|
|
1650
2075
|
/**
|
|
1651
2076
|
* Hard-reset the workspace's checked-out branch to `origin/<branch>`, fetching
|
|
1652
2077
|
* first. This is the break-glass primitive behind roles.yaml recovery: it
|
|
@@ -2036,8 +2461,8 @@ export class GitService implements IGitService {
|
|
|
2036
2461
|
const cwd = await this.repoDir(workspaceId);
|
|
2037
2462
|
await this.fetchPrRefs(cwd, baseBranch, headBranch);
|
|
2038
2463
|
return this.mutex.run(workspaceId, async () => {
|
|
2039
|
-
const baseRef = await this.
|
|
2040
|
-
const headRef = await this.
|
|
2464
|
+
const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
|
|
2465
|
+
const headRef = await this.resolvePublishedBranchRef(cwd, headBranch);
|
|
2041
2466
|
const [{ stdout: baseSha }, { stdout: headSha }] = await Promise.all([
|
|
2042
2467
|
this.git(cwd, ['rev-parse', baseRef]),
|
|
2043
2468
|
this.git(cwd, ['rev-parse', headRef]),
|
|
@@ -2086,8 +2511,8 @@ export class GitService implements IGitService {
|
|
|
2086
2511
|
const cwd = await this.repoDir(workspaceId);
|
|
2087
2512
|
if (!opts.at) await this.fetchPrRefs(cwd, baseBranch, headBranch);
|
|
2088
2513
|
return this.mutex.run(workspaceId, async () => {
|
|
2089
|
-
const baseRef = opts.at ? opts.at.baseSha : await this.
|
|
2090
|
-
const headRef = opts.at ? opts.at.headSha : await this.
|
|
2514
|
+
const baseRef = opts.at ? opts.at.baseSha : await this.resolvePublishedBranchRef(cwd, baseBranch);
|
|
2515
|
+
const headRef = opts.at ? opts.at.headSha : await this.resolvePublishedBranchRef(cwd, headBranch);
|
|
2091
2516
|
const range = `${baseRef}...${headRef}`; // three-dot = changes on head since merge-base
|
|
2092
2517
|
|
|
2093
2518
|
const [{ stdout: nameStatusOut }, { stdout: numstatOut }] = await Promise.all([
|
|
@@ -2103,8 +2528,17 @@ export class GitService implements IGitService {
|
|
|
2103
2528
|
// land. Filter it from the review surface entirely; the neutralisation
|
|
2104
2529
|
// reads the raw refs itself and is unaffected. Both lists are filtered
|
|
2105
2530
|
// IN STEP so the index-zip below stays aligned.
|
|
2106
|
-
|
|
2107
|
-
|
|
2531
|
+
// The empty-folder placeholder is filtered the same way: it is never
|
|
2532
|
+
// content, so it is not a file to review or approve. It still merges
|
|
2533
|
+
// with the rest, and `changedPathsForPr` keeps it, so a request that
|
|
2534
|
+
// only creates a folder is not mistaken for an empty one and closed.
|
|
2535
|
+
// An empty file deleted as its folder gets the (equally empty)
|
|
2536
|
+
// placeholder reads to `-M` as a RENAME onto it; the real side of such
|
|
2537
|
+
// a pair is first made the plain removal (or addition) it is, so
|
|
2538
|
+
// dropping the placeholder never drops the file with it.
|
|
2539
|
+
statuses = statuses.map(withoutPlaceholderRename);
|
|
2540
|
+
if (statuses.some((s) => s.path === 'roles.yaml' || isFolderPlaceholder(s.path))) {
|
|
2541
|
+
const keep = statuses.map((s) => s.path !== 'roles.yaml' && !isFolderPlaceholder(s.path));
|
|
2108
2542
|
statuses = statuses.filter((_, i) => keep[i]);
|
|
2109
2543
|
if (counts.length === keep.length) counts = counts.filter((_, i) => keep[i]);
|
|
2110
2544
|
}
|
|
@@ -2116,8 +2550,8 @@ export class GitService implements IGitService {
|
|
|
2116
2550
|
// +/- to a file.
|
|
2117
2551
|
const aligned = counts.length === statuses.length;
|
|
2118
2552
|
if (!aligned) {
|
|
2119
|
-
|
|
2120
|
-
`
|
|
2553
|
+
crLog.warn(
|
|
2554
|
+
`diff name-status/numstat length mismatch (${statuses.length} vs ${counts.length}) ` +
|
|
2121
2555
|
`for ${range} — reporting file list without +/- counts`,
|
|
2122
2556
|
);
|
|
2123
2557
|
}
|
|
@@ -2153,35 +2587,103 @@ export class GitService implements IGitService {
|
|
|
2153
2587
|
* Just the repo-relative paths changed by a change request (three-dot), no
|
|
2154
2588
|
* patches — the cheap version used to build CR-list summaries and owner
|
|
2155
2589
|
* routing. `changedFilesForPr` is the full version with statuses + diffs.
|
|
2590
|
+
*
|
|
2591
|
+
* `fetch: false` skips the per-request ref fetch, for a caller that has
|
|
2592
|
+
* ALREADY refreshed the clone's remote-tracking refs — which is the whole
|
|
2593
|
+
* clone in one round trip, against one per request here. A CR list is
|
|
2594
|
+
* exactly that caller: at five open requests the per-request fetch was
|
|
2595
|
+
* measured at ~0.55s each, and the list is re-read on every proposal and
|
|
2596
|
+
* every 60s poll. Only pass it when a fetch of the clone really has just
|
|
2597
|
+
* happened; the refs are otherwise as old as the last one, and the diff
|
|
2598
|
+
* would describe a stale head.
|
|
2599
|
+
*
|
|
2600
|
+
* It is a skip, not a promise never to fetch: a branch this clone has
|
|
2601
|
+
* never heard of is fetched anyway. That case is not a stale diff, it is
|
|
2602
|
+
* NO diff — an empty touched-path set, which is a change request missing
|
|
2603
|
+
* from its own author's tree. Two local `rev-parse`s are worth not being
|
|
2604
|
+
* that.
|
|
2156
2605
|
*/
|
|
2157
2606
|
async changedPathsForPr(
|
|
2158
2607
|
workspaceId: string,
|
|
2159
2608
|
baseBranch: string,
|
|
2160
2609
|
headBranch: string,
|
|
2610
|
+
opts: {
|
|
2611
|
+
fetch?: boolean;
|
|
2612
|
+
/**
|
|
2613
|
+
* Every path the change WRITES, for authorizing it rather than
|
|
2614
|
+
* describing it: `roles.yaml` kept (a merge, unlike a change request's,
|
|
2615
|
+
* does not strip it) and BOTH sides of every rename. A rename writes the
|
|
2616
|
+
* old path too — it deletes it — so an access check that saw only the
|
|
2617
|
+
* new name would clear a caller who cannot touch what the merge removes.
|
|
2618
|
+
*/
|
|
2619
|
+
forAccessCheck?: boolean;
|
|
2620
|
+
} = {},
|
|
2161
2621
|
): Promise<string[]> {
|
|
2162
2622
|
assertValidBranchName(baseBranch);
|
|
2163
2623
|
assertValidBranchName(headBranch);
|
|
2164
2624
|
// Fetch outside the mutex (network round-trip) so origin latency can't hold
|
|
2165
2625
|
// the workspace lock; the lock guards only the local diff below.
|
|
2166
2626
|
const cwd = await this.repoDir(workspaceId);
|
|
2167
|
-
|
|
2627
|
+
// The skip decision and the diff are ONE reading of the refs, not two.
|
|
2628
|
+
// Both are resolved here, to COMMITS, and the commits are what the diff
|
|
2629
|
+
// below runs on — the same pinning `changedFilesForPr`'s `at` does. A
|
|
2630
|
+
// concurrent `fetch --prune origin` (`ensureRemotesFetched` and
|
|
2631
|
+
// `fetchPrRefs` both run outside this mutex, deliberately) can drop a
|
|
2632
|
+
// remote-tracking ref between the two, and re-resolving inside the mutex
|
|
2633
|
+
// would then throw `WorkflowValidationError` — which `touchedPathsFor`
|
|
2634
|
+
// swallows into an empty touched-path set, i.e. exactly the change
|
|
2635
|
+
// request missing from its own author's tree that the skip is guarded
|
|
2636
|
+
// against. The commits stay readable whatever happens to the ref names.
|
|
2637
|
+
const pinned =
|
|
2638
|
+
opts.fetch === false ? await this.publishedPrCommits(cwd, baseBranch, headBranch) : null;
|
|
2639
|
+
if (!pinned) {
|
|
2640
|
+
await this.fetchPrRefs(cwd, baseBranch, headBranch);
|
|
2641
|
+
}
|
|
2168
2642
|
return this.mutex.run(workspaceId, async () => {
|
|
2169
|
-
const baseRef = await this.
|
|
2170
|
-
const headRef = await this.
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2643
|
+
const baseRef = pinned ? pinned.base : await this.resolvePublishedBranchRef(cwd, baseBranch);
|
|
2644
|
+
const headRef = pinned ? pinned.head : await this.resolvePublishedBranchRef(cwd, headBranch);
|
|
2645
|
+
return this.prChangedPathsAt(cwd, baseRef, headRef, opts);
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
/**
|
|
2650
|
+
* The shaping behind `changedPathsForPr`, without its workspace
|
|
2651
|
+
* reservation: the caller must already hold one. `mergeChangeRequest` reads
|
|
2652
|
+
* the same set inside its OWN reservation, where re-entering this one would
|
|
2653
|
+
* deadlock — and where the refs are the ones the merge is actually built on.
|
|
2654
|
+
*/
|
|
2655
|
+
private async prChangedPathsAt(
|
|
2656
|
+
cwd: string,
|
|
2657
|
+
baseRef: string,
|
|
2658
|
+
headRef: string,
|
|
2659
|
+
opts: { forAccessCheck?: boolean } = {},
|
|
2660
|
+
): Promise<string[]> {
|
|
2661
|
+
const { stdout } = await this.git(cwd, [
|
|
2662
|
+
'diff', '-M', '-z', '--name-status', `${baseRef}...${headRef}`,
|
|
2663
|
+
]);
|
|
2664
|
+
// Deduped: both sides of a rename can collide with another entry's path.
|
|
2665
|
+
return [
|
|
2666
|
+
...new Set(
|
|
2667
|
+
parseNameStatusZ(stdout)
|
|
2668
|
+
// A rename onto or off the placeholder is a real file removed or
|
|
2669
|
+
// added (see `withoutPlaceholderRename`): both of its paths are
|
|
2670
|
+
// touched, or filtering the placeholder would lose the file.
|
|
2671
|
+
//
|
|
2672
|
+
// Authorizing the change needs both sides of EVERY rename, not just
|
|
2673
|
+
// the placeholder's: git reports a rename under its new name alone,
|
|
2674
|
+
// and the old name is a path the change deletes.
|
|
2675
|
+
.flatMap((s) =>
|
|
2676
|
+
s.previousPath &&
|
|
2677
|
+
(opts.forAccessCheck || isFolderPlaceholder(s.path) || isFolderPlaceholder(s.previousPath))
|
|
2678
|
+
? [s.previousPath, s.path]
|
|
2679
|
+
: [s.path],
|
|
2680
|
+
)
|
|
2179
2681
|
// Same rule as `changedFilesForPr`: a roles.yaml change never
|
|
2180
2682
|
// survives a merge, so it is not a touched path for routing or
|
|
2181
2683
|
// summaries either.
|
|
2182
|
-
.filter((p) => p !== 'roles.yaml')
|
|
2183
|
-
)
|
|
2184
|
-
|
|
2684
|
+
.filter((p) => opts.forAccessCheck || p !== 'roles.yaml'),
|
|
2685
|
+
),
|
|
2686
|
+
];
|
|
2185
2687
|
}
|
|
2186
2688
|
|
|
2187
2689
|
/**
|
|
@@ -2200,8 +2702,8 @@ export class GitService implements IGitService {
|
|
|
2200
2702
|
const cwd = await this.repoDir(workspaceId);
|
|
2201
2703
|
await this.fetchPrRefs(cwd, baseBranch, headBranch);
|
|
2202
2704
|
return this.mutex.run(workspaceId, async () => {
|
|
2203
|
-
const baseRef = await this.
|
|
2204
|
-
const headRef = await this.
|
|
2705
|
+
const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
|
|
2706
|
+
const headRef = await this.resolvePublishedBranchRef(cwd, headBranch);
|
|
2205
2707
|
try {
|
|
2206
2708
|
const { stdout } = await this.git(cwd, ['merge-base', baseRef, headRef]);
|
|
2207
2709
|
return stdout.trim() || null;
|
|
@@ -2214,6 +2716,143 @@ export class GitService implements IGitService {
|
|
|
2214
2716
|
});
|
|
2215
2717
|
}
|
|
2216
2718
|
|
|
2719
|
+
/**
|
|
2720
|
+
* Where a change request forked from its target, and whether the target has
|
|
2721
|
+
* moved on since — for the two commits a detail read has ALREADY resolved
|
|
2722
|
+
* (no fetch, no ref resolution, same contract as `changedFilesForPr`'s `at`).
|
|
2723
|
+
*
|
|
2724
|
+
* `mergeBaseSha` is the "before" every file in the request is read against,
|
|
2725
|
+
* so an edit made on the target after the proposal never shows as something
|
|
2726
|
+
* the proposal deletes. `behind` is true when the target holds commits the
|
|
2727
|
+
* proposal does not (`head..base` is non-empty) — the proposal needs
|
|
2728
|
+
* updating before its diff and its eventual merge describe the same text.
|
|
2729
|
+
* No shared history: `mergeBaseSha: null`, and `behind` stays honest (the
|
|
2730
|
+
* target certainly has commits the proposal lacks).
|
|
2731
|
+
*/
|
|
2732
|
+
async forkPointForPr(
|
|
2733
|
+
workspaceId: string,
|
|
2734
|
+
at: { baseSha: string; headSha: string },
|
|
2735
|
+
): Promise<{ mergeBaseSha: string | null; behind: boolean }> {
|
|
2736
|
+
for (const sha of [at.baseSha, at.headSha]) {
|
|
2737
|
+
if (!/^[0-9a-f]{40,64}$/.test(sha)) {
|
|
2738
|
+
throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2742
|
+
return this.mutex.run(workspaceId, async () => {
|
|
2743
|
+
let mergeBaseSha: string | null = null;
|
|
2744
|
+
try {
|
|
2745
|
+
const { stdout } = await this.git(cwd, ['merge-base', at.baseSha, at.headSha]);
|
|
2746
|
+
mergeBaseSha = stdout.trim() || null;
|
|
2747
|
+
} catch (err) {
|
|
2748
|
+
// Exit 1 is git's "no common ancestor"; anything else is infra.
|
|
2749
|
+
if ((err as { exitCode?: number }).exitCode !== 1) throw err;
|
|
2750
|
+
}
|
|
2751
|
+
const { stdout: count } = await this.git(cwd, [
|
|
2752
|
+
'rev-list', '--count', `${at.headSha}..${at.baseSha}`,
|
|
2753
|
+
]);
|
|
2754
|
+
return { mergeBaseSha, behind: Number.parseInt(count.trim(), 10) > 0 };
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
/**
|
|
2759
|
+
* Every repo-relative path whose content differs between two commits on a
|
|
2760
|
+
* change request's own branch — what an update from the target actually
|
|
2761
|
+
* CHANGED about the proposal.
|
|
2762
|
+
*
|
|
2763
|
+
* Two-dot, because that is the question: not "what does this request
|
|
2764
|
+
* propose" (three-dot, against the target) but "what did the merge move
|
|
2765
|
+
* under the reviewers who already approved". `--no-renames` is what makes
|
|
2766
|
+
* the answer the conservative one: git detects renames BY DEFAULT
|
|
2767
|
+
* (`diff.renames` has been on since 2.9) and then reports only the new
|
|
2768
|
+
* path, which would leave an approval on the path a merge renamed AWAY
|
|
2769
|
+
* reading as untouched. Switched off, a rename reports as the old path
|
|
2770
|
+
* removed and the new one added, so an approval on either side is correctly
|
|
2771
|
+
* read as touched — a path that appears here loses its approval, and only a
|
|
2772
|
+
* path that does not appear keeps it.
|
|
2773
|
+
*/
|
|
2774
|
+
async pathsChangedBetween(
|
|
2775
|
+
workspaceId: string,
|
|
2776
|
+
fromSha: string,
|
|
2777
|
+
toSha: string,
|
|
2778
|
+
): Promise<string[]> {
|
|
2779
|
+
for (const sha of [fromSha, toSha]) {
|
|
2780
|
+
if (!/^[0-9a-f]{40,64}$/.test(sha)) {
|
|
2781
|
+
throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2785
|
+
return this.mutex.run(workspaceId, async () => {
|
|
2786
|
+
const { stdout } = await this.git(cwd, [
|
|
2787
|
+
'diff', '--no-renames', '-z', '--name-only', fromSha, toSha,
|
|
2788
|
+
]);
|
|
2789
|
+
// `-z` terminates each name with NUL, so the trailing field is empty —
|
|
2790
|
+
// and a tracked name may legally carry leading or trailing spaces, so
|
|
2791
|
+
// nothing is trimmed here.
|
|
2792
|
+
return [...new Set(stdout.split('\0').filter((name) => name !== ''))];
|
|
2793
|
+
});
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
/**
|
|
2797
|
+
* One file as it stood at a change request's fork point — the "before" side
|
|
2798
|
+
* of the request dialog's diff. `sha` must be a commit on the target
|
|
2799
|
+
* branch's published history (`origin/<baseBranch>`); anything else is
|
|
2800
|
+
* refused, so this can never become a read of an arbitrary commit. `null`
|
|
2801
|
+
* means the path did not exist at the fork point (the request adds it).
|
|
2802
|
+
*/
|
|
2803
|
+
async readFileAtForkPoint(
|
|
2804
|
+
workspaceId: string,
|
|
2805
|
+
baseBranch: string,
|
|
2806
|
+
sha: string,
|
|
2807
|
+
relativePath: string,
|
|
2808
|
+
): Promise<string | null> {
|
|
2809
|
+
assertValidBranchName(baseBranch);
|
|
2810
|
+
assertValidRelativePath(relativePath);
|
|
2811
|
+
if (!/^[0-9a-f]{40,64}$/.test(sha)) {
|
|
2812
|
+
throw new WorkflowValidationError(`invalid commit sha: ${sha}`);
|
|
2813
|
+
}
|
|
2814
|
+
const repoRelativePath = this.stripRepoPrefix(relativePath);
|
|
2815
|
+
return this.mutex.run(workspaceId, async () => {
|
|
2816
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2817
|
+
const baseRef = await this.resolvePublishedBranchRef(cwd, baseBranch);
|
|
2818
|
+
try {
|
|
2819
|
+
await this.git(cwd, ['merge-base', '--is-ancestor', sha, baseRef]);
|
|
2820
|
+
} catch (err) {
|
|
2821
|
+
if ((err as { exitCode?: number }).exitCode === 1) {
|
|
2822
|
+
throw new WorkflowDomainError(
|
|
2823
|
+
`That commit is not part of "${baseBranch}" — it is not this request's fork point.`,
|
|
2824
|
+
404,
|
|
2825
|
+
);
|
|
2826
|
+
}
|
|
2827
|
+
throw err;
|
|
2828
|
+
}
|
|
2829
|
+
await this.assertNotTreeAtRef(cwd, sha, repoRelativePath, relativePath);
|
|
2830
|
+
return this.readFileAtRef(workspaceId, sha, repoRelativePath);
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
/**
|
|
2835
|
+
* Whether `repoRelativePath` exists (as a file or tree) at `ref`. ONLY
|
|
2836
|
+
* git's own "that path is not at this ref" answer is false; an unresolvable
|
|
2837
|
+
* ref, a timeout, a locked or corrupt repository PROPAGATE — a revert that
|
|
2838
|
+
* read one of those as "absent" would decide wrongly what to restore.
|
|
2839
|
+
*/
|
|
2840
|
+
async pathExistsAtRef(workspaceId: string, ref: string, repoRelativePath: string): Promise<boolean> {
|
|
2841
|
+
assertValidRelativePath(repoRelativePath);
|
|
2842
|
+
return this.mutex.run(workspaceId, async () => {
|
|
2843
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2844
|
+
try {
|
|
2845
|
+
await this.git(cwd, ['cat-file', '-e', `${ref}:${repoRelativePath}`]);
|
|
2846
|
+
return true;
|
|
2847
|
+
} catch (err) {
|
|
2848
|
+
const stderr =
|
|
2849
|
+
(err as { stderr?: string }).stderr ?? (err instanceof Error ? err.message : String(err));
|
|
2850
|
+
if (/does not exist in|exists on disk, but not in/.test(stderr)) return false;
|
|
2851
|
+
throw err;
|
|
2852
|
+
}
|
|
2853
|
+
});
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2217
2856
|
/**
|
|
2218
2857
|
* Restore one path in the working tree (and index) to its content at `ref`;
|
|
2219
2858
|
* a path ABSENT at `ref` is deleted — the revert of an added file is its
|
|
@@ -2227,8 +2866,14 @@ export class GitService implements IGitService {
|
|
|
2227
2866
|
repoRelativePath: string,
|
|
2228
2867
|
): Promise<void> {
|
|
2229
2868
|
assertValidRelativePath(repoRelativePath);
|
|
2230
|
-
return this.mutex.run(workspaceId, async () =>
|
|
2231
|
-
|
|
2869
|
+
return this.mutex.run(workspaceId, async () =>
|
|
2870
|
+
this.restorePathUnlocked(await this.repoDir(workspaceId), ref, repoRelativePath),
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
/** `restorePathFromRef`'s body, for a caller that already holds the workspace's reservation. */
|
|
2875
|
+
private async restorePathUnlocked(cwd: string, ref: string, repoRelativePath: string): Promise<void> {
|
|
2876
|
+
{
|
|
2232
2877
|
let existsAtRef = true;
|
|
2233
2878
|
try {
|
|
2234
2879
|
await this.git(cwd, ['cat-file', '-e', `${ref}:${repoRelativePath}`]);
|
|
@@ -2250,7 +2895,7 @@ export class GitService implements IGitService {
|
|
|
2250
2895
|
// missing from disk is exactly what `git add` records as deleted.
|
|
2251
2896
|
await fs.rm(path.join(cwd, repoRelativePath), { force: true });
|
|
2252
2897
|
}
|
|
2253
|
-
}
|
|
2898
|
+
}
|
|
2254
2899
|
}
|
|
2255
2900
|
|
|
2256
2901
|
/** Fetch the two branches a CR spans so origin refs reflect the latest push. */
|
|
@@ -2321,6 +2966,69 @@ export class GitService implements IGitService {
|
|
|
2321
2966
|
: relativePath;
|
|
2322
2967
|
}
|
|
2323
2968
|
|
|
2969
|
+
/**
|
|
2970
|
+
* The COMMITS both of a change request's branches point at in this clone
|
|
2971
|
+
* already, or null when either is unknown to it — the question behind
|
|
2972
|
+
* `changedPathsForPr`'s `fetch: false`, answered with the resolution itself
|
|
2973
|
+
* so the caller diffs precisely what it decided on. Only git's own "no such
|
|
2974
|
+
* ref" answers null; every other failure is the caller's to see, and
|
|
2975
|
+
* treating it as "not here" would spend a network fetch on a clone that is
|
|
2976
|
+
* broken for some other reason.
|
|
2977
|
+
*/
|
|
2978
|
+
private async publishedPrCommits(
|
|
2979
|
+
cwd: string,
|
|
2980
|
+
baseBranch: string,
|
|
2981
|
+
headBranch: string,
|
|
2982
|
+
): Promise<{ base: string; head: string } | null> {
|
|
2983
|
+
const shas: string[] = [];
|
|
2984
|
+
for (const branch of [baseBranch, headBranch]) {
|
|
2985
|
+
try {
|
|
2986
|
+
shas.push((await this.resolvePublishedBranch(cwd, branch)).sha);
|
|
2987
|
+
} catch (err) {
|
|
2988
|
+
if (err instanceof WorkflowValidationError) return null;
|
|
2989
|
+
throw err;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
return { base: shas[0], head: shas[1] };
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
/**
|
|
2996
|
+
* `resolveBranchRef` with the preference reversed: the PUBLISHED ref
|
|
2997
|
+
* (`origin/<branch>`, just fetched by `fetchPrRefs`) before the local head.
|
|
2998
|
+
* A change request is a pair of published branches, and a clone's local
|
|
2999
|
+
* copy of the OTHER branch is whatever it was when the clone last touched
|
|
3000
|
+
* it — in a proposal's own workspace the local target is typically the
|
|
3001
|
+
* target as of the fork. Reading that made every request look up to date
|
|
3002
|
+
* and diffed it against a stale target. A local-only branch still resolves.
|
|
3003
|
+
*/
|
|
3004
|
+
private async resolvePublishedBranchRef(cwd: string, branch: string): Promise<string> {
|
|
3005
|
+
return (await this.resolvePublishedBranch(cwd, branch)).ref;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
/**
|
|
3009
|
+
* {@link resolvePublishedBranchRef} with the commit it resolved to — the
|
|
3010
|
+
* `rev-parse` that verifies the ref already prints it, so pinning a diff to
|
|
3011
|
+
* the commit costs nothing over naming the ref.
|
|
3012
|
+
*/
|
|
3013
|
+
private async resolvePublishedBranch(
|
|
3014
|
+
cwd: string,
|
|
3015
|
+
branch: string,
|
|
3016
|
+
): Promise<{ ref: string; sha: string }> {
|
|
3017
|
+
for (const ref of [`refs/remotes/origin/${branch}`, `refs/heads/${branch}`]) {
|
|
3018
|
+
try {
|
|
3019
|
+
const { stdout } = await this.git(cwd, ['rev-parse', '--verify', '--quiet', ref]);
|
|
3020
|
+
return { ref, sha: stdout.trim() };
|
|
3021
|
+
} catch (err) {
|
|
3022
|
+
// Only git's own "no such ref" (exit 1 under --quiet) moves on to the
|
|
3023
|
+
// next candidate. A deadline or any other failure is not that answer:
|
|
3024
|
+
// skipping past it would pick the stale local copy, or call a real
|
|
3025
|
+
// branch unknown.
|
|
3026
|
+
if (!(err instanceof GitRunError) || err.timedOut || err.exitCode !== 1) throw err;
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
throw new WorkflowValidationError(`unknown branch: ${branch}`);
|
|
3030
|
+
}
|
|
3031
|
+
|
|
2324
3032
|
/**
|
|
2325
3033
|
* Resolve a branch name to a concrete ref the local clone knows about.
|
|
2326
3034
|
* Tries the local head first, falls back to the matching remote-tracking
|
|
@@ -2370,14 +3078,14 @@ export class GitService implements IGitService {
|
|
|
2370
3078
|
}
|
|
2371
3079
|
const dirtyList = porcelain.stdout.trim().replace(/\s+/g, ' ');
|
|
2372
3080
|
if (queueExplainsIt) {
|
|
2373
|
-
|
|
2374
|
-
`
|
|
3081
|
+
log.info(
|
|
3082
|
+
`workspace=${workspaceId} branch=${branch} working tree is dirty while ` +
|
|
2375
3083
|
`pending commits drain (expected — the background worker is catching up). ` +
|
|
2376
3084
|
`Files: ${dirtyList}`,
|
|
2377
3085
|
);
|
|
2378
3086
|
} else {
|
|
2379
|
-
|
|
2380
|
-
`
|
|
3087
|
+
log.warn(
|
|
3088
|
+
`workspace=${workspaceId} branch=${branch} has a non-clean working ` +
|
|
2381
3089
|
`tree under save=share with NO queued pending commits — this should never ` +
|
|
2382
3090
|
`happen and likely indicates a missed lock-release commit. Files: ${dirtyList}`,
|
|
2383
3091
|
);
|
|
@@ -2527,6 +3235,41 @@ export class GitService implements IGitService {
|
|
|
2527
3235
|
}
|
|
2528
3236
|
}
|
|
2529
3237
|
|
|
3238
|
+
/**
|
|
3239
|
+
* The commits on `ref` that touched `repoRelativePath`, newest first, at
|
|
3240
|
+
* most `limit` of them. What a skill's `version` is looked up through: each
|
|
3241
|
+
* commit is a copy of the file that may have declared a version. Renames are
|
|
3242
|
+
* not followed — a moved skill's earlier history is the old path's.
|
|
3243
|
+
*/
|
|
3244
|
+
async pathHistory(
|
|
3245
|
+
workspaceId: string,
|
|
3246
|
+
ref: string,
|
|
3247
|
+
repoRelativePath: string,
|
|
3248
|
+
limit: number,
|
|
3249
|
+
): Promise<string[]> {
|
|
3250
|
+
const cwd = await this.repoDir(workspaceId);
|
|
3251
|
+
const { stdout } = await this.git(cwd, [
|
|
3252
|
+
'rev-list',
|
|
3253
|
+
`--max-count=${Math.max(1, Math.trunc(limit))}`,
|
|
3254
|
+
ref,
|
|
3255
|
+
'--',
|
|
3256
|
+
repoRelativePath,
|
|
3257
|
+
]);
|
|
3258
|
+
return stdout.split('\n').map((line) => line.trim()).filter((line) => line.length > 0);
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
/**
|
|
3262
|
+
* Every file under `folder` at `ref`, repo-root-relative and in tree order.
|
|
3263
|
+
* A folder that does not exist at that ref lists nothing. NUL-delimited, so
|
|
3264
|
+
* a non-ASCII name comes back as itself rather than C-quoted, and one with
|
|
3265
|
+
* a leading or trailing space keeps it.
|
|
3266
|
+
*/
|
|
3267
|
+
async listFilesAtRef(workspaceId: string, ref: string, folder: string): Promise<string[]> {
|
|
3268
|
+
const cwd = await this.repoDir(workspaceId);
|
|
3269
|
+
const { stdout } = await this.git(cwd, ['ls-tree', '-r', '-z', '--name-only', ref, '--', folder]);
|
|
3270
|
+
return stdout.split('\0').filter((line) => line.length > 0);
|
|
3271
|
+
}
|
|
3272
|
+
|
|
2530
3273
|
/**
|
|
2531
3274
|
* A clean `git status` for a path has two honest readings (already
|
|
2532
3275
|
* committed; a queued re-apply of something that landed) and one dishonest
|
|
@@ -2573,69 +3316,32 @@ export class GitService implements IGitService {
|
|
|
2573
3316
|
return stdout.trim().length > 0;
|
|
2574
3317
|
}
|
|
2575
3318
|
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
// `LC_ALL=C` / `LANG=C` force git's human-readable output (including
|
|
2603
|
-
// stderr) to stable, English, locale-independent text. Callers that
|
|
2604
|
-
// classify errors by message — e.g. `readFileAtRef` distinguishing a
|
|
2605
|
-
// "path does not exist in <ref>" absence from a hard failure — would
|
|
2606
|
-
// otherwise misread a translated message on a non-English host and, for
|
|
2607
|
-
// the fail-closed roles.yaml preservation path, fail OPEN.
|
|
2608
|
-
env: { ...process.env, GIT_LITERAL_PATHSPECS: '1', LC_ALL: 'C', LANG: 'C' },
|
|
2609
|
-
maxBuffer: 32 * 1024 * 1024,
|
|
2610
|
-
});
|
|
2611
|
-
if (opts?.input !== undefined && pending.child.stdin) {
|
|
2612
|
-
// A dying git can close stdin mid-write; the promise below still
|
|
2613
|
-
// rejects with the exit code, which is the error we want to surface.
|
|
2614
|
-
pending.child.stdin.on('error', () => undefined);
|
|
2615
|
-
pending.child.stdin.write(opts.input);
|
|
2616
|
-
pending.child.stdin.end();
|
|
2617
|
-
}
|
|
2618
|
-
const { stdout, stderr } = await pending;
|
|
2619
|
-
return { stdout: stdout.toString(), stderr: stderr.toString() };
|
|
2620
|
-
} catch (err) {
|
|
2621
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2622
|
-
// Skip past any leading `-c key=val` pairs so the error names the actual
|
|
2623
|
-
// git subcommand that failed (e.g. "git fetch failed:" not "git -c failed:").
|
|
2624
|
-
let i = 0;
|
|
2625
|
-
while (i < args.length && args[i] === '-c') i += 2;
|
|
2626
|
-
const subcommand = args[i] ?? args[0];
|
|
2627
|
-
const wrapped = new Error(`git ${subcommand} failed: ${redact(msg)}`) as Error & {
|
|
2628
|
-
exitCode?: number;
|
|
2629
|
-
stderr?: string;
|
|
2630
|
-
};
|
|
2631
|
-
// Preserve the underlying exit code / stderr so callers can distinguish
|
|
2632
|
-
// expected non-zero exits (e.g. merge-base exit 1 = no common ancestor)
|
|
2633
|
-
// from infra failures (ENOENT, exit 128) without parsing message strings.
|
|
2634
|
-
const original = err as { code?: unknown; stderr?: unknown };
|
|
2635
|
-
if (typeof original.code === 'number') wrapped.exitCode = original.code;
|
|
2636
|
-
if (typeof original.stderr === 'string') wrapped.stderr = redact(original.stderr);
|
|
2637
|
-
throw wrapped;
|
|
2638
|
-
}
|
|
3319
|
+
/**
|
|
3320
|
+
* Every git invocation this service makes goes through here, and from here
|
|
3321
|
+
* through the injected runner — which is what puts a deadline on it. The
|
|
3322
|
+
* environment, the buffer ceiling, the stdin handling and the error shape all
|
|
3323
|
+
* live in `NodeGitRunner` now; see `shared/git.contract.ts` for why.
|
|
3324
|
+
*
|
|
3325
|
+
* Errors arrive as `GitRunError`, which still carries `exitCode` and
|
|
3326
|
+
* `stderr`, so the callers that read those to tell an expected non-zero exit
|
|
3327
|
+
* from a real failure are unaffected.
|
|
3328
|
+
*/
|
|
3329
|
+
private git(cwd: string, args: string[], opts?: Omit<GitRunOptions, 'encoding'>): Promise<GitRunResult> {
|
|
3330
|
+
return this.gitRunner.run(cwd, args, {
|
|
3331
|
+
...opts,
|
|
3332
|
+
// `GIT_LITERAL_PATHSPECS=1` makes git treat every pathspec literally
|
|
3333
|
+
// instead of interpreting `[`, `]`, `*`, `?`, `!`, or `:(magic)` as glob
|
|
3334
|
+
// / magic syntax. KB files routinely arrive with bracketed prefixes like
|
|
3335
|
+
// `[Approved] foo.docx` or `[Updated 2025] bar.md`; the upload pipeline
|
|
3336
|
+
// (`writeFileBinary` + `releaseLock` + `commitFile`) passes the relative
|
|
3337
|
+
// path straight through to `git add` / `git checkout -- <path>`, which
|
|
3338
|
+
// would otherwise glob and either match the wrong file or no file at all.
|
|
3339
|
+
//
|
|
3340
|
+
// THIS service's setting, not the runner's: the startup runner spells its
|
|
3341
|
+
// literal paths as `:(literal)<path>`, which this variable would turn into
|
|
3342
|
+
// a search for a file literally named that.
|
|
3343
|
+
env: { GIT_LITERAL_PATHSPECS: '1', ...opts?.env },
|
|
3344
|
+
});
|
|
2639
3345
|
}
|
|
2640
3346
|
}
|
|
2641
3347
|
|
|
@@ -2686,6 +3392,23 @@ export function parseNameStatusZ(out: string): NameStatusEntry[] {
|
|
|
2686
3392
|
return entries;
|
|
2687
3393
|
}
|
|
2688
3394
|
|
|
3395
|
+
/**
|
|
3396
|
+
* Undo a rename git detected between a real file and the empty-folder
|
|
3397
|
+
* placeholder. Deleting a folder's last file writes the placeholder, and
|
|
3398
|
+
* when that file was empty too, `-M` pairs the two as a 100% rename. The
|
|
3399
|
+
* pair is really a removal (or, the other way round, an addition) plus the
|
|
3400
|
+
* placeholder, which is never reviewed — so the entry becomes the real side
|
|
3401
|
+
* alone, and the caller's placeholder filter has nothing of it to drop.
|
|
3402
|
+
*/
|
|
3403
|
+
export function withoutPlaceholderRename(entry: NameStatusEntry): NameStatusEntry {
|
|
3404
|
+
const { previousPath } = entry;
|
|
3405
|
+
if (!previousPath || entry.status !== 'renamed') return entry;
|
|
3406
|
+
const toPlaceholder = isFolderPlaceholder(entry.path);
|
|
3407
|
+
const fromPlaceholder = isFolderPlaceholder(previousPath);
|
|
3408
|
+
if (toPlaceholder === fromPlaceholder) return entry;
|
|
3409
|
+
return toPlaceholder ? { status: 'removed', path: previousPath } : { status: 'added', path: entry.path };
|
|
3410
|
+
}
|
|
3411
|
+
|
|
2689
3412
|
interface NumstatEntry {
|
|
2690
3413
|
additions: number;
|
|
2691
3414
|
deletions: number;
|