@gitkraken/core-gitlens 0.24.0 → 0.25.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/CHANGELOG.md +27 -1
- package/README.md +23 -15
- package/dist/git/errors.d.ts +6 -5
- package/dist/git/errors.js +7 -6
- package/dist/git/errors.js.map +1 -1
- package/dist/git/features.d.ts +1 -1
- package/dist/git/features.js +1 -1
- package/dist/git/features.js.map +1 -1
- package/dist/git/models/remoteProvider.d.ts +4 -1
- package/dist/git/models/remoteProvider.js.map +1 -1
- package/dist/git/providers/provider.d.ts +4 -2
- package/dist/git/utils/branch.utils.d.ts +4 -1
- package/dist/git/utils/branch.utils.js +5 -2
- package/dist/git/utils/branch.utils.js.map +1 -1
- package/dist/git/utils/mergeConflicts.utils.js +2 -2
- package/dist/git/utils/mergeConflicts.utils.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +14 -3
- package/dist/git-cli/cliGitProvider.js +71 -34
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +7 -1
- package/dist/git-cli/exec/git.js +19 -4
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/providers/branchRemote.js +2 -5
- package/dist/git-cli/providers/branchRemote.js.map +1 -1
- package/dist/git-cli/providers/branches.js +4 -4
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/config.js +4 -1
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/plus/ai/errors.d.ts +4 -1
- package/dist/plus/ai/errors.js +4 -0
- package/dist/plus/ai/errors.js.map +1 -1
- package/dist/plus/ai/providers/mistralProvider.js +1 -1
- package/dist/plus/ai/providers/mistralProvider.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -0
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
- package/dist/plus/git-github/api/github.js +12 -13
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/token.d.ts +2 -0
- package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts +1 -1
- package/dist/plus/integrations/authentication/cloudIntegrationService.js +6 -1
- package/dist/plus/integrations/authentication/cloudIntegrationService.js.map +1 -1
- package/dist/plus/integrations/authentication/configuredIntegrationService.js +2 -2
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +18 -5
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts +3 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationService.js +4 -0
- package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -1
- package/dist/plus/integrations/authentication/models.d.ts +2 -0
- package/dist/plus/integrations/authentication/models.js +2 -1
- package/dist/plus/integrations/authentication/models.js.map +1 -1
- package/dist/plus/integrations/authentication/observedAuthenticationProvider.js +1 -1
- package/dist/plus/integrations/authentication/rejectedTokenTracker.d.ts +5 -4
- package/dist/plus/integrations/authentication/rejectedTokenTracker.js +5 -4
- package/dist/plus/integrations/authentication/rejectedTokenTracker.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -2
- package/dist/plus/integrations/index.js +1 -1
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +6 -0
- package/dist/plus/integrations/integrationService.js +3 -3
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/lite.js +1 -1
- package/dist/plus/integrations/manager.d.ts +51 -3
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +14 -0
- package/dist/plus/integrations/models/gitHostIntegration.js +50 -41
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +27 -5
- package/dist/plus/integrations/models/integration.js +57 -15
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +6 -0
- package/dist/plus/integrations/models/issuesIntegration.js +11 -11
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.js +1 -1
- package/dist/plus/integrations/providers/azureDevOps.js +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.js +1 -1
- package/dist/plus/integrations/providers/bitbucket/bitbucket.js +1 -1
- package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.js +1 -1
- package/dist/plus/integrations/providers/bitbucket.js +1 -1
- package/dist/plus/integrations/providers/github.js +1 -1
- package/dist/plus/integrations/providers/gitlab/gitlab.js +2 -2
- package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.js +1 -1
- package/dist/plus/integrations/providers/jira-server.js +2 -2
- package/dist/plus/integrations/providers/jira.js +1 -1
- package/dist/plus/integrations/providers/linear.js +2 -2
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/models.js +2 -1
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providerErrors.js +9 -0
- package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.js +1 -1
- package/dist/plus/integrations/providers/trello.js +1 -1
- package/dist/plus/integrations/providers/utils.js +1 -1
- package/dist/plus/integrations/reads/counts.js +1 -1
- package/dist/plus/integrations/reads/cursors.js +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +5 -4
- package/dist/plus/integrations/reads/drains.js +28 -8
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.d.ts +1 -1
- package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +1 -1
- package/dist/plus/integrations/reads/issues.d.ts +2 -0
- package/dist/plus/integrations/reads/issues.js +5 -3
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/paging.d.ts +5 -6
- package/dist/plus/integrations/reads/paging.js +5 -6
- package/dist/plus/integrations/reads/paging.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequests.d.ts +4 -0
- package/dist/plus/integrations/reads/pullRequests.js +4 -2
- package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
- package/dist/plus/integrations/reads/resolveRepository.js +1 -1
- package/dist/plus/integrations/reads/sweeps.js +42 -10
- package/dist/plus/integrations/reads/sweeps.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +23 -3
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/notifications/api.d.ts +20 -0
- package/dist/plus/notifications/api.js +89 -0
- package/dist/plus/notifications/api.js.map +1 -0
- package/dist/plus/notifications/context.d.ts +28 -0
- package/dist/plus/notifications/context.js +0 -0
- package/dist/plus/notifications/errors.d.ts +13 -0
- package/dist/plus/notifications/errors.js +22 -0
- package/dist/plus/notifications/errors.js.map +1 -0
- package/dist/plus/notifications/models.d.ts +94 -0
- package/dist/plus/notifications/models.js +0 -0
- package/dist/plus/notifications/notificationsService.d.ts +115 -0
- package/dist/plus/notifications/notificationsService.js +381 -0
- package/dist/plus/notifications/notificationsService.js.map +1 -0
- package/dist/plus/notifications/parse.d.ts +12 -0
- package/dist/plus/notifications/parse.js +152 -0
- package/dist/plus/notifications/parse.js.map +1 -0
- package/dist/plus/notifications/presentation.d.ts +28 -0
- package/dist/plus/notifications/presentation.js +55 -0
- package/dist/plus/notifications/presentation.js.map +1 -0
- package/dist/utils/env/node/exec.d.ts +10 -1
- package/dist/utils/env/node/exec.js +39 -2
- package/dist/utils/env/node/exec.js.map +1 -1
- package/docs/integrations.md +21 -2
- package/docs/notifications.md +239 -0
- package/package.json +21 -1
|
@@ -761,7 +761,10 @@ var ConfigGitSubProvider = class {
|
|
|
761
761
|
if (result.stdout.trim() === "true") {
|
|
762
762
|
const result = await this.revParseGitDir(cwd);
|
|
763
763
|
const repoPath = result?.commonPath ?? result?.path;
|
|
764
|
-
if (repoPath?.length)
|
|
764
|
+
if (repoPath?.length) {
|
|
765
|
+
const workTree = await this.git.rev_parse__work_tree_of_git_dir(repoPath);
|
|
766
|
+
return workTree ? this.getRepositoryInfo(workTree) : [true, repoPath];
|
|
767
|
+
}
|
|
765
768
|
}
|
|
766
769
|
}
|
|
767
770
|
if (inDotGit || ex.code === "ENOENT") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":["process_env","fs"],"sources":["../../../../git-cli/src/providers/config.ts"],"sourcesContent":["import { promises as fs, statSync } from 'fs';\nimport { hostname, userInfo } from 'os';\nimport { env as process_env } from 'process';\nimport type { Cache, GkConfigInvalidationTarget, GkReconcileOutcome } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { WorkspaceUntrustedError } from '@gitlens/git/errors.js';\nimport type { GitDir } from '@gitlens/git/models/repository.js';\nimport type { SigningConfig, SigningFormat, ValidationResult } from '@gitlens/git/models/signature.js';\nimport type { GitUser } from '@gitlens/git/models/user.js';\nimport type {\n\tDeprecatedGkConfigKeys,\n\tGitConfigKeys,\n\tGitConfigSubProvider,\n\tGitConfigType,\n\tGkConfigKeys,\n} from '@gitlens/git/providers/config.js';\nimport { parseBranchMetadataGkConfigKey } from '@gitlens/git/utils/branch.utils.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport { first, some } from '@gitlens/utils/iterable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { dirname, getBestPath, isAbsolute, joinPaths, normalizePath } from '@gitlens/utils/path.js';\nimport { getSettledValue } from '@gitlens/utils/promise.js';\nimport type { CacheController } from '@gitlens/utils/promiseCache.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport { fsExists } from '../exec/exec.js';\nimport type { Git } from '../exec/git.js';\nimport { KeyedSequencer } from '../exec/indexLock.js';\n\nconst mappedAuthorRegex = /(.+)\\s<(.+)>/;\nconst emptyArray: readonly never[] = Object.freeze([]);\n\n/** Catch-all pattern matching every key in `.git/gk/config` for the one-shot bulk read. */\nconst gkConfigAllPattern = '.';\n\n/**\n * Maps from failed bulk reads, which a reconcile must not diff as the file's content, each to the read that produced\n * it. Module-wide, like {@link gkConfigMapReads}: a reconcile can join a read another provider sharing its `Cache`\n * started.\n */\nconst failedGkConfigMaps = new WeakMap<Map<string, string>, GkConfigMapRead>();\n\n/** What the bulk read behind a cached gk config map saw of `.git/gk/config` before it spawned */\ninterface GkConfigMapRead {\n\t/** Set as the read spawns; until then it can only see the file as it is after every change reported so far */\n\tspawned: boolean;\n\tpath?: string;\n\t/** The file's {@link getGkConfigFingerprint} taken right before the read spawned; `undefined` when untrusted */\n\tfingerprint?: string;\n}\n\n/**\n * The read behind each commonPath's cached gk config map, per `Cache`. Recorded as the read starts, which is when\n * the cache creates the entry for it, so it always describes the entry cached now. Kept per `Cache` rather than per\n * provider, since providers sharing a `Cache` serve each other's reads.\n */\nconst gkConfigMapReads = new WeakMap<Cache, Map<string, GkConfigMapRead>>();\n\n/** The wall clock a read's fingerprint is checked against (see {@link isGkConfigStatRacy}), exported for testing */\nexport const gkConfigFingerprintClock = { now: (): number => Date.now() };\n\nconst nsPerMs = 1_000_000n;\nconst nsPerSecond = 1_000_000_000n;\n\n/**\n * Whether a stat of `.git/gk/config` taken at `statTimeMs` came too soon after the file's last change to vouch for\n * the file staying unchanged while it matches — git's \"racily clean\" index entries. A later write in the same tick of\n * the filesystem's clock as that change, of the same size and given the inode the stat saw (freed by the write in\n * between and reused), gets the same fingerprint; once the stat is a tick past the change, any later write carries a\n * later time. Times that are whole seconds come from a filesystem that keeps no finer (ext4 with 128-byte inodes,\n * HFS+, FAT's 2-second mtime, some network mounts), so the stat must be 2s past; otherwise 50ms covers the coarsest\n * tick a finer clock still has (a 10ms jiffy, Windows' 15.6ms timer) and the wall clock lagging it by one. A file\n * changed after `statTimeMs` (a clock ahead of ours) is racy too. Exported for testing.\n */\nexport function isGkConfigStatRacy(mtimeNs: bigint, ctimeNs: bigint, statTimeMs: number): boolean {\n\tconst changedNs = mtimeNs > ctimeNs ? mtimeNs : ctimeNs;\n\tconst marginNs = mtimeNs % nsPerSecond === 0n && ctimeNs % nsPerSecond === 0n ? 2n * nsPerSecond : 50n * nsPerMs;\n\treturn BigInt(Math.floor(statTimeMs)) * nsPerMs - changedNs < marginNs;\n}\n\n/**\n * A stat of `.git/gk/config` that two stats share only if no write landed between them: `git config` writes a lock\n * file and renames it over the original, so every write gives the file a new inode, and its size and times tell\n * apart a later write that lands on a freed inode — unless that write fell in the same clock tick as the change the\n * first stat saw, which `statTimeMs` rules out (see {@link isGkConfigStatRacy}): given the time the stat is taken,\n * a stat that racy is `undefined`, never matching. `'absent'` when there is no file, `undefined` when it can't be\n * stat'd.\n */\nfunction getGkConfigFingerprint(path: string, statTimeMs?: number): string | undefined {\n\ttry {\n\t\tconst stat = statSync(path, { bigint: true, throwIfNoEntry: false });\n\t\tif (stat == null) return 'absent';\n\t\tif (statTimeMs != null && isGkConfigStatRacy(stat.mtimeNs, stat.ctimeNs, statTimeMs)) return undefined;\n\n\t\treturn `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Canonicalizes a git config key for lookup against `--get-regexp` output, which git emits with the\n * section and variable-name lowercased and only the subsection case-preserved. Without this a\n * subsection-less key like `gk.defaultRemote` would miss the lowercased `gk.defaultremote` in the map.\n */\nexport function canonicalizeGitConfigKey(key: string): string {\n\tconst first = key.indexOf('.');\n\tif (first === -1) return key.toLowerCase();\n\n\tconst last = key.lastIndexOf('.');\n\t// `section.name` (no subsection) → fully lowercased; `section.subsection.name` → lowercase\n\t// section + name, preserve the case-sensitive subsection in the middle.\n\tif (first === last) return key.toLowerCase();\n\treturn `${key.slice(0, first).toLowerCase()}${key.slice(first, last)}${key.slice(last).toLowerCase()}`;\n}\n\n/**\n * Parses git config --get-regex output into a Map.\n * The output format is \"key value\" per line, where key and value are space-separated.\n *\n * A line with NO space at all is git's rendering of a bareword/valueless entry (`key`, no `=`) — git's\n * OWN config parser reads that as boolean TRUE (git-config(1): \"a variable defined without `= <value>`\n * is taken as true\"), confirmed against git's `format_config` (builtin/config.c): the key-delimiter is\n * appended unconditionally, then backed out ONLY when the stored value is NULL (the bareword case) —\n * an explicit empty value (`key =`) is a real, non-NULL empty string, so its line KEEPS the trailing\n * space and is already handled by the branch below, byte-distinct from a bareword line.\n *\n * By default a bareword line is skipped (the historical behavior, which reads as \"unset\" — wrong, but\n * safe); pass `includeValueless` to map it to the literal string `'true'` instead, so `parseGitBoolean`\n * and callers checking `.has()` for \"configured\" both read it correctly.\n */\nexport function parseConfigRegexOutput(\n\tdata: string | undefined,\n\toptions?: { includeValueless?: boolean },\n): Map<string, string> {\n\tconst configMap = new Map<string, string>();\n\tif (!data) return configMap;\n\n\tfor (const line of data.split('\\n')) {\n\t\tif (!line) continue;\n\n\t\tconst spaceIndex = line.indexOf(' ');\n\t\tif (spaceIndex === -1) {\n\t\t\tif (options?.includeValueless) {\n\t\t\t\tconfigMap.set(line, 'true');\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconfigMap.set(line.substring(0, spaceIndex), line.substring(spaceIndex + 1));\n\t}\n\n\treturn configMap;\n}\n\n/**\n * Parses `git config --null --get-regexp` output into every value of each key, in the order git printed them. Each\n * entry is its key, a newline and its value, ended by a NUL, so a value may hold newlines or spaces unambiguously.\n * A valueless entry (`key`, no `=`), which git prints with no newline, is skipped, as {@link parseConfigRegexOutput}\n * skips it by default.\n */\nexport function parseConfigRegexNullOutput(data: string | undefined): Map<string, string[]> {\n\tconst configMap = new Map<string, string[]>();\n\tif (!data) return configMap;\n\n\tfor (const entry of data.split('\\0')) {\n\t\tconst newlineIndex = entry.indexOf('\\n');\n\t\tif (newlineIndex === -1) continue;\n\n\t\tconst key = entry.substring(0, newlineIndex);\n\t\tconst value = entry.substring(newlineIndex + 1);\n\t\tconst values = configMap.get(key);\n\t\tif (values != null) {\n\t\t\tvalues.push(value);\n\t\t} else {\n\t\t\tconfigMap.set(key, [value]);\n\t\t}\n\t}\n\n\treturn configMap;\n}\n\nexport function parseGitBoolean(value: string | undefined): boolean {\n\tif (value == null) return false;\n\n\tconst normalized = value.toLowerCase().trim();\n\treturn normalized === 'true' || normalized === 'yes' || normalized === 'on' || normalized === '1';\n}\n\n/** Whether a git config value spells boolean-false (`false`/`no`/`off`/`0`/empty), per git's maybe-bool parsing. */\nexport function isGitBooleanFalse(value: string): boolean {\n\tconst normalized = value.toLowerCase().trim();\n\treturn (\n\t\tnormalized === '' || normalized === 'false' || normalized === 'no' || normalized === 'off' || normalized === '0'\n\t);\n}\n\n/** Splits `git config --null --get-regexp` output into its entries, in order; a key set without a value has none */\nexport function parseConfigListing(listing: string): [key: string, value: string | undefined][] {\n\tconst entries: [key: string, value: string | undefined][] = [];\n\tfor (const entry of listing.split('\\0')) {\n\t\tif (!entry) continue;\n\n\t\tconst separator = entry.indexOf('\\n');\n\t\tentries.push(\n\t\t\tseparator < 0 ? [entry, undefined] : [entry.substring(0, separator), entry.substring(separator + 1)],\n\t\t);\n\t}\n\treturn entries;\n}\n\n/**\n * Whether `listing` shows config that can differ by checkout over time: `extensions.worktreeConfig` (each checkout's\n * `config.worktree`), or a conditional include that is neither `hasconfig:` nor `gitdir:`, since `onbranch:` follows\n * each checkout's HEAD and an unknown condition might follow anything. A `gitdir:` include matching only some\n * checkouts is accepted as shared: it is the common identity setup, and treating it as differing would end sharing for\n * nearly everyone. The cost is a known gap: an edit to a file only those checkouts include goes unseen there, since\n * nothing watches it, until a change the shared listing does see.\n */\nexport function mayConfigDifferByCheckout(listing: string): boolean {\n\tfor (const [key, value] of parseConfigListing(listing)) {\n\t\tif (key === 'extensions.worktreeconfig') {\n\t\t\t// A key set without a value is true\n\t\t\tif (value == null || !isGitBooleanFalse(value)) return true;\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (!key.startsWith('includeif.')) continue;\n\n\t\tconst condition = key.substring('includeif.'.length, key.lastIndexOf('.'));\n\t\tif (\n\t\t\tcondition.startsWith('hasconfig:') ||\n\t\t\tcondition.startsWith('gitdir:') ||\n\t\t\tcondition.startsWith('gitdir/i:')\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nexport class ConfigGitSubProvider implements GitConfigSubProvider {\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t) {\n\t\tthis.cache.setGkConfigReconciler((repoPath, priorSnapshot) => this.reconcileGkConfig(repoPath, priorSnapshot));\n\t}\n\n\t/**\n\t * In-flight gk-config reconciles keyed by commonPath. `pending` records that another `'gkConfig'`\n\t * event landed mid-pass, so one trailing pass runs after the current one settles. `paths` holds every\n\t * worktree path that reported the change, mapped to its close generation at that moment — the pass\n\t * reads through one that's still open and notifies all of them.\n\t */\n\tprivate readonly _gkConfigReconciles = new Map<string, { pending: boolean; paths: Map<string, number> }>();\n\n\t/**\n\t * The latest pending `setGkConfig` write of each `commonPath\\0key`. A call writing the same value with the same\n\t * invalidation and error options joins it; any other call chains after it, so one key's writes land in call order. Cleared on settle.\n\t */\n\tprivate readonly _pendingGkConfigWrites = new Map<\n\t\tstring,\n\t\t{ value: string | undefined; skipKey: string; throwOnError: boolean; promise: Promise<void> }\n\t>();\n\n\t/** Writes each `.git/gk/config` one at a time — concurrent `git config` writes fail on its lock */\n\tprivate readonly _gkConfigWrites = new KeyedSequencer();\n\n\t@trace({ quiet: true })\n\tgetConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfig(global ? undefined : repoPath, key, () =>\n\t\t\tthis.getConfigCore(repoPath, key, options),\n\t\t);\n\t}\n\n\tprivate async getConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst args = ['config', '--get'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\tif (options?.type) {\n\t\t\targs.push(`--type=${options.type}`);\n\t\t}\n\t\targs.push(key);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t/**\n\t * A checkout's raw `core.excludesFile` and `core.ignorecase`, the config its gitignore filter loads. Every checkout\n\t * of a repository shares one read in the main checkout, unless the listing shows config that can differ by checkout\n\t * (see `mayConfigDifferByCheckout`) or the read failed; then a linked checkout reads its own.\n\t */\n\t@trace({ quiet: true })\n\tasync getIgnoreConfig(\n\t\trepoPath: string,\n\t): Promise<{ excludesFile: string | undefined; ignoreCase: string | undefined }> {\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst listing = await this.cache.getIgnoreConfigListing(repoPath, (path, cacheable, signal) =>\n\t\t\tthis.readIgnoreConfigListing(path, cacheable, signal),\n\t\t);\n\t\tif (listing != null && (repoPath === commonPath || !mayConfigDifferByCheckout(listing))) {\n\t\t\tlet excludesFile: string | undefined;\n\t\t\tlet ignoreCase: string | undefined;\n\t\t\t// A key can repeat, and git answers with the last\n\t\t\tfor (const [key, value] of parseConfigListing(listing)) {\n\t\t\t\tif (key === 'core.excludesfile') {\n\t\t\t\t\texcludesFile = value?.trim() || undefined;\n\t\t\t\t} else if (key === 'core.ignorecase') {\n\t\t\t\t\t// A key set without a value is true\n\t\t\t\t\tignoreCase = value == null ? 'true' : value.trim() || undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { excludesFile: excludesFile, ignoreCase: ignoreCase };\n\t\t}\n\n\t\tconst [excludesFileResult, ignoreCaseResult] = await Promise.allSettled([\n\t\t\tthis.getConfig(repoPath, 'core.excludesFile'),\n\t\t\tthis.getConfig(repoPath, 'core.ignorecase'),\n\t\t]);\n\t\treturn { excludesFile: getSettledValue(excludesFileResult), ignoreCase: getSettledValue(ignoreCaseResult) };\n\t}\n\n\tprivate async readIgnoreConfigListing(\n\t\tcwd: string,\n\t\tcacheable: CacheController,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<string | undefined> {\n\t\t// Conditional includes and `extensions.worktreeConfig` tell whether one checkout's listing holds for the others.\n\t\t// Exit 1 is nothing set, a real answer; a failure must not reject, as the filter's per-checkout reads never did\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: cwd, cancellation: cancellation, errors: 'ignore' },\n\t\t\t'config',\n\t\t\t'--null',\n\t\t\t'--get-regexp',\n\t\t\t'^(core\\\\.excludesfile|core\\\\.ignorecase|includeif\\\\..*|extensions\\\\.worktreeconfig)$',\n\t\t);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tif (result.completion.status !== 'exited' || (result.exitCode !== 0 && result.exitCode !== 1)) {\n\t\t\tcacheable.invalidate();\n\t\t\treturn undefined;\n\t\t}\n\t\treturn result.stdout;\n\t}\n\n\t@trace({ quiet: true })\n\tgetConfigRegex(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfigRegex(global ? undefined : repoPath, pattern, () =>\n\t\t\tthis.getConfigRegexCore(repoPath, pattern, options),\n\t\t);\n\t}\n\n\t/**\n\t * Every value of each key matching `pattern` (`git config --get-regexp`), for a key git lets repeat, like\n\t * `remote.<name>.fetch`: {@link getConfigRegex} keeps only each key's last value. Cached beside it, so the same\n\t * writes and config changes clear it. Rejects when git couldn't read the configuration.\n\t */\n\t@trace({ quiet: true })\n\tasync getConfigRegexAll(repoPath: string, pattern: string): Promise<Map<string, string[]>> {\n\t\t// Under a key no regex is spelled as, with each key's values joined by a NUL, which no value can hold\n\t\t// Read from, and cached under, the common path, never the caller's checkout: every worktree is served this one\n\t\t// entry, so it must not take in a `config.worktree` of whichever checkout happened to read first\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst joined = await this.cache.getConfigRegex(commonPath, `\\0all\\0${pattern}`, async () => {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: commonPath, errors: 'ignore' },\n\t\t\t\t'config',\n\t\t\t\t'--null',\n\t\t\t\t'--get-regexp',\n\t\t\t\tpattern,\n\t\t\t);\n\t\t\t// 1 is git's exit code for no key matching\n\t\t\tif (result.completion.status !== 'exited' || (result.exitCode !== 0 && result.exitCode !== 1)) {\n\t\t\t\tthrow new Error(`Unable to read the config keys matching '${pattern}'`);\n\t\t\t}\n\n\t\t\treturn new Map(\n\t\t\t\tArray.from(parseConfigRegexNullOutput(result.stdout), ([key, values]) => [key, values.join('\\0')]),\n\t\t\t);\n\t\t});\n\t\treturn new Map(Array.from(joined, ([key, value]) => [key, value.split('\\0')]));\n\t}\n\n\tprivate async getConfigRegexCore(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst args = ['config', '--get-regex'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\targs.push(pattern);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\t// Deliberately NOT `.trim()`-ed: an explicit-empty value's LAST line ends in a meaningful trailing\n\t\t// space, and `.trim()`-ing the whole (possibly multi-line) buffer strips it — `parseConfigRegexOutput`\n\t\t// already skips the blank line a trailing newline produces, so no trim is needed.\n\t\treturn parseConfigRegexOutput(result.stdout);\n\t}\n\n\t@debug()\n\tasync setConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean },\n\t): Promise<void> {\n\t\tawait this.setConfigCore(repoPath, key, value, options);\n\n\t\t// A branch's upstream (`remote`/`merge`) is read back through the cached branch list, which a\n\t\t// `'config'` reset alone leaves serving the old tracking info.\n\t\tif (repoPath != null && !options?.file && /^branch\\..+\\.(?:remote|merge)$/i.test(key)) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches');\n\t\t}\n\n\t\t// A `--global` or custom-`file` write isn't this repository's `.git/config`\n\t\tif (repoPath != null && !options?.file && !options?.global) {\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config']);\n\t\t}\n\t}\n\n\tprivate async setConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean; throwOnError?: boolean },\n\t): Promise<void> {\n\t\tconst args: string[] = ['config'];\n\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\targs.push(global ? '--global' : '--local');\n\t\t}\n\n\t\tif (value == null) {\n\t\t\targs.push('--unset', key);\n\t\t} else {\n\t\t\targs.push(key, value);\n\t\t}\n\n\t\tawait this.git.run(\n\t\t\t{ cwd: repoPath ?? '', runLocally: true, errors: options?.throwOnError ? 'throw' : undefined },\n\t\t\t...args,\n\t\t);\n\n\t\t// Only invalidate cache when not using a custom file (custom files aren't cached)\n\t\tif (!options?.file) {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\t// Invalidate the cached value for this key and clear all regex patterns for this scope\n\t\t\tthis.cache.deleteConfig(global ? undefined : repoPath, key);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug({ quiet: true })\n\tasync getCurrentUser(repoPath: string): Promise<GitUser | undefined> {\n\t\tif (!repoPath) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst cached = this.cache.currentUser.get(repoPath);\n\t\tif (cached != null) return cached;\n\t\t// If we found null, user data was not found - don't bother trying again\n\t\tif (cached === null) return undefined;\n\n\t\tconst user: GitUser = { name: undefined, email: undefined };\n\n\t\ttry {\n\t\t\tconst configMap = await this.getConfigRegex(repoPath, '^user\\\\.(name|email)$', {\n\t\t\t\trunGitLocally: true,\n\t\t\t});\n\t\t\tif (configMap.size) {\n\t\t\t\tuser.name = configMap.get('user.name');\n\t\t\t\tuser.email = configMap.get('user.email');\n\t\t\t} else {\n\t\t\t\tuser.name =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_NAME || process_env.GIT_COMMITTER_NAME || userInfo()?.username || undefined;\n\t\t\t\tif (!user.name) {\n\t\t\t\t\t// If we found no user data, mark it so we won't bother trying again\n\t\t\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tuser.email =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_EMAIL ||\n\t\t\t\t\tprocess_env.GIT_COMMITTER_EMAIL ||\n\t\t\t\t\tprocess_env.EMAIL ||\n\t\t\t\t\t`${user.name}@${hostname()}`;\n\t\t\t}\n\n\t\t\tconst author = `${user.name} <${user.email}>`;\n\t\t\t// Check if there is a mailmap for the current user\n\t\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'check-mailmap', author);\n\n\t\t\tif (result.stdout && result.stdout !== author) {\n\t\t\t\tconst match = mappedAuthorRegex.exec(result.stdout);\n\t\t\t\tif (match != null) {\n\t\t\t\t\t[, user.name, user.email] = match;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.cache.currentUser.set(repoPath, user);\n\t\t\treturn user;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\n\t\t\t// Mark it so we won't bother trying again\n\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@gate()\n\t@trace({ quiet: true, exit: r => `returned ${r}` })\n\tasync getDefaultWorktreePath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn getBestPath(normalizePath(joinPaths(basePath, '..')));\n\t}\n\n\t@gate()\n\t@trace({\n\t\tquiet: true,\n\t\texit: r =>\n\t\t\t`returned ${r.uri.toString(true)}, commonUri=${r.commonUri?.toString(true)}, parentUri=${r.parentUri?.toString(true)}`,\n\t})\n\tasync getGitDir(repoPath: string): Promise<GitDir> {\n\t\tconst cached = this.cache.gitDir.get(repoPath);\n\t\tif (cached != null) return cached;\n\n\t\tconst scope = getScopedLogger();\n\t\tconst repoInfo = await this.getRepositoryInfo(repoPath);\n\n\t\tlet gitDir: GitDir;\n\t\tif (!Array.isArray(repoInfo) && repoInfo != null) {\n\t\t\tgitDir = {\n\t\t\t\turi: fileUri(repoInfo.gitDir),\n\t\t\t\tcommonUri: repoInfo.commonGitDir ? fileUri(repoInfo.commonGitDir) : undefined,\n\t\t\t\tparentUri: repoInfo.superprojectPath ? fileUri(repoInfo.superprojectPath) : undefined,\n\t\t\t};\n\t\t} else {\n\t\t\tgitDir = {\n\t\t\t\turi: this.provider.getAbsoluteUri(joinPaths(repoPath, '.git'), repoPath),\n\t\t\t};\n\n\t\t\tconst gitDirPath = gitDir.uri.toString(true);\n\t\t\tscope?.warn(`rev-parse failed for '${repoPath}'; falling back to '${gitDirPath}'`);\n\t\t\tthis.context.hooks?.operations?.onGitDirResolveFailed?.(\n\t\t\t\trepoPath,\n\t\t\t\tgitDirPath,\n\t\t\t\t`rev_parse returned ${JSON.stringify(repoInfo)}`,\n\t\t\t);\n\t\t}\n\t\tthis.cache.gitDir.set(repoPath, gitDir);\n\n\t\treturn gitDir;\n\t}\n\n\t/**\n\t * Gets the path to the .git/gk/config file for storing GitKraken-specific metadata.\n\t * Uses commonPath for worktrees so all worktrees share the same data.\n\t */\n\tprivate async getGkConfigPath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\t// Use commonUri (main .git dir) for worktrees, otherwise use uri\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn joinPaths(basePath, 'gk', 'config');\n\t}\n\n\t@trace({ quiet: true })\n\tasync getGkConfig(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\toptions?: { throwOnError?: boolean },\n\t): Promise<string | undefined> {\n\t\tif (options?.throwOnError) return this.getGkConfigStrict(repoPath, key);\n\n\t\t// Served in-memory from a single bulk read of the whole `.git/gk/config` (no gk read needs `--type`).\n\t\t// Canonicalize the key the way git does for `--get-regexp` output so subsection-less keys\n\t\t// (e.g. `gk.defaultRemote`) match, and coerce an empty value to undefined to match the old\n\t\t// `git config --get` (`.trim() || undefined`) contract.\n\t\tconst map = await this.getGkConfigMap(repoPath);\n\t\treturn map.get(canonicalizeGitConfigKey(key)) || undefined;\n\t}\n\n\tprivate async getGkConfigStrict(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t): Promise<string | undefined> {\n\t\tawait this.migrateGkConfigFromGitConfig(this.cache.getCommonPath(repoPath));\n\t\tconst path = await this.getGkConfigPath(repoPath);\n\t\tif (path == null) throw new Error('Unable to locate GitKraken config storage');\n\n\t\ttry {\n\t\t\tawait fs.lstat(path);\n\t\t} catch (ex) {\n\t\t\tif ((ex as NodeJS.ErrnoException).code !== 'ENOENT') throw ex;\n\n\t\t\t// Windows also reports ENOENT for a child beneath a file, so absence needs a directory parent.\n\t\t\tfor (const parent of [dirname(path), dirname(dirname(path))]) {\n\t\t\t\tconst entry = await fs.lstat(parent).catch((parentError: unknown) => {\n\t\t\t\t\tif ((parentError as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n\n\t\t\t\t\tthrow parentError;\n\t\t\t\t});\n\t\t\t\tif (entry == null) continue;\n\n\t\t\t\tif (entry.isDirectory() || (entry.isSymbolicLink() && (await fs.stat(parent)).isDirectory())) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\n\t\t// Listing has no ambiguous \"no matching key\" exit code; storage failures must stop read-modify-write.\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, errors: 'throw', runLocally: true },\n\t\t\t'config',\n\t\t\t'--null',\n\t\t\t'--list',\n\t\t\t'-f',\n\t\t\tpath,\n\t\t);\n\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0) {\n\t\t\tthrow new Error('Unable to read GitKraken config');\n\t\t}\n\n\t\treturn parseConfigRegexNullOutput(result.stdout).get(canonicalizeGitConfigKey(key))?.at(-1) || undefined;\n\t}\n\n\t@trace({ quiet: true })\n\tasync getGkConfigRegex(repoPath: string, pattern: string): Promise<Map<string, string>> {\n\t\tconst all = await this.getGkConfigMap(repoPath);\n\n\t\t// Filter the bulk map in-memory. The old path passed `pattern` to `git config --get-regex`\n\t\t// (errors ignored → empty result on a bad pattern); preserve that no-throw contract here\n\t\t// rather than letting an invalid-JS pattern reject the read.\n\t\tlet re: RegExp;\n\t\ttry {\n\t\t\tre = new RegExp(pattern);\n\t\t} catch {\n\t\t\treturn new Map();\n\t\t}\n\n\t\tconst result = new Map<string, string>();\n\t\tfor (const [k, v] of all) {\n\t\t\tif (re.test(k)) {\n\t\t\t\tresult.set(k, v);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Reads the entire `.git/gk/config` in one `git config --get-regexp` and caches the parsed map\n\t * per commonPath. Per-key (getGkConfig) and per-namespace (getGkConfigRegex) lookups are served\n\t * from this single cached read — so a branch-overview render that fans out across several gk\n\t * namespaces costs one `git config` call instead of one per namespace.\n\t */\n\tprivate async getGkConfigMap(repoPath: string): Promise<Map<string, string>> {\n\t\t// Migrate BEFORE populating the cache, not inside the factory: a first-time migration calls\n\t\t// clearCaches('gkConfig'), which would otherwise evict the in-flight gkConfigMap entry the\n\t\t// cache wrapper synchronously stored and force a redundant bulk re-read. Checked here too, not only in the\n\t\t// migration: every gk lookup lands here, and entering the migration logs it.\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tif (!this._migratedRepos.has(commonPath)) {\n\t\t\tawait this.migrateGkConfigFromGitConfig(commonPath);\n\t\t}\n\t\treturn this.cache.getGkConfigMap(repoPath, async cacheable => {\n\t\t\tconst read = this.trackGkConfigMapRead(repoPath);\n\t\t\tconst { map, failed } = await this.getGkConfigRegexCore(repoPath, gkConfigAllPattern, read);\n\t\t\t// This map has no TTL and everything gk-related reads through it, so a failed read cached as an\n\t\t\t// empty map would report every branch as having no stored base or merge target for the session —\n\t\t\t// and would make the section-cleanup pre-checks skip real work. Only cache a read that happened.\n\t\t\tif (failed) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t\tfailedGkConfigMaps.set(map, read);\n\t\t\t}\n\t\t\treturn map;\n\t\t});\n\t}\n\n\t/** Records the read that is creating `repoPath`'s cached gk config map (see {@link gkConfigMapReads}) */\n\tprivate trackGkConfigMapRead(repoPath: string): GkConfigMapRead {\n\t\tlet reads = gkConfigMapReads.get(this.cache);\n\t\tif (reads == null) {\n\t\t\treads = new Map();\n\t\t\tgkConfigMapReads.set(this.cache, reads);\n\t\t}\n\n\t\tconst read: GkConfigMapRead = { spawned: false };\n\t\treads.set(this.cache.getCommonPath(repoPath), read);\n\t\treturn read;\n\t}\n\n\t/**\n\t * Hard-evicts the cached gk config map, unless `.git/gk/config` is unchanged since the read behind it stat'd the\n\t * file, or that read is yet to spawn. The stat is taken before the read, so a write landing between the two\n\t * only costs a re-read on its report, never a missed change. Returns the read it kept, if any.\n\t */\n\tprivate evictGkConfigMapIfChanged(repoPath: string): GkConfigMapRead | undefined {\n\t\tconst read = gkConfigMapReads.get(this.cache)?.get(this.cache.getCommonPath(repoPath));\n\t\tif (read != null) {\n\t\t\tif (!read.spawned) return read;\n\t\t\tif (\n\t\t\t\tread.path != null &&\n\t\t\t\tread.fingerprint != null &&\n\t\t\t\tgetGkConfigFingerprint(read.path) === read.fingerprint\n\t\t\t) {\n\t\t\t\treturn read;\n\t\t\t}\n\t\t}\n\n\t\tthis.cache.deleteGkConfigMap(repoPath);\n\t\treturn undefined;\n\t}\n\n\tprivate async getGkConfigRegexCore(\n\t\trepoPath: string,\n\t\tpattern: string,\n\t\tread: GkConfigMapRead,\n\t): Promise<{ map: Map<string, string>; failed: boolean }> {\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tread.spawned = true;\n\t\t\t// No `.git/gk/config` yet is a real answer, not a failure — a repo GitLens has never written to.\n\t\t\treturn { map: new Map(), failed: false };\n\t\t}\n\n\t\t// Taken before the spawn, never after: a write landing in between then only makes its report re-read the file.\n\t\t// The clock is read before the stat, so the stat is no earlier than the time it's checked against.\n\t\tread.path = gkConfigPath;\n\t\tread.fingerprint = getGkConfigFingerprint(gkConfigPath, gkConfigFingerprintClock.now());\n\t\tread.spawned = true;\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t'config',\n\t\t\t'--get-regex',\n\t\t\t'-f',\n\t\t\tgkConfigPath,\n\t\t\tpattern,\n\t\t);\n\t\t// `--get-regex` exits 1 when nothing matches, which IS an answer (an empty gk config).\n\t\tconst exited = result.completion.status === 'exited';\n\t\treturn {\n\t\t\t// Deliberately NOT `.trim()`-ed — see `getConfigRegexCore`'s comment: it strips the trailing\n\t\t\t// space that marks an explicit-empty value's LAST line, silently dropping it from the map.\n\t\t\tmap: parseConfigRegexOutput(result.stdout),\n\t\t\tfailed: !exited || (result.exitCode !== 0 && result.exitCode !== 1),\n\t\t};\n\t}\n\n\t@debug()\n\tasync setGkConfig(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[]; throwOnError?: boolean },\n\t): Promise<void> {\n\t\t// A self-write (with skip targets) and a user-driven write of the same value have different invalidation\n\t\t// semantics, so only a matching skip option may join: otherwise the user-driven write would skip invalidation.\n\t\t// Sorted, so the same targets in any order match.\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst skipKey = options?.skipInvalidation?.length ? options.skipInvalidation.toSorted().join(',') : '';\n\t\tconst pendingKey = `${commonPath}\\0${key}`;\n\t\tconst throwOnError = options?.throwOnError === true;\n\n\t\tconst previous = this._pendingGkConfigWrites.get(pendingKey);\n\t\tif (\n\t\t\tprevious != null &&\n\t\t\tprevious.value === value &&\n\t\t\tprevious.skipKey === skipKey &&\n\t\t\tprevious.throwOnError === throwOnError\n\t\t) {\n\t\t\treturn previous.promise;\n\t\t}\n\n\t\tconst promise =\n\t\t\tprevious != null\n\t\t\t\t? previous.promise.catch(() => {}).then(() => this.setGkConfigCore(repoPath, key, value, options))\n\t\t\t\t: this.setGkConfigCore(repoPath, key, value, options);\n\t\tconst pending = { value: value, skipKey: skipKey, throwOnError: throwOnError, promise: promise };\n\t\tthis._pendingGkConfigWrites.set(pendingKey, pending);\n\t\t// Swallow the cleanup chain's rejection so a failed write doesn't surface as an unhandled\n\t\t// rejection separate from the caller's own handling of the returned promise.\n\t\tvoid promise\n\t\t\t.finally(() => {\n\t\t\t\tif (this._pendingGkConfigWrites.get(pendingKey) === pending) {\n\t\t\t\t\tthis._pendingGkConfigWrites.delete(pendingKey);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(() => {});\n\t\treturn promise;\n\t}\n\n\tprivate async setGkConfigCore(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[]; throwOnError?: boolean },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tif (options?.throwOnError) throw new Error('Unable to locate GitKraken config storage');\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) {\n\t\t\tif (options?.throwOnError) throw new Error('Unable to prepare GitKraken config storage');\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Seed the reconcile snapshot from the pre-write file, so the write below can tell whether it changed the\n\t\t// value (first tracked-key write per repository only; the bulk read is cached for everything else)\n\t\tif (this.cache.needsGkConfigSnapshot(repoPath, key)) {\n\t\t\tawait this.getGkConfigMap(repoPath);\n\t\t}\n\n\t\tawait this._gkConfigWrites.run(gkConfigPath, undefined, () =>\n\t\t\tthis.setConfigCore(repoPath, key, value, { file: gkConfigPath, throwOnError: options?.throwOnError }),\n\t\t);\n\n\t\t// Invalidate the bulk map (so the next read re-fetches) and the derived caches for this key's\n\t\t// ref, then record the write, which fires `onDidChangeMergeTargets` when it changed a merge key —\n\t\t// except for a self-write (`skipInvalidation` given: an internal save while answering a read), which\n\t\t// nobody awaits; only another process's watcher reports that one.\n\t\t// The `.git/gk/config` file-watcher also fires `'gkConfig'` shortly after, which reconciles\n\t\t// the same change (see `reconcileGkConfig`) — recording our own write here keeps that later\n\t\t// reconcile from re-diffing this key as an external change.\n\t\tthis.cache.deleteGkConfig(repoPath, key, options);\n\t\tthis.cache.recordGkConfigWrite(repoPath, key, value, { announce: options?.skipInvalidation == null });\n\n\t\t// The watcher's report of this write can't reach the cached branches: the snapshot already holds the value\n\t\tconst metadataKey = parseBranchMetadataGkConfigKey(key);\n\t\tif (metadataKey != null) {\n\t\t\tthis.cache.patchBranchMetadata(repoPath, [\n\t\t\t\t{ branch: metadataKey.branch, key: metadataKey.key, value: value },\n\t\t\t]);\n\t\t}\n\t}\n\n\t@debug()\n\tasync removeGkConfigBranchSection(repoPath: string, ref: string): Promise<void> {\n\t\tif (await this.runGkConfigSectionCommand(repoPath, ref, '--remove-section', `branch.${ref}`)) {\n\t\t\tthis.cache.recordGkConfigSectionWrite(repoPath, ref);\n\t\t}\n\t}\n\n\t@debug()\n\tasync renameGkConfigBranchSection(repoPath: string, oldRef: string, newRef: string): Promise<void> {\n\t\t// Clear the destination first, unconditionally. `--rename-section` onto a section that already\n\t\t// exists APPENDS rather than replaces, leaving duplicate keys — and git resolves a duplicated key\n\t\t// to its LAST value, so the orphan left by an earlier branch of this name would win over the\n\t\t// metadata being moved. Doing this outside `runGkConfigSectionCommand` also covers the case where\n\t\t// the source has no gk keys at all, where its pre-check would otherwise skip the whole call and\n\t\t// leave the orphan in place for the renamed branch to inherit.\n\t\tawait this.removeGkConfigBranchSection(repoPath, newRef);\n\t\tif (\n\t\t\tawait this.runGkConfigSectionCommand(\n\t\t\t\trepoPath,\n\t\t\t\toldRef,\n\t\t\t\t'--rename-section',\n\t\t\t\t`branch.${oldRef}`,\n\t\t\t\t`branch.${newRef}`,\n\t\t\t)\n\t\t) {\n\t\t\tthis.cache.recordGkConfigSectionWrite(repoPath, oldRef, newRef);\n\t\t}\n\t}\n\n\t/**\n\t * Runs a section-level `git config` against `.git/gk/config` for `ref`'s section, skipping the\n\t * subprocess entirely when the bulk map (already cached on any warm path) shows that ref has no gk\n\t * keys — the overwhelmingly common case, since most branches never get gk metadata written at all.\n\t *\n\t * Never throws: callers invoke this as bookkeeping after a branch op has already succeeded, so a\n\t * failure here must not be reported as (or roll back) the branch/checkout/worktree operation itself.\n\t * The worst case is metadata that stays behind until the next op on that name.\n\t *\n\t * Returns whether `.git/gk/config` now reflects the command (it ran, or there was nothing to do), so the\n\t * caller can record its own write in the reconcile snapshot only when it actually landed.\n\t */\n\tprivate async runGkConfigSectionCommand(repoPath: string, ref: string, ...args: string[]): Promise<boolean> {\n\t\tconst scope = getScopedLogger();\n\t\ttry {\n\t\t\tconst prefix = `branch.${ref}.`;\n\t\t\tconst map = await this.getGkConfigMap(repoPath);\n\t\t\t// The shortcut needs a map with CONTENT to be trustworthy. A failed bulk read resolves empty\n\t\t\t// (see `getGkConfigMap`) — the cache refuses it, but this caller still holds it — and an empty map\n\t\t\t// read as authoritative would skip the removal entirely, leaving a deleted branch's base,\n\t\t\t// disposition and issue links behind for the next branch reusing that name. When it's empty, run\n\t\t\t// the command: worst case is a no-op on a section that isn't there.\n\t\t\tif (map.size && !some(map.keys(), k => k.startsWith(prefix))) return true;\n\n\t\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\t\tif (!gkConfigPath) return false;\n\t\t\t// `getGkConfigPath` only joins a path, so check the file: with no `.git/gk/config` there is\n\t\t\t// genuinely nothing to remove, and running the command would fail with a fatal we'd then warn\n\t\t\t// about on every branch delete in a repo that has never had gk metadata.\n\t\t\tif (!(await fsExists(gkConfigPath))) return true;\n\n\t\t\tconst result = await this._gkConfigWrites.run(gkConfigPath, undefined, () =>\n\t\t\t\tthis.git.run(\n\t\t\t\t\t{ cwd: repoPath, runLocally: true, errors: 'ignore' },\n\t\t\t\t\t'config',\n\t\t\t\t\t'-f',\n\t\t\t\t\tgkConfigPath,\n\t\t\t\t\t...args,\n\t\t\t\t),\n\t\t\t);\n\t\t\t// `errors: 'ignore'` is deliberate — see the note above on why this must never throw — but that\n\t\t\t// also means the `catch` below never sees a failed write, so without this the metadata silently\n\t\t\t// stays behind (or, on a rename, stays under the old name) with nothing recorded anywhere.\n\t\t\t// Reported, not thrown: the branch op it follows has already succeeded.\n\t\t\tconst succeeded = result.completion.status === 'exited' && result.exitCode === 0;\n\t\t\tif (!succeeded) {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`Failed to update gk config section for '${ref}' in '${repoPath}': ${\n\t\t\t\t\t\tresult.completion.status === 'exited'\n\t\t\t\t\t\t\t? `git config exited ${result.exitCode}`\n\t\t\t\t\t\t\t: `${result.completion.status} · ${result.completion.error.message}`\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// A whole section moved or vanished, which the per-key `deleteGkConfig` cascade can't express.\n\t\t\t// Drop the bulk map so the next read hits disk; the derived caches for the affected ref are\n\t\t\t// evicted by the caller's own `deleteBaseBranchName` plus the `'branches'` cascade it fires.\n\t\t\tthis.cache.deleteGkConfigMap(repoPath);\n\t\t\treturn succeeded;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to update gk config section for '${ref}' in '${repoPath}'`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Reconciles a watcher-observed `'gkConfig'` change — registered with the shared `Cache` as its\n\t * gk-config reconciler (see the constructor). Hard-evicts the bulk map as the change is reported, unless the file\n\t * is unchanged since the map was read (see `evictGkConfigMapIfChanged`), then reads it (from disk, or the map\n\t * kept) and diffs it against `priorSnapshot` to cascade\n\t * `baseBranchName`/`branchOverviews` only for refs whose merge-relevant keys actually changed, and to evict the\n\t * branches only when a branch metadata key (another process's activity stamp) changed. The diff runs on a kept\n\t * map too: a read can land between an external write and its report, and already hold the change.\n\t *\n\t * Serialized per commonPath: a reconcile is a read-then-diff against a single shared baseline, so\n\t * two overlapping passes would both diff against the same snapshot and the later change could fall\n\t * out entirely. An event arriving mid-pass instead queues one trailing pass, which re-reads and\n\t * diffs against the baseline the previous pass just established.\n\t *\n\t * `.git/gk/config` lives in the common dir, so the watcher delivers one event per worktree sharing it\n\t * (see `WatchGroup.onCommonEvent`), and core's own write is reported too. Siblings never run concurrently — the\n\t * later ones fold into the in-flight pass — but folding in arms the trailing pass, which re-diffs the map unless\n\t * the file changed again. However many siblings land while a pass is running, they coalesce into that single\n\t * trailing pass; the cost is the price of never dropping a real change.\n\t * Every reporting path is retained because `Repository` matches the follow-up change event on exact\n\t * path, and a sibling worktree that never receives one would sit on stale derived values.\n\t */\n\tprivate reconcileGkConfig(\n\t\trepoPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t): GkReconcileOutcome | Promise<GkReconcileOutcome> {\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\n\t\tconst inflight = this._gkConfigReconciles.get(commonPath);\n\t\tif (inflight != null) {\n\t\t\t// Now, not in the trailing pass: a read between this report and that pass must see the change, and would\n\t\t\t// otherwise join the map read before it\n\t\t\tthis.evictGkConfigMapIfChanged(repoPath);\n\t\t\tinflight.paths.set(repoPath, this.cache.getCloseGeneration(repoPath));\n\t\t\tinflight.pending = true;\n\t\t\t// Handed to the in-flight pass's trailing loop, so it IS being reconciled — just not by this\n\t\t\t// call. Reporting a failure here would coarse-clear on every write in a burst.\n\t\t\treturn 'deferred';\n\t\t}\n\n\t\tconst entry = { pending: false, paths: new Map([[repoPath, this.cache.getCloseGeneration(repoPath)]]) };\n\t\tthis._gkConfigReconciles.set(commonPath, entry);\n\t\treturn this.runGkConfigReconcile(commonPath, priorSnapshot, entry);\n\t}\n\n\t/** Drops paths that closed since they reported the change; returns whichever remain. */\n\tprivate pruneClosedReconcilePaths(paths: Map<string, number>): Map<string, number> {\n\t\tfor (const [path, generation] of paths) {\n\t\t\tif (this.cache.getCloseGeneration(path) !== generation) {\n\t\t\t\tpaths.delete(path);\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\tprivate async runGkConfigReconcile(\n\t\tcommonPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t\tentry: { pending: boolean; paths: Map<string, number> },\n\t): Promise<GkReconcileOutcome> {\n\t\tconst scope = getScopedLogger();\n\t\tlet snapshot = priorSnapshot;\n\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tentry.pending = false;\n\n\t\t\t\t// Read through a path that's still open — one worktree closing must not drop the event for\n\t\t\t\t// its still-open siblings, which is why this reads from the surviving set rather than from\n\t\t\t\t// whichever path happened to report first.\n\t\t\t\tconst readPath = first(this.pruneClosedReconcilePaths(entry.paths).keys());\n\t\t\t\t// Every path that reported the change has since closed — nothing left to reconcile, and\n\t\t\t\t// nothing left holding a stale derived value either.\n\t\t\t\tif (readPath == null) return 'reconciled';\n\n\t\t\t\t// Runs before this pass's first await, so a first pass decides as the change is reported; a trailing one\n\t\t\t\t// re-checks the map the pass before it read\n\t\t\t\tconst kept = this.evictGkConfigMapIfChanged(readPath);\n\t\t\t\tlet freshMap = await this.getGkConfigMap(readPath);\n\t\t\t\t// A kept read that failed was started before this pass, which would otherwise have read the file\n\t\t\t\t// itself, so it gets one fresh read before the coarse fallback\n\t\t\t\tif (kept != null && failedGkConfigMaps.get(freshMap) === kept) {\n\t\t\t\t\tthis.cache.deleteGkConfigMap(readPath);\n\t\t\t\t\tfreshMap = await this.getGkConfigMap(readPath);\n\t\t\t\t}\n\t\t\t\t// A failed read diffed as the file's content would remove every key it missed\n\t\t\t\tif (failedGkConfigMaps.has(freshMap)) {\n\t\t\t\t\tthrow new Error(`Failed to read the gk config through '${readPath}'`);\n\t\t\t\t}\n\n\t\t\t\t// Paths may have closed while the re-read was in flight; if every one did, there's nothing\n\t\t\t\t// left to reconcile against.\n\t\t\t\tif (!this.pruneClosedReconcilePaths(entry.paths).size) return 'reconciled';\n\n\t\t\t\tif (this.cache.reconcileGkConfigMap(readPath, snapshot, freshMap)) {\n\t\t\t\t\t// Fire a follow-up change: a consumer that re-read on the original event may have done so\n\t\t\t\t\t// before this async cascade landed, so it would otherwise hold a stale derived value with\n\t\t\t\t\t// nothing left to prompt another re-read. Only the host hears it: it is not a write of\n\t\t\t\t\t// core's, and announcing it as one would evict the map just reconciled.\n\t\t\t\t\tfor (const path of entry.paths.keys()) {\n\t\t\t\t\t\tthis.provider.notifyHost(path, ['gkConfig']);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// `reconcileGkConfigMap` advanced the stored baseline to what we just read; a trailing pass\n\t\t\t\t// diffs its own read against that.\n\t\t\t\tsnapshot = this.cache.getGkConfigSnapshot(readPath);\n\t\t\t} while (entry.pending);\n\n\t\t\treturn 'reconciled';\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to reconcile gk config for '${commonPath}'`);\n\t\t\t// The caller falls back to the coarse clear — derived caches are otherwise left holding\n\t\t\t// pre-change values with nothing to correct them.\n\t\t\treturn 'failed';\n\t\t} finally {\n\t\t\tthis._gkConfigReconciles.delete(commonPath);\n\t\t}\n\t}\n\n\t@debug({ quiet: true })\n\tasync getSigningConfig(repoPath: string): Promise<SigningConfig> {\n\t\t// Fetch all signing-related config in one call\n\t\tconst configMap = await this.getConfigRegex(\n\t\t\trepoPath,\n\t\t\t'^(commit\\\\.gpgsign|gpg\\\\.format|user\\\\.signingkey|gpg\\\\.program|gpg\\\\.ssh\\\\.program|gpg\\\\.ssh\\\\.allowedsignersfile)$',\n\t\t);\n\n\t\t// Extract values (keys are lowercase in git output)\n\t\tconst enabledRaw = configMap.get('commit.gpgsign');\n\t\tconst format = configMap.get('gpg.format');\n\t\tconst signingKey = configMap.get('user.signingkey');\n\t\tconst gpgProgram = configMap.get('gpg.program');\n\t\tconst sshProgram = configMap.get('gpg.ssh.program');\n\t\tconst allowedSignersFile = configMap.get('gpg.ssh.allowedsignersfile');\n\n\t\t// Check if git config has commit signing enabled, falling back to the host-level override\n\t\t// (e.g. VS Code's `git.enableCommitSigning`) when `commit.gpgsign` is unset/false. The host\n\t\t// override can only enable signing, never force it off.\n\t\tconst isEnabled = parseGitBoolean(enabledRaw) || this.context.config?.signing?.enabled === true;\n\n\t\treturn {\n\t\t\tenabled: isEnabled,\n\t\t\tformat: (format as SigningFormat) ?? 'gpg',\n\t\t\tsigningKey: signingKey,\n\t\t\tgpgProgram: gpgProgram,\n\t\t\tsshProgram: sshProgram,\n\t\t\tallowedSignersFile: allowedSignersFile,\n\t\t};\n\t}\n\n\t@debug({ quiet: true })\n\tasync validateSigningSetup(repoPath: string): Promise<ValidationResult> {\n\t\tconst config = await this.getSigningConfig(repoPath);\n\n\t\tif (!config.signingKey) {\n\t\t\treturn { valid: false, error: 'No signing key configured' };\n\t\t}\n\n\t\t// Basic validation: just check that a signing key is configured\n\t\t// Git will handle the actual validation when signing commits\n\t\treturn { valid: true };\n\t}\n\n\t@debug()\n\tasync setSigningConfig(\n\t\trepoPath: string,\n\t\tconfig: Partial<SigningConfig>,\n\t\toptions?: { global?: boolean },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst entries: [GitConfigKeys, string][] = [];\n\t\tif (config.enabled != null) {\n\t\t\tentries.push(['commit.gpgsign', config.enabled ? 'true' : 'false']);\n\t\t}\n\t\tif (config.format != null) {\n\t\t\tentries.push(['gpg.format', config.format]);\n\t\t}\n\t\tif (config.signingKey != null) {\n\t\t\tentries.push(['user.signingkey', config.signingKey]);\n\t\t}\n\t\tif (config.gpgProgram != null) {\n\t\t\tentries.push(['gpg.program', config.gpgProgram]);\n\t\t}\n\t\tif (config.sshProgram != null) {\n\t\t\tentries.push(['gpg.ssh.program', config.sshProgram]);\n\t\t}\n\t\tif (config.allowedSignersFile != null) {\n\t\t\tentries.push(['gpg.ssh.allowedSignersFile', config.allowedSignersFile]);\n\t\t}\n\n\t\tlet wrote = false;\n\t\ttry {\n\t\t\tfor (const [key, value] of entries) {\n\t\t\t\tawait this.setConfigCore(repoPath, key, value, options);\n\t\t\t\twrote = true;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t} finally {\n\t\t\t// One `config` announcement for the keys written, rather than one (each fanned out to every worktree) per key\n\t\t\tif (wrote && !options?.global) {\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config']);\n\t\t\t}\n\t\t}\n\t}\n\n\tgetSigningConfigFlags(config: SigningConfig): string[] {\n\t\tconst flags: string[] = [];\n\n\t\tif (config.gpgProgram) {\n\t\t\tflags.push('-c', `gpg.program=${config.gpgProgram}`);\n\t\t}\n\t\tif (config.format && config.format !== 'gpg') {\n\t\t\tflags.push('-c', `gpg.format=${config.format}`);\n\t\t}\n\t\tif (config.sshProgram) {\n\t\t\tflags.push('-c', `gpg.ssh.program=${config.sshProgram}`);\n\t\t}\n\t\tif (config.allowedSignersFile) {\n\t\t\tflags.push('-c', `gpg.ssh.allowedSignersFile=${config.allowedSignersFile}`);\n\t\t}\n\n\t\treturn flags;\n\t}\n\n\tasync getRepositoryInfo(\n\t\tcwd: string,\n\t): Promise<\n\t\t| { repoPath: string; gitDir: string; commonGitDir: string | undefined; superprojectPath: string | undefined }\n\t\t| [safe: true, repoPath: string]\n\t\t| [safe: false]\n\t\t| []\n\t> {\n\t\tlet result;\n\n\t\tif (this.context.workspace?.isTrusted === false) {\n\t\t\ttry {\n\t\t\t\tawait fs.stat(joinPaths(cwd, 'HEAD'));\n\t\t\t\tresult = await this.git.run(\n\t\t\t\t\t{ cwd: cwd, errors: 'throw', configs: ['-C', cwd] },\n\t\t\t\t\t'rev-parse',\n\t\t\t\t\t'--show-cdup',\n\t\t\t\t);\n\t\t\t\tif (!result.stdout.trim()) {\n\t\t\t\t\tLogger.warn(`Skipping (untrusted); bare clone repository detected in '${cwd}'`);\n\t\t\t\t\treturn emptyArray as [];\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throws, we should be good to open the repo\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tresult = await this.git.run(\n\t\t\t\t{ cwd: cwd, errors: 'throw' },\n\t\t\t\t'rev-parse',\n\t\t\t\t'--show-toplevel',\n\t\t\t\t'--git-dir',\n\t\t\t\t'--git-common-dir',\n\t\t\t\t'--show-superproject-working-tree',\n\t\t\t);\n\t\t\tif (!result.stdout) return emptyArray as [];\n\n\t\t\t// Output is 3-4 lines: show-toplevel, git-dir, git-common-dir, [show-superproject-working-tree]\n\t\t\t// The 4th line is only present for submodules\n\t\t\tconst lines = result.stdout.split('\\n').map(r => r.trimStart());\n\t\t\tconst [repoPath, dotGitPath, commonDotGitPath, superprojectPath] = lines;\n\n\t\t\tif (!repoPath) return emptyArray as [];\n\n\t\t\tconst normalizedRepoPath = normalizePath(repoPath.replace(/[\\r|\\n]+$/, ''));\n\n\t\t\tlet gitDir = dotGitPath;\n\t\t\tif (gitDir && !isAbsolute(gitDir)) {\n\t\t\t\tgitDir = joinPaths(cwd, gitDir);\n\t\t\t}\n\t\t\tgitDir = normalizePath(gitDir);\n\n\t\t\tlet commonGitDir: string | undefined;\n\t\t\tif (commonDotGitPath) {\n\t\t\t\tcommonGitDir = commonDotGitPath;\n\t\t\t\tif (!isAbsolute(commonGitDir)) {\n\t\t\t\t\tcommonGitDir = joinPaths(cwd, commonGitDir);\n\t\t\t\t}\n\t\t\t\tcommonGitDir = normalizePath(commonGitDir);\n\t\t\t\tif (commonGitDir === gitDir) {\n\t\t\t\t\tcommonGitDir = undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\trepoPath: normalizedRepoPath,\n\t\t\t\tgitDir: gitDir,\n\t\t\t\tcommonGitDir: commonGitDir,\n\t\t\t\tsuperprojectPath: superprojectPath?.trim() || undefined,\n\t\t\t};\n\t\t} catch (ex: any) {\n\t\t\tif (ex instanceof WorkspaceUntrustedError) return emptyArray as [];\n\n\t\t\tconst unsafeMatch =\n\t\t\t\t/(?:^fatal:\\s*detected dubious ownership in repository at '([^']+)'|unsafe repository \\('([^']+)' is owned by someone else\\))[\\s\\S]*(git config --global --add safe\\.directory [^\\n\\u2022]+)/m.exec(\n\t\t\t\t\tex.stderr,\n\t\t\t\t);\n\t\t\tif (unsafeMatch != null) {\n\t\t\t\tLogger.warn(\n\t\t\t\t\t`Skipping; unsafe repository detected in '${unsafeMatch[1] || unsafeMatch[2]}'; run '${\n\t\t\t\t\t\tunsafeMatch[3]\n\t\t\t\t\t}' to allow it`,\n\t\t\t\t);\n\t\t\t\treturn [false];\n\t\t\t}\n\n\t\t\tconst inDotGit = /this operation must be run in a work tree/.test(ex.stderr);\n\t\t\tif (inDotGit && this.context.workspace?.isTrusted !== false) {\n\t\t\t\tresult = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--is-bare-repository');\n\t\t\t\tif (result.stdout.trim() === 'true') {\n\t\t\t\t\tconst result = await this.revParseGitDir(cwd);\n\t\t\t\t\tconst repoPath = result?.commonPath ?? result?.path;\n\t\t\t\t\tif (repoPath?.length) return [true, repoPath];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (inDotGit || ex.code === 'ENOENT') {\n\t\t\t\tlet exists = inDotGit ? false : await fsExists(cwd);\n\t\t\t\tif (!exists) {\n\t\t\t\t\tdo {\n\t\t\t\t\t\tconst parent = dirname(cwd);\n\t\t\t\t\t\tif (parent === cwd || parent.length === 0) return emptyArray as [];\n\n\t\t\t\t\t\tcwd = parent;\n\t\t\t\t\t\texists = await fsExists(cwd);\n\t\t\t\t\t} while (!exists);\n\n\t\t\t\t\treturn this.getRepositoryInfo(cwd);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn emptyArray as [];\n\t\t}\n\t}\n\n\tprivate async revParseGitDir(cwd: string): Promise<{ path: string; commonPath?: string } | undefined> {\n\t\tconst result = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--git-dir', '--git-common-dir');\n\t\tif (!result.stdout) return undefined;\n\n\t\tlet [dotGitPath, commonDotGitPath] = result.stdout.split('\\n').map(r => r.trimStart());\n\n\t\tif (!isAbsolute(dotGitPath)) {\n\t\t\tdotGitPath = joinPaths(cwd, dotGitPath);\n\t\t}\n\t\tdotGitPath = normalizePath(dotGitPath);\n\n\t\tif (commonDotGitPath) {\n\t\t\tif (!isAbsolute(commonDotGitPath)) {\n\t\t\t\tcommonDotGitPath = joinPaths(cwd, commonDotGitPath);\n\t\t\t}\n\t\t\tcommonDotGitPath = normalizePath(commonDotGitPath);\n\n\t\t\treturn { path: dotGitPath, commonPath: commonDotGitPath !== dotGitPath ? commonDotGitPath : undefined };\n\t\t}\n\n\t\treturn { path: dotGitPath };\n\t}\n\n\t/**\n\t * Ensures the `.git/gk/` folder exists, but only if the parent `.git` directory already exists.\n\t * This prevents creating stray `.git` directories if the computed path is wrong.\n\t * Returns `true` if the folder was ensured, `false` if the parent `.git` does not exist.\n\t */\n\tprivate async ensureGkConfigFolder(\n\t\tgkConfigFolder: string,\n\t\tscope: ReturnType<typeof getScopedLogger>,\n\t): Promise<boolean> {\n\t\t// Verify the .git directory already exists — never create it from scratch\n\t\tconst dotGitDir = joinPaths(gkConfigFolder, '..');\n\t\ttry {\n\t\t\tawait fs.stat(dotGitDir);\n\t\t} catch {\n\t\t\tscope?.warn(`Skipping GK config write — expected git directory '${dotGitDir}' does not exist`);\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.mkdir(gkConfigFolder, { recursive: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigFolder}' directory`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate _migratedRepos = new Set<string>();\n\n\t/**\n\t * One-time migration of GK config entries from regular git config to `.git/gk/config`.\n\t * If `.git/gk/config` already exists, assumes migration is complete (or data is already there).\n\t * Preserves existing `.git/gk/config` values as source of truth (won't overwrite).\n\t * Removes migrated keys from regular git config to stop cluttering it.\n\t */\n\t@gate()\n\t@debug()\n\tprivate async migrateGkConfigFromGitConfig(repoPath: string): Promise<void> {\n\t\tif (this._migratedRepos.has(repoPath)) return;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\t// If .git/gk/config already exists, consider migration done — it was either\n\t\t// already migrated or values were written there directly via setGkConfig\n\t\ttry {\n\t\t\tawait fs.stat(gkConfigPath);\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// file doesn't exist, proceed with migration\n\t\t}\n\n\t\t// If .git/gk/config doesn't exist, create an empty file to prevent multiple migration attempts in future sessions\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;\n\n\t\ttry {\n\t\t\tawait fs.writeFile(gkConfigPath, new Uint8Array());\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigPath}' file`);\n\t\t}\n\n\t\t// Read legacy gk-* keys from regular git config\n\t\tlet migrateConfig: Map<string, string>;\n\t\ttry {\n\t\t\tmigrateConfig = await this.getConfigRegexCore(repoPath, '^branch\\\\..*\\\\.gk-', { runGitLocally: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, 'Failed to read legacy GK config entries');\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!migrateConfig.size) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tscope?.info(`Migrating ${String(migrateConfig.size)} GK config entries from git config to .git/gk/config`);\n\n\t\t// Copy legacy entries to .git/gk/config\n\t\tfor (const [key, value] of [...migrateConfig]) {\n\t\t\ttry {\n\t\t\t\tawait this._gkConfigWrites.run(gkConfigPath, undefined, () =>\n\t\t\t\t\tthis.setConfigCore(repoPath, key, value, { file: gkConfigPath }),\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to migrate key '${key}' to GK config`);\n\t\t\t\t// If we failed to migrate, delete it from the list so we won't try to remove it from git config later\n\t\t\t\tmigrateConfig.delete(key);\n\t\t\t}\n\t\t}\n\n\t\t// Remove legacy keys from regular git config (use --unset-all defensively)\n\t\tfor (const [key] of migrateConfig) {\n\t\t\ttry {\n\t\t\t\tawait this.git.run(\n\t\t\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t\t\t'config',\n\t\t\t\t\t'--local',\n\t\t\t\t\t'--unset-all',\n\t\t\t\t\tkey,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to remove migrated key '${key}' from git config`);\n\t\t\t}\n\t\t}\n\n\t\t// Clear caches since we modified both config files\n\t\tthis.cache.clearCaches(repoPath, 'config', 'gkConfig');\n\n\t\tthis._migratedRepos.add(repoPath);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgCA,MAAM,oBAAoB;AAC1B,MAAM,aAA+B,OAAO,OAAO,CAAC,CAAC;;AAGrD,MAAM,qBAAqB;;;;;;AAO3B,MAAM,qCAAqB,IAAI,QAA8C;;;;;;AAgB7E,MAAM,mCAAmB,IAAI,QAA6C;;AAG1E,MAAa,2BAA2B,EAAE,WAAmB,KAAK,IAAI,EAAE;AAExE,MAAM,UAAU;AAChB,MAAM,cAAc;;;;;;;;;;;AAYpB,SAAgB,mBAAmB,SAAiB,SAAiB,YAA6B;CACjG,MAAM,YAAY,UAAU,UAAU,UAAU;CAChD,MAAM,WAAW,UAAU,gBAAgB,MAAM,UAAU,gBAAgB,KAAK,KAAK,cAAc,MAAM;CACzG,OAAO,OAAO,KAAK,MAAM,UAAU,CAAC,IAAI,UAAU,YAAY;AAC/D;;;;;;;;;AAUA,SAAS,uBAAuB,MAAc,YAAyC;CACtF,IAAI;EACH,MAAM,OAAO,SAAS,MAAM;GAAE,QAAQ;GAAM,gBAAgB;EAAM,CAAC;EACnE,IAAI,QAAQ,MAAM,OAAO;EACzB,IAAI,cAAc,QAAQ,mBAAmB,KAAK,SAAS,KAAK,SAAS,UAAU,GAAG,OAAO,KAAA;EAE7F,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,KAAK,GAAG,KAAK,QAAQ,GAAG,KAAK;CACrE,QAAQ;EACP;CACD;AACD;;;;;;AAOA,SAAgB,yBAAyB,KAAqB;CAC7D,MAAM,QAAQ,IAAI,QAAQ,GAAG;CAC7B,IAAI,UAAU,IAAI,OAAO,IAAI,YAAY;CAEzC,MAAM,OAAO,IAAI,YAAY,GAAG;CAGhC,IAAI,UAAU,MAAM,OAAO,IAAI,YAAY;CAC3C,OAAO,GAAG,IAAI,MAAM,GAAG,KAAK,CAAC,CAAC,YAAY,IAAI,IAAI,MAAM,OAAO,IAAI,IAAI,IAAI,MAAM,IAAI,CAAC,CAAC,YAAY;AACpG;;;;;;;;;;;;;;;;AAiBA,SAAgB,uBACf,MACA,SACsB;CACtB,MAAM,4BAAY,IAAI,IAAoB;CAC1C,IAAI,CAAC,MAAM,OAAO;CAElB,KAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;EACpC,IAAI,CAAC,MAAM;EAEX,MAAM,aAAa,KAAK,QAAQ,GAAG;EACnC,IAAI,eAAe,IAAI;GACtB,IAAI,SAAS,kBACZ,UAAU,IAAI,MAAM,MAAM;GAE3B;EACD;EAEA,UAAU,IAAI,KAAK,UAAU,GAAG,UAAU,GAAG,KAAK,UAAU,aAAa,CAAC,CAAC;CAC5E;CAEA,OAAO;AACR;;;;;;;AAQA,SAAgB,2BAA2B,MAAiD;CAC3F,MAAM,4BAAY,IAAI,IAAsB;CAC5C,IAAI,CAAC,MAAM,OAAO;CAElB,KAAK,MAAM,SAAS,KAAK,MAAM,IAAI,GAAG;EACrC,MAAM,eAAe,MAAM,QAAQ,IAAI;EACvC,IAAI,iBAAiB,IAAI;EAEzB,MAAM,MAAM,MAAM,UAAU,GAAG,YAAY;EAC3C,MAAM,QAAQ,MAAM,UAAU,eAAe,CAAC;EAC9C,MAAM,SAAS,UAAU,IAAI,GAAG;EAChC,IAAI,UAAU,MACb,OAAO,KAAK,KAAK;OAEjB,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC;CAE5B;CAEA,OAAO;AACR;AAEA,SAAgB,gBAAgB,OAAoC;CACnE,IAAI,SAAS,MAAM,OAAO;CAE1B,MAAM,aAAa,MAAM,YAAY,CAAC,CAAC,KAAK;CAC5C,OAAO,eAAe,UAAU,eAAe,SAAS,eAAe,QAAQ,eAAe;AAC/F;;AAGA,SAAgB,kBAAkB,OAAwB;CACzD,MAAM,aAAa,MAAM,YAAY,CAAC,CAAC,KAAK;CAC5C,OACC,eAAe,MAAM,eAAe,WAAW,eAAe,QAAQ,eAAe,SAAS,eAAe;AAE/G;;AAGA,SAAgB,mBAAmB,SAA6D;CAC/F,MAAM,UAAsD,CAAC;CAC7D,KAAK,MAAM,SAAS,QAAQ,MAAM,IAAI,GAAG;EACxC,IAAI,CAAC,OAAO;EAEZ,MAAM,YAAY,MAAM,QAAQ,IAAI;EACpC,QAAQ,KACP,YAAY,IAAI,CAAC,OAAO,KAAA,CAAS,IAAI,CAAC,MAAM,UAAU,GAAG,SAAS,GAAG,MAAM,UAAU,YAAY,CAAC,CAAC,CACpG;CACD;CACA,OAAO;AACR;;;;;;;;;AAUA,SAAgB,0BAA0B,SAA0B;CACnE,KAAK,MAAM,CAAC,KAAK,UAAU,mBAAmB,OAAO,GAAG;EACvD,IAAI,QAAQ,6BAA6B;GAExC,IAAI,SAAS,QAAQ,CAAC,kBAAkB,KAAK,GAAG,OAAO;GAEvD;EACD;EACA,IAAI,CAAC,IAAI,WAAW,YAAY,GAAG;EAEnC,MAAM,YAAY,IAAI,UAAU,IAAqB,IAAI,YAAY,GAAG,CAAC;EACzE,IACC,UAAU,WAAW,YAAY,KACjC,UAAU,WAAW,SAAS,KAC9B,UAAU,WAAW,WAAW,GAEhC;EAGD,OAAO;CACR;CACA,OAAO;AACR;AAEA,IAAa,uBAAb,MAAkE;CAE/C;CACA;CACA;CACA;CAJlB,YACC,SACA,KACA,OACA,UACC;EAJgB,KAAA,UAAA;EACA,KAAA,MAAA;EACA,KAAA,QAAA;EACA,KAAA,WAAA;EAEjB,KAAK,MAAM,uBAAuB,UAAU,kBAAkB,KAAK,kBAAkB,UAAU,aAAa,CAAC;CAC9G;;;;;;;CAQA,sCAAuC,IAAI,IAA8D;;;;;CAMzG,yCAA0C,IAAI,IAG5C;;CAGF,kBAAmC,IAAI,eAAe;CAEtD,UAEC,UACA,KACA,SAC8B;EAC9B,MAAM,SAAS,SAAS,UAAU,YAAY;EAC9C,OAAO,KAAK,MAAM,UAAU,SAAS,KAAA,IAAY,UAAU,WAC1D,KAAK,cAAc,UAAU,KAAK,OAAO,CAC1C;CACD;CAEA,MAAc,cACb,UACA,KACA,SAC8B;EAC9B,MAAM,OAAO,CAAC,UAAU,OAAO;EAC/B,IAAI,SAAS,MACZ,KAAK,KAAK,MAAM,QAAQ,IAAI;OACtB,IAAI,SAAS,UAAU,YAAY,MACzC,KAAK,KAAK,UAAU;EAErB,IAAI,SAAS,MACZ,KAAK,KAAK,UAAU,QAAQ,MAAM;EAEnC,KAAK,KAAK,GAAG;EAMb,QAAO,MAJc,KAAK,IAAI,IAC7B;GAAE,KAAK,YAAY;GAAI,QAAQ;GAAU,YAAY,SAAS;EAAc,GAC5E,GAAG,IACJ,EAAA,CACc,OAAO,KAAK,KAAK,KAAA;CAChC;;;;;;CAOA,MACM,gBACL,UACgF;EAChF,MAAM,aAAa,KAAK,MAAM,cAAc,QAAQ;EACpD,MAAM,UAAU,MAAM,KAAK,MAAM,uBAAuB,WAAW,MAAM,WAAW,WACnF,KAAK,wBAAwB,MAAM,WAAW,MAAM,CACrD;EACA,IAAI,WAAW,SAAS,aAAa,cAAc,CAAC,0BAA0B,OAAO,IAAI;GACxF,IAAI;GACJ,IAAI;GAEJ,KAAK,MAAM,CAAC,KAAK,UAAU,mBAAmB,OAAO,GACpD,IAAI,QAAQ,qBACX,eAAe,OAAO,KAAK,KAAK,KAAA;QAC1B,IAAI,QAAQ,mBAElB,aAAa,SAAS,OAAO,SAAS,MAAM,KAAK,KAAK,KAAA;GAGxD,OAAO;IAAgB;IAA0B;GAAW;EAC7D;EAEA,MAAM,CAAC,oBAAoB,oBAAoB,MAAM,QAAQ,WAAW,CACvE,KAAK,UAAU,UAAU,mBAAmB,GAC5C,KAAK,UAAU,UAAU,iBAAiB,CAC3C,CAAC;EACD,OAAO;GAAE,cAAc,gBAAgB,kBAAkB;GAAG,YAAY,gBAAgB,gBAAgB;EAAE;CAC3G;CAEA,MAAc,wBACb,KACA,WACA,cAC8B;EAG9B,MAAM,SAAS,MAAM,KAAK,IAAI,IAC7B;GAAO;GAAmB;GAAc,QAAQ;EAAS,GACzD,UACA,UACA,gBACA,sFACD;EACA,IAAI,cAAc,SAAS,MAAM,IAAI,kBAAkB;EAEvD,IAAI,OAAO,WAAW,WAAW,YAAa,OAAO,aAAa,KAAK,OAAO,aAAa,GAAI;GAC9F,UAAU,WAAW;GACrB;EACD;EACA,OAAO,OAAO;CACf;CAEA,eAEC,UACA,SACA,SAC+B;EAC/B,MAAM,SAAS,SAAS,UAAU,YAAY;EAC9C,OAAO,KAAK,MAAM,eAAe,SAAS,KAAA,IAAY,UAAU,eAC/D,KAAK,mBAAmB,UAAU,SAAS,OAAO,CACnD;CACD;;;;;;CAOA,MACM,kBAAkB,UAAkB,SAAiD;EAI1F,MAAM,aAAa,KAAK,MAAM,cAAc,QAAQ;EACpD,MAAM,SAAS,MAAM,KAAK,MAAM,eAAe,YAAY,UAAU,WAAW,YAAY;GAC3F,MAAM,SAAS,MAAM,KAAK,IAAI,IAC7B;IAAE,KAAK;IAAY,QAAQ;GAAS,GACpC,UACA,UACA,gBACA,OACD;GAEA,IAAI,OAAO,WAAW,WAAW,YAAa,OAAO,aAAa,KAAK,OAAO,aAAa,GAC1F,MAAM,IAAI,MAAM,4CAA4C,QAAQ,EAAE;GAGvE,OAAO,IAAI,IACV,MAAM,KAAK,2BAA2B,OAAO,MAAM,IAAI,CAAC,KAAK,YAAY,CAAC,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC,CAClG;EACD,CAAC;EACD,OAAO,IAAI,IAAI,MAAM,KAAK,SAAS,CAAC,KAAK,WAAW,CAAC,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,CAAC;CAC9E;CAEA,MAAc,mBACb,UACA,SACA,SAC+B;EAC/B,MAAM,OAAO,CAAC,UAAU,aAAa;EACrC,IAAI,SAAS,MACZ,KAAK,KAAK,MAAM,QAAQ,IAAI;OACtB,IAAI,SAAS,UAAU,YAAY,MACzC,KAAK,KAAK,UAAU;EAErB,KAAK,KAAK,OAAO;EASjB,OAAO,wBAAuB,MAPT,KAAK,IAAI,IAC7B;GAAE,KAAK,YAAY;GAAI,QAAQ;GAAU,YAAY,SAAS;EAAc,GAC5E,GAAG,IACJ,EAAA,CAIqC,MAAM;CAC5C;CAEA,MACM,UACL,UACA,KACA,OACA,SACgB;EAChB,MAAM,KAAK,cAAc,UAAU,KAAK,OAAO,OAAO;EAItD,IAAI,YAAY,QAAQ,CAAC,SAAS,QAAQ,kCAAkC,KAAK,GAAG,GACnF,KAAK,QAAQ,OAAO,OAAO,UAAU,UAAU,UAAU;EAI1D,IAAI,YAAY,QAAQ,CAAC,SAAS,QAAQ,CAAC,SAAS,QACnD,KAAK,QAAQ,OAAO,YAAY,YAAY,UAAU,CAAC,QAAQ,CAAC;CAElE;CAEA,MAAc,cACb,UACA,KACA,OACA,SACgB;EAChB,MAAM,OAAiB,CAAC,QAAQ;EAEhC,IAAI,SAAS,MACZ,KAAK,KAAK,MAAM,QAAQ,IAAI;OACtB;GACN,MAAM,SAAS,SAAS,UAAU,YAAY;GAC9C,KAAK,KAAK,SAAS,aAAa,SAAS;EAC1C;EAEA,IAAI,SAAS,MACZ,KAAK,KAAK,WAAW,GAAG;OAExB,KAAK,KAAK,KAAK,KAAK;EAGrB,MAAM,KAAK,IAAI,IACd;GAAE,KAAK,YAAY;GAAI,YAAY;GAAM,QAAQ,SAAS,eAAe,UAAU,KAAA;EAAU,GAC7F,GAAG,IACJ;EAGA,IAAI,CAAC,SAAS,MAAM;GACnB,MAAM,SAAS,SAAS,UAAU,YAAY;GAE9C,KAAK,MAAM,aAAa,SAAS,KAAA,IAAY,UAAU,GAAG;EAC3D;CACD;CAEA,MAEM,eAAe,UAAgD;EACpE,IAAI,CAAC,UAAU,OAAO,KAAA;EAEtB,MAAM,QAAQ,gBAAgB;EAE9B,MAAM,SAAS,KAAK,MAAM,YAAY,IAAI,QAAQ;EAClD,IAAI,UAAU,MAAM,OAAO;EAE3B,IAAI,WAAW,MAAM,OAAO,KAAA;EAE5B,MAAM,OAAgB;GAAE,MAAM,KAAA;GAAW,OAAO,KAAA;EAAU;EAE1D,IAAI;GACH,MAAM,YAAY,MAAM,KAAK,eAAe,UAAU,yBAAyB,EAC9E,eAAe,KAChB,CAAC;GACD,IAAI,UAAU,MAAM;IACnB,KAAK,OAAO,UAAU,IAAI,WAAW;IACrC,KAAK,QAAQ,UAAU,IAAI,YAAY;GACxC,OAAO;IACN,KAAK,OACJA,IAAY,mBAAmBA,IAAY,sBAAsB,SAAS,CAAC,EAAE,YAAY,KAAA;IAC1F,IAAI,CAAC,KAAK,MAAM;KAEf,KAAK,MAAM,YAAY,IAAI,UAAU,IAAI;KACzC;IACD;IAEA,KAAK,QACJA,IAAY,oBACZA,IAAY,uBACZA,IAAY,SACZ,GAAG,KAAK,KAAK,GAAG,SAAS;GAC3B;GAEA,MAAM,SAAS,GAAG,KAAK,KAAK,IAAI,KAAK,MAAM;GAE3C,MAAM,SAAS,MAAM,KAAK,IAAI,IAAI;IAAE,KAAK;IAAU,QAAQ;GAAS,GAAG,iBAAiB,MAAM;GAE9F,IAAI,OAAO,UAAU,OAAO,WAAW,QAAQ;IAC9C,MAAM,QAAQ,kBAAkB,KAAK,OAAO,MAAM;IAClD,IAAI,SAAS,MACZ,GAAG,KAAK,MAAM,KAAK,SAAS;GAE9B;GAEA,KAAK,MAAM,YAAY,IAAI,UAAU,IAAI;GACzC,OAAO;EACR,SAAS,IAAI;GACZ,OAAO,MAAM,EAAE;GAGf,KAAK,MAAM,YAAY,IAAI,UAAU,IAAI;GACzC;EACD;CACD;CAEA,MAEM,uBAAuB,UAA+C;EAC3E,MAAM,SAAS,MAAM,KAAK,UAAU,QAAQ;EAC5C,IAAI,UAAU,MAAM,OAAO,KAAA;EAE3B,MAAM,YAAY,OAAO,aAAa,OAAO,IAAA,CAAK;EAClD,OAAO,YAAY,cAAc,UAAU,UAAU,IAAI,CAAC,CAAC;CAC5D;CAEA,MAMM,UAAU,UAAmC;EAClD,MAAM,SAAS,KAAK,MAAM,OAAO,IAAI,QAAQ;EAC7C,IAAI,UAAU,MAAM,OAAO;EAE3B,MAAM,QAAQ,gBAAgB;EAC9B,MAAM,WAAW,MAAM,KAAK,kBAAkB,QAAQ;EAEtD,IAAI;EACJ,IAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,YAAY,MAC3C,SAAS;GACR,KAAK,QAAQ,SAAS,MAAM;GAC5B,WAAW,SAAS,eAAe,QAAQ,SAAS,YAAY,IAAI,KAAA;GACpE,WAAW,SAAS,mBAAmB,QAAQ,SAAS,gBAAgB,IAAI,KAAA;EAC7E;OACM;GACN,SAAS,EACR,KAAK,KAAK,SAAS,eAAe,UAAU,UAAU,MAAM,GAAG,QAAQ,EACxE;GAEA,MAAM,aAAa,OAAO,IAAI,SAAS,IAAI;GAC3C,OAAO,KAAK,yBAAyB,SAAS,sBAAsB,WAAW,EAAE;GACjF,KAAK,QAAQ,OAAO,YAAY,wBAC/B,UACA,YACA,sBAAsB,KAAK,UAAU,QAAQ,GAC9C;EACD;EACA,KAAK,MAAM,OAAO,IAAI,UAAU,MAAM;EAEtC,OAAO;CACR;;;;;CAMA,MAAc,gBAAgB,UAA+C;EAC5E,MAAM,SAAS,MAAM,KAAK,UAAU,QAAQ;EAC5C,IAAI,UAAU,MAAM,OAAO,KAAA;EAG3B,MAAM,YAAY,OAAO,aAAa,OAAO,IAAA,CAAK;EAClD,OAAO,UAAU,UAAU,MAAM,QAAQ;CAC1C;CAEA,MACM,YACL,UACA,KACA,SAC8B;EAC9B,IAAI,SAAS,cAAc,OAAO,KAAK,kBAAkB,UAAU,GAAG;EAOtE,QAAO,MADW,KAAK,eAAe,QAAQ,EAAA,CACnC,IAAI,yBAAyB,GAAG,CAAC,KAAK,KAAA;CAClD;CAEA,MAAc,kBACb,UACA,KAC8B;EAC9B,MAAM,KAAK,6BAA6B,KAAK,MAAM,cAAc,QAAQ,CAAC;EAC1E,MAAM,OAAO,MAAM,KAAK,gBAAgB,QAAQ;EAChD,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,2CAA2C;EAE7E,IAAI;GACH,MAAMC,SAAG,MAAM,IAAI;EACpB,SAAS,IAAI;GACZ,IAAK,GAA6B,SAAS,UAAU,MAAM;GAG3D,KAAK,MAAM,UAAU,CAAC,QAAQ,IAAI,GAAG,QAAQ,QAAQ,IAAI,CAAC,CAAC,GAAG;IAC7D,MAAM,QAAQ,MAAMA,SAAG,MAAM,MAAM,CAAC,CAAC,OAAO,gBAAyB;KACpE,IAAK,YAAsC,SAAS,UAAU,OAAO,KAAA;KAErE,MAAM;IACP,CAAC;IACD,IAAI,SAAS,MAAM;IAEnB,IAAI,MAAM,YAAY,KAAM,MAAM,eAAe,MAAM,MAAMA,SAAG,KAAK,MAAM,EAAA,CAAG,YAAY,GACzF;IAED;GACD;GAEA,MAAM;EACP;EAGA,MAAM,SAAS,MAAM,KAAK,IAAI,IAC7B;GAAE,KAAK;GAAU,QAAQ;GAAS,YAAY;EAAK,GACnD,UACA,UACA,UACA,MACA,IACD;EACA,IAAI,OAAO,WAAW,WAAW,YAAY,OAAO,aAAa,GAChE,MAAM,IAAI,MAAM,iCAAiC;EAGlD,OAAO,2BAA2B,OAAO,MAAM,CAAC,CAAC,IAAI,yBAAyB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,KAAA;CAChG;CAEA,MACM,iBAAiB,UAAkB,SAA+C;EACvF,MAAM,MAAM,MAAM,KAAK,eAAe,QAAQ;EAK9C,IAAI;EACJ,IAAI;GACH,KAAK,IAAI,OAAO,OAAO;EACxB,QAAQ;GACP,uBAAO,IAAI,IAAI;EAChB;EAEA,MAAM,yBAAS,IAAI,IAAoB;EACvC,KAAK,MAAM,CAAC,GAAG,MAAM,KACpB,IAAI,GAAG,KAAK,CAAC,GACZ,OAAO,IAAI,GAAG,CAAC;EAGjB,OAAO;CACR;;;;;;;CAQA,MAAc,eAAe,UAAgD;EAK5E,MAAM,aAAa,KAAK,MAAM,cAAc,QAAQ;EACpD,IAAI,CAAC,KAAK,eAAe,IAAI,UAAU,GACtC,MAAM,KAAK,6BAA6B,UAAU;EAEnD,OAAO,KAAK,MAAM,eAAe,UAAU,OAAM,cAAa;GAC7D,MAAM,OAAO,KAAK,qBAAqB,QAAQ;GAC/C,MAAM,EAAE,KAAK,WAAW,MAAM,KAAK,qBAAqB,UAAU,oBAAoB,IAAI;GAI1F,IAAI,QAAQ;IACX,UAAU,WAAW;IACrB,mBAAmB,IAAI,KAAK,IAAI;GACjC;GACA,OAAO;EACR,CAAC;CACF;;CAGA,qBAA6B,UAAmC;EAC/D,IAAI,QAAQ,iBAAiB,IAAI,KAAK,KAAK;EAC3C,IAAI,SAAS,MAAM;GAClB,wBAAQ,IAAI,IAAI;GAChB,iBAAiB,IAAI,KAAK,OAAO,KAAK;EACvC;EAEA,MAAM,OAAwB,EAAE,SAAS,MAAM;EAC/C,MAAM,IAAI,KAAK,MAAM,cAAc,QAAQ,GAAG,IAAI;EAClD,OAAO;CACR;;;;;;CAOA,0BAAkC,UAA+C;EAChF,MAAM,OAAO,iBAAiB,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,KAAK,MAAM,cAAc,QAAQ,CAAC;EACrF,IAAI,QAAQ,MAAM;GACjB,IAAI,CAAC,KAAK,SAAS,OAAO;GAC1B,IACC,KAAK,QAAQ,QACb,KAAK,eAAe,QACpB,uBAAuB,KAAK,IAAI,MAAM,KAAK,aAE3C,OAAO;EAET;EAEA,KAAK,MAAM,kBAAkB,QAAQ;CAEtC;CAEA,MAAc,qBACb,UACA,SACA,MACyD;EACzD,MAAM,eAAe,MAAM,KAAK,gBAAgB,QAAQ;EACxD,IAAI,CAAC,cAAc;GAClB,KAAK,UAAU;GAEf,OAAO;IAAE,qBAAK,IAAI,IAAI;IAAG,QAAQ;GAAM;EACxC;EAIA,KAAK,OAAO;EACZ,KAAK,cAAc,uBAAuB,cAAc,yBAAyB,IAAI,CAAC;EACtF,KAAK,UAAU;EACf,MAAM,SAAS,MAAM,KAAK,IAAI,IAC7B;GAAE,KAAK;GAAU,QAAQ;GAAU,YAAY;EAAK,GACpD,UACA,eACA,MACA,cACA,OACD;EAEA,MAAM,SAAS,OAAO,WAAW,WAAW;EAC5C,OAAO;GAGN,KAAK,uBAAuB,OAAO,MAAM;GACzC,QAAQ,CAAC,UAAW,OAAO,aAAa,KAAK,OAAO,aAAa;EAClE;CACD;CAEA,MACM,YACL,UACA,KACA,OACA,SACgB;EAIhB,MAAM,aAAa,KAAK,MAAM,cAAc,QAAQ;EACpD,MAAM,UAAU,SAAS,kBAAkB,SAAS,QAAQ,iBAAiB,SAAS,CAAC,CAAC,KAAK,GAAG,IAAI;EACpG,MAAM,aAAa,GAAG,WAAW,IAAI;EACrC,MAAM,eAAe,SAAS,iBAAiB;EAE/C,MAAM,WAAW,KAAK,uBAAuB,IAAI,UAAU;EAC3D,IACC,YAAY,QACZ,SAAS,UAAU,SACnB,SAAS,YAAY,WACrB,SAAS,iBAAiB,cAE1B,OAAO,SAAS;EAGjB,MAAM,UACL,YAAY,OACT,SAAS,QAAQ,YAAY,CAAC,CAAC,CAAC,CAAC,WAAW,KAAK,gBAAgB,UAAU,KAAK,OAAO,OAAO,CAAC,IAC/F,KAAK,gBAAgB,UAAU,KAAK,OAAO,OAAO;EACtD,MAAM,UAAU;GAAS;GAAgB;GAAuB;GAAuB;EAAQ;EAC/F,KAAK,uBAAuB,IAAI,YAAY,OAAO;EAGnD,QACE,cAAc;GACd,IAAI,KAAK,uBAAuB,IAAI,UAAU,MAAM,SACnD,KAAK,uBAAuB,OAAO,UAAU;EAE/C,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;EAChB,OAAO;CACR;CAEA,MAAc,gBACb,UACA,KACA,OACA,SACgB;EAChB,MAAM,QAAQ,gBAAgB;EAE9B,MAAM,eAAe,MAAM,KAAK,gBAAgB,QAAQ;EACxD,IAAI,CAAC,cAAc;GAClB,IAAI,SAAS,cAAc,MAAM,IAAI,MAAM,2CAA2C;GAEtF;EACD;EAEA,MAAM,iBAAiB,UAAU,cAAc,IAAI;EAEnD,IAAI,CAAE,MAAM,KAAK,qBAAqB,gBAAgB,KAAK,GAAI;GAC9D,IAAI,SAAS,cAAc,MAAM,IAAI,MAAM,4CAA4C;GAEvF;EACD;EAIA,IAAI,KAAK,MAAM,sBAAsB,UAAU,GAAG,GACjD,MAAM,KAAK,eAAe,QAAQ;EAGnC,MAAM,KAAK,gBAAgB,IAAI,cAAc,KAAA,SAC5C,KAAK,cAAc,UAAU,KAAK,OAAO;GAAE,MAAM;GAAc,cAAc,SAAS;EAAa,CAAC,CACrG;EASA,KAAK,MAAM,eAAe,UAAU,KAAK,OAAO;EAChD,KAAK,MAAM,oBAAoB,UAAU,KAAK,OAAO,EAAE,UAAU,SAAS,oBAAoB,KAAK,CAAC;EAGpG,MAAM,cAAc,+BAA+B,GAAG;EACtD,IAAI,eAAe,MAClB,KAAK,MAAM,oBAAoB,UAAU,CACxC;GAAE,QAAQ,YAAY;GAAQ,KAAK,YAAY;GAAY;EAAM,CAClE,CAAC;CAEH;CAEA,MACM,4BAA4B,UAAkB,KAA4B;EAC/E,IAAI,MAAM,KAAK,0BAA0B,UAAU,KAAK,oBAAoB,UAAU,KAAK,GAC1F,KAAK,MAAM,2BAA2B,UAAU,GAAG;CAErD;CAEA,MACM,4BAA4B,UAAkB,QAAgB,QAA+B;EAOlG,MAAM,KAAK,4BAA4B,UAAU,MAAM;EACvD,IACC,MAAM,KAAK,0BACV,UACA,QACA,oBACA,UAAU,UACV,UAAU,QACX,GAEA,KAAK,MAAM,2BAA2B,UAAU,QAAQ,MAAM;CAEhE;;;;;;;;;;;;;CAcA,MAAc,0BAA0B,UAAkB,KAAa,GAAG,MAAkC;EAC3G,MAAM,QAAQ,gBAAgB;EAC9B,IAAI;GACH,MAAM,SAAS,UAAU,IAAI;GAC7B,MAAM,MAAM,MAAM,KAAK,eAAe,QAAQ;GAM9C,IAAI,IAAI,QAAQ,CAAC,KAAK,IAAI,KAAK,IAAG,MAAK,EAAE,WAAW,MAAM,CAAC,GAAG,OAAO;GAErE,MAAM,eAAe,MAAM,KAAK,gBAAgB,QAAQ;GACxD,IAAI,CAAC,cAAc,OAAO;GAI1B,IAAI,CAAE,MAAM,SAAS,YAAY,GAAI,OAAO;GAE5C,MAAM,SAAS,MAAM,KAAK,gBAAgB,IAAI,cAAc,KAAA,SAC3D,KAAK,IAAI,IACR;IAAE,KAAK;IAAU,YAAY;IAAM,QAAQ;GAAS,GACpD,UACA,MACA,cACA,GAAG,IACJ,CACD;GAKA,MAAM,YAAY,OAAO,WAAW,WAAW,YAAY,OAAO,aAAa;GAC/E,IAAI,CAAC,WACJ,OAAO,KACN,2CAA2C,IAAI,QAAQ,SAAS,KAC/D,OAAO,WAAW,WAAW,WAC1B,qBAAqB,OAAO,aAC5B,GAAG,OAAO,WAAW,OAAO,KAAK,OAAO,WAAW,MAAM,WAE9D;GAKD,KAAK,MAAM,kBAAkB,QAAQ;GACrC,OAAO;EACR,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,2CAA2C,IAAI,QAAQ,SAAS,EAAE;GACnF,OAAO;EACR;CACD;;;;;;;;;;;;;;;;;;;;;;;CAwBA,kBACC,UACA,eACmD;EACnD,MAAM,aAAa,KAAK,MAAM,cAAc,QAAQ;EAEpD,MAAM,WAAW,KAAK,oBAAoB,IAAI,UAAU;EACxD,IAAI,YAAY,MAAM;GAGrB,KAAK,0BAA0B,QAAQ;GACvC,SAAS,MAAM,IAAI,UAAU,KAAK,MAAM,mBAAmB,QAAQ,CAAC;GACpE,SAAS,UAAU;GAGnB,OAAO;EACR;EAEA,MAAM,QAAQ;GAAE,SAAS;GAAO,uBAAO,IAAI,IAAI,CAAC,CAAC,UAAU,KAAK,MAAM,mBAAmB,QAAQ,CAAC,CAAC,CAAC;EAAE;EACtG,KAAK,oBAAoB,IAAI,YAAY,KAAK;EAC9C,OAAO,KAAK,qBAAqB,YAAY,eAAe,KAAK;CAClE;;CAGA,0BAAkC,OAAiD;EAClF,KAAK,MAAM,CAAC,MAAM,eAAe,OAChC,IAAI,KAAK,MAAM,mBAAmB,IAAI,MAAM,YAC3C,MAAM,OAAO,IAAI;EAGnB,OAAO;CACR;CAEA,MAAc,qBACb,YACA,eACA,OAC8B;EAC9B,MAAM,QAAQ,gBAAgB;EAC9B,IAAI,WAAW;EAEf,IAAI;GACH,GAAG;IACF,MAAM,UAAU;IAKhB,MAAM,WAAW,MAAM,KAAK,0BAA0B,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC;IAGzE,IAAI,YAAY,MAAM,OAAO;IAI7B,MAAM,OAAO,KAAK,0BAA0B,QAAQ;IACpD,IAAI,WAAW,MAAM,KAAK,eAAe,QAAQ;IAGjD,IAAI,QAAQ,QAAQ,mBAAmB,IAAI,QAAQ,MAAM,MAAM;KAC9D,KAAK,MAAM,kBAAkB,QAAQ;KACrC,WAAW,MAAM,KAAK,eAAe,QAAQ;IAC9C;IAEA,IAAI,mBAAmB,IAAI,QAAQ,GAClC,MAAM,IAAI,MAAM,yCAAyC,SAAS,EAAE;IAKrE,IAAI,CAAC,KAAK,0BAA0B,MAAM,KAAK,CAAC,CAAC,MAAM,OAAO;IAE9D,IAAI,KAAK,MAAM,qBAAqB,UAAU,UAAU,QAAQ,GAK/D,KAAK,MAAM,QAAQ,MAAM,MAAM,KAAK,GACnC,KAAK,SAAS,WAAW,MAAM,CAAC,UAAU,CAAC;IAM7C,WAAW,KAAK,MAAM,oBAAoB,QAAQ;GACnD,SAAS,MAAM;GAEf,OAAO;EACR,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,sCAAsC,WAAW,EAAE;GAGpE,OAAO;EACR,UAAU;GACT,KAAK,oBAAoB,OAAO,UAAU;EAC3C;CACD;CAEA,MACM,iBAAiB,UAA0C;EAEhE,MAAM,YAAY,MAAM,KAAK,eAC5B,UACA,sHACD;EAGA,MAAM,aAAa,UAAU,IAAI,gBAAgB;EACjD,MAAM,SAAS,UAAU,IAAI,YAAY;EACzC,MAAM,aAAa,UAAU,IAAI,iBAAiB;EAClD,MAAM,aAAa,UAAU,IAAI,aAAa;EAC9C,MAAM,aAAa,UAAU,IAAI,iBAAiB;EAClD,MAAM,qBAAqB,UAAU,IAAI,4BAA4B;EAOrE,OAAO;GACN,SAHiB,gBAAgB,UAAU,KAAK,KAAK,QAAQ,QAAQ,SAAS,YAAY;GAI1F,QAAS,UAA4B;GACzB;GACA;GACA;GACQ;EACrB;CACD;CAEA,MACM,qBAAqB,UAA6C;EAGvE,IAAI,EAAC,MAFgB,KAAK,iBAAiB,QAAQ,EAAA,CAEvC,YACX,OAAO;GAAE,OAAO;GAAO,OAAO;EAA4B;EAK3D,OAAO,EAAE,OAAO,KAAK;CACtB;CAEA,MACM,iBACL,UACA,QACA,SACgB;EAChB,MAAM,QAAQ,gBAAgB;EAE9B,MAAM,UAAqC,CAAC;EAC5C,IAAI,OAAO,WAAW,MACrB,QAAQ,KAAK,CAAC,kBAAkB,OAAO,UAAU,SAAS,OAAO,CAAC;EAEnE,IAAI,OAAO,UAAU,MACpB,QAAQ,KAAK,CAAC,cAAc,OAAO,MAAM,CAAC;EAE3C,IAAI,OAAO,cAAc,MACxB,QAAQ,KAAK,CAAC,mBAAmB,OAAO,UAAU,CAAC;EAEpD,IAAI,OAAO,cAAc,MACxB,QAAQ,KAAK,CAAC,eAAe,OAAO,UAAU,CAAC;EAEhD,IAAI,OAAO,cAAc,MACxB,QAAQ,KAAK,CAAC,mBAAmB,OAAO,UAAU,CAAC;EAEpD,IAAI,OAAO,sBAAsB,MAChC,QAAQ,KAAK,CAAC,8BAA8B,OAAO,kBAAkB,CAAC;EAGvE,IAAI,QAAQ;EACZ,IAAI;GACH,KAAK,MAAM,CAAC,KAAK,UAAU,SAAS;IACnC,MAAM,KAAK,cAAc,UAAU,KAAK,OAAO,OAAO;IACtD,QAAQ;GACT;EACD,SAAS,IAAI;GACZ,OAAO,MAAM,EAAE;GACf,MAAM;EACP,UAAU;GAET,IAAI,SAAS,CAAC,SAAS,QACtB,KAAK,QAAQ,OAAO,YAAY,YAAY,UAAU,CAAC,QAAQ,CAAC;EAElE;CACD;CAEA,sBAAsB,QAAiC;EACtD,MAAM,QAAkB,CAAC;EAEzB,IAAI,OAAO,YACV,MAAM,KAAK,MAAM,eAAe,OAAO,YAAY;EAEpD,IAAI,OAAO,UAAU,OAAO,WAAW,OACtC,MAAM,KAAK,MAAM,cAAc,OAAO,QAAQ;EAE/C,IAAI,OAAO,YACV,MAAM,KAAK,MAAM,mBAAmB,OAAO,YAAY;EAExD,IAAI,OAAO,oBACV,MAAM,KAAK,MAAM,8BAA8B,OAAO,oBAAoB;EAG3E,OAAO;CACR;CAEA,MAAM,kBACL,KAMC;EACD,IAAI;EAEJ,IAAI,KAAK,QAAQ,WAAW,cAAc,OACzC,IAAI;GACH,MAAMA,SAAG,KAAK,UAAU,KAAK,MAAM,CAAC;GACpC,SAAS,MAAM,KAAK,IAAI,IACvB;IAAO;IAAK,QAAQ;IAAS,SAAS,CAAC,MAAM,GAAG;GAAE,GAClD,aACA,aACD;GACA,IAAI,CAAC,OAAO,OAAO,KAAK,GAAG;IAC1B,QAAO,KAAK,4DAA4D,IAAI,EAAE;IAC9E,OAAO;GACR;EACD,QAAQ,CAER;EAGD,IAAI;GACH,SAAS,MAAM,KAAK,IAAI,IACvB;IAAO;IAAK,QAAQ;GAAQ,GAC5B,aACA,mBACA,aACA,oBACA,kCACD;GACA,IAAI,CAAC,OAAO,QAAQ,OAAO;GAK3B,MAAM,CAAC,UAAU,YAAY,kBAAkB,oBADjC,OAAO,OAAO,MAAM,IAAI,CAAC,CAAC,KAAI,MAAK,EAAE,UAAU,CACU;GAEvE,IAAI,CAAC,UAAU,OAAO;GAEtB,MAAM,qBAAqB,cAAc,SAAS,QAAQ,aAAa,EAAE,CAAC;GAE1E,IAAI,SAAS;GACb,IAAI,UAAU,CAAC,WAAW,MAAM,GAC/B,SAAS,UAAU,KAAK,MAAM;GAE/B,SAAS,cAAc,MAAM;GAE7B,IAAI;GACJ,IAAI,kBAAkB;IACrB,eAAe;IACf,IAAI,CAAC,WAAW,YAAY,GAC3B,eAAe,UAAU,KAAK,YAAY;IAE3C,eAAe,cAAc,YAAY;IACzC,IAAI,iBAAiB,QACpB,eAAe,KAAA;GAEjB;GAEA,OAAO;IACN,UAAU;IACF;IACM;IACd,kBAAkB,kBAAkB,KAAK,KAAK,KAAA;GAC/C;EACD,SAAS,IAAS;GACjB,IAAI,cAAc,yBAAyB,OAAO;GAElD,MAAM,cACL,+LAA+L,KAC9L,GAAG,MACJ;GACD,IAAI,eAAe,MAAM;IACxB,QAAO,KACN,4CAA4C,YAAY,MAAM,YAAY,GAAG,UAC5E,YAAY,GACZ,cACF;IACA,OAAO,CAAC,KAAK;GACd;GAEA,MAAM,WAAW,4CAA4C,KAAK,GAAG,MAAM;GAC3E,IAAI,YAAY,KAAK,QAAQ,WAAW,cAAc,OAAO;IAC5D,SAAS,MAAM,KAAK,IAAI,IAAI;KAAO;KAAK,QAAQ;IAAS,GAAG,aAAa,sBAAsB;IAC/F,IAAI,OAAO,OAAO,KAAK,MAAM,QAAQ;KACpC,MAAM,SAAS,MAAM,KAAK,eAAe,GAAG;KAC5C,MAAM,WAAW,QAAQ,cAAc,QAAQ;KAC/C,IAAI,UAAU,QAAQ,OAAO,CAAC,MAAM,QAAQ;IAC7C;GACD;GAEA,IAAI,YAAY,GAAG,SAAS,UAAU;IACrC,IAAI,SAAS,WAAW,QAAQ,MAAM,SAAS,GAAG;IAClD,IAAI,CAAC,QAAQ;KACZ,GAAG;MACF,MAAM,SAAS,QAAQ,GAAG;MAC1B,IAAI,WAAW,OAAO,OAAO,WAAW,GAAG,OAAO;MAElD,MAAM;MACN,SAAS,MAAM,SAAS,GAAG;KAC5B,SAAS,CAAC;KAEV,OAAO,KAAK,kBAAkB,GAAG;IAClC;GACD;GACA,OAAO;EACR;CACD;CAEA,MAAc,eAAe,KAAyE;EACrG,MAAM,SAAS,MAAM,KAAK,IAAI,IAAI;GAAO;GAAK,QAAQ;EAAS,GAAG,aAAa,aAAa,kBAAkB;EAC9G,IAAI,CAAC,OAAO,QAAQ,OAAO,KAAA;EAE3B,IAAI,CAAC,YAAY,oBAAoB,OAAO,OAAO,MAAM,IAAI,CAAC,CAAC,KAAI,MAAK,EAAE,UAAU,CAAC;EAErF,IAAI,CAAC,WAAW,UAAU,GACzB,aAAa,UAAU,KAAK,UAAU;EAEvC,aAAa,cAAc,UAAU;EAErC,IAAI,kBAAkB;GACrB,IAAI,CAAC,WAAW,gBAAgB,GAC/B,mBAAmB,UAAU,KAAK,gBAAgB;GAEnD,mBAAmB,cAAc,gBAAgB;GAEjD,OAAO;IAAE,MAAM;IAAY,YAAY,qBAAqB,aAAa,mBAAmB,KAAA;GAAU;EACvG;EAEA,OAAO,EAAE,MAAM,WAAW;CAC3B;;;;;;CAOA,MAAc,qBACb,gBACA,OACmB;EAEnB,MAAM,YAAY,UAAU,gBAAgB,IAAI;EAChD,IAAI;GACH,MAAMA,SAAG,KAAK,SAAS;EACxB,QAAQ;GACP,OAAO,KAAK,sDAAsD,UAAU,iBAAiB;GAC7F,OAAO;EACR;EAEA,IAAI;GACH,MAAMA,SAAG,MAAM,gBAAgB,EAAE,WAAW,KAAK,CAAC;EACnD,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,qBAAqB,eAAe,YAAY;GACjE,OAAO;EACR;EAEA,OAAO;CACR;CAEA,iCAAyB,IAAI,IAAY;;;;;;;CAQzC,MAEc,6BAA6B,UAAiC;EAC3E,IAAI,KAAK,eAAe,IAAI,QAAQ,GAAG;EAEvC,MAAM,QAAQ,gBAAgB;EAE9B,MAAM,eAAe,MAAM,KAAK,gBAAgB,QAAQ;EACxD,IAAI,CAAC,cAAc;GAClB,KAAK,eAAe,IAAI,QAAQ;GAChC;EACD;EAEA,MAAM,iBAAiB,UAAU,cAAc,IAAI;EAInD,IAAI;GACH,MAAMA,SAAG,KAAK,YAAY;GAC1B,KAAK,eAAe,IAAI,QAAQ;GAChC;EACD,QAAQ,CAER;EAGA,IAAI,CAAE,MAAM,KAAK,qBAAqB,gBAAgB,KAAK,GAAI;EAE/D,IAAI;GACH,MAAMA,SAAG,UAAU,8BAAc,IAAI,WAAW,CAAC;EAClD,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,qBAAqB,aAAa,OAAO;EAC3D;EAGA,IAAI;EACJ,IAAI;GACH,gBAAgB,MAAM,KAAK,mBAAmB,UAAU,sBAAsB,EAAE,eAAe,KAAK,CAAC;EACtG,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,yCAAyC;GAC1D,KAAK,eAAe,IAAI,QAAQ;GAChC;EACD;EAEA,IAAI,CAAC,cAAc,MAAM;GACxB,KAAK,eAAe,IAAI,QAAQ;GAChC;EACD;EAEA,OAAO,KAAK,aAAa,OAAO,cAAc,IAAI,EAAE,qDAAqD;EAGzG,KAAK,MAAM,CAAC,KAAK,UAAU,CAAC,GAAG,aAAa,GAC3C,IAAI;GACH,MAAM,KAAK,gBAAgB,IAAI,cAAc,KAAA,SAC5C,KAAK,cAAc,UAAU,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC,CAChE;EACD,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,0BAA0B,IAAI,eAAe;GAE9D,cAAc,OAAO,GAAG;EACzB;EAID,KAAK,MAAM,CAAC,QAAQ,eACnB,IAAI;GACH,MAAM,KAAK,IAAI,IACd;IAAE,KAAK;IAAU,QAAQ;IAAU,YAAY;GAAK,GACpD,UACA,WACA,eACA,GACD;EACD,SAAS,IAAI;GACZ,OAAO,MAAM,IAAI,kCAAkC,IAAI,kBAAkB;EAC1E;EAID,KAAK,MAAM,YAAY,UAAU,UAAU,UAAU;EAErD,KAAK,eAAe,IAAI,QAAQ;CACjC;AACD;AA1qCE,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,aAAA,IAAA;AAwCrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,mBAAA,IAAA;AAqDrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,kBAAA,IAAA;AAiBrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,qBAAA,IAAA;AAiDrB,WAAA,CAAA,MAAM,CAAA,GAAA,qBAAA,WAAA,aAAA,IAAA;AAuDN,WAAA,CAAA,KAAK,GACL,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,kBAAA,IAAA;AA0DrB,WAAA,CAAA,KAAK,GACL,MAAM;CAAE,OAAO;CAAM,OAAM,MAAK,YAAY;AAAI,CAAC,CAAA,GAAA,qBAAA,WAAA,0BAAA,IAAA;AASjD,WAAA,CAAA,KAAK,GACL,MAAM;CACN,OAAO;CACP,OAAM,MACL,YAAY,EAAE,IAAI,SAAS,IAAI,EAAE,cAAc,EAAE,WAAW,SAAS,IAAI,EAAE,cAAc,EAAE,WAAW,SAAS,IAAI;AACrH,CAAC,CAAA,GAAA,qBAAA,WAAA,aAAA,IAAA;AA8CA,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,eAAA,IAAA;AA+DrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,oBAAA,IAAA;AA0HrB,WAAA,CAAA,MAAM,CAAA,GAAA,qBAAA,WAAA,eAAA,IAAA;AA+FN,WAAA,CAAA,MAAM,CAAA,GAAA,qBAAA,WAAA,+BAAA,IAAA;AAON,WAAA,CAAA,MAAM,CAAA,GAAA,qBAAA,WAAA,+BAAA,IAAA;AA+MN,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,oBAAA,IAAA;AA+BrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,qBAAA,WAAA,wBAAA,IAAA;AAarB,WAAA,CAAA,MAAM,CAAA,GAAA,qBAAA,WAAA,oBAAA,IAAA;AA8ON,WAAA,CAAA,KAAK,GACL,MAAM,CAAA,GAAA,qBAAA,WAAA,gCAAA,IAAA"}
|
|
1
|
+
{"version":3,"file":"config.js","names":["process_env","fs"],"sources":["../../../../git-cli/src/providers/config.ts"],"sourcesContent":["import { promises as fs, statSync } from 'fs';\nimport { hostname, userInfo } from 'os';\nimport { env as process_env } from 'process';\nimport type { Cache, GkConfigInvalidationTarget, GkReconcileOutcome } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { WorkspaceUntrustedError } from '@gitlens/git/errors.js';\nimport type { GitDir } from '@gitlens/git/models/repository.js';\nimport type { SigningConfig, SigningFormat, ValidationResult } from '@gitlens/git/models/signature.js';\nimport type { GitUser } from '@gitlens/git/models/user.js';\nimport type {\n\tDeprecatedGkConfigKeys,\n\tGitConfigKeys,\n\tGitConfigSubProvider,\n\tGitConfigType,\n\tGkConfigKeys,\n} from '@gitlens/git/providers/config.js';\nimport { parseBranchMetadataGkConfigKey } from '@gitlens/git/utils/branch.utils.js';\nimport { CancellationError } from '@gitlens/utils/cancellation.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport { first, some } from '@gitlens/utils/iterable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { dirname, getBestPath, isAbsolute, joinPaths, normalizePath } from '@gitlens/utils/path.js';\nimport { getSettledValue } from '@gitlens/utils/promise.js';\nimport type { CacheController } from '@gitlens/utils/promiseCache.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport { fsExists } from '../exec/exec.js';\nimport type { Git } from '../exec/git.js';\nimport { KeyedSequencer } from '../exec/indexLock.js';\n\nconst mappedAuthorRegex = /(.+)\\s<(.+)>/;\nconst emptyArray: readonly never[] = Object.freeze([]);\n\n/** Catch-all pattern matching every key in `.git/gk/config` for the one-shot bulk read. */\nconst gkConfigAllPattern = '.';\n\n/**\n * Maps from failed bulk reads, which a reconcile must not diff as the file's content, each to the read that produced\n * it. Module-wide, like {@link gkConfigMapReads}: a reconcile can join a read another provider sharing its `Cache`\n * started.\n */\nconst failedGkConfigMaps = new WeakMap<Map<string, string>, GkConfigMapRead>();\n\n/** What the bulk read behind a cached gk config map saw of `.git/gk/config` before it spawned */\ninterface GkConfigMapRead {\n\t/** Set as the read spawns; until then it can only see the file as it is after every change reported so far */\n\tspawned: boolean;\n\tpath?: string;\n\t/** The file's {@link getGkConfigFingerprint} taken right before the read spawned; `undefined` when untrusted */\n\tfingerprint?: string;\n}\n\n/**\n * The read behind each commonPath's cached gk config map, per `Cache`. Recorded as the read starts, which is when\n * the cache creates the entry for it, so it always describes the entry cached now. Kept per `Cache` rather than per\n * provider, since providers sharing a `Cache` serve each other's reads.\n */\nconst gkConfigMapReads = new WeakMap<Cache, Map<string, GkConfigMapRead>>();\n\n/** The wall clock a read's fingerprint is checked against (see {@link isGkConfigStatRacy}), exported for testing */\nexport const gkConfigFingerprintClock = { now: (): number => Date.now() };\n\nconst nsPerMs = 1_000_000n;\nconst nsPerSecond = 1_000_000_000n;\n\n/**\n * Whether a stat of `.git/gk/config` taken at `statTimeMs` came too soon after the file's last change to vouch for\n * the file staying unchanged while it matches — git's \"racily clean\" index entries. A later write in the same tick of\n * the filesystem's clock as that change, of the same size and given the inode the stat saw (freed by the write in\n * between and reused), gets the same fingerprint; once the stat is a tick past the change, any later write carries a\n * later time. Times that are whole seconds come from a filesystem that keeps no finer (ext4 with 128-byte inodes,\n * HFS+, FAT's 2-second mtime, some network mounts), so the stat must be 2s past; otherwise 50ms covers the coarsest\n * tick a finer clock still has (a 10ms jiffy, Windows' 15.6ms timer) and the wall clock lagging it by one. A file\n * changed after `statTimeMs` (a clock ahead of ours) is racy too. Exported for testing.\n */\nexport function isGkConfigStatRacy(mtimeNs: bigint, ctimeNs: bigint, statTimeMs: number): boolean {\n\tconst changedNs = mtimeNs > ctimeNs ? mtimeNs : ctimeNs;\n\tconst marginNs = mtimeNs % nsPerSecond === 0n && ctimeNs % nsPerSecond === 0n ? 2n * nsPerSecond : 50n * nsPerMs;\n\treturn BigInt(Math.floor(statTimeMs)) * nsPerMs - changedNs < marginNs;\n}\n\n/**\n * A stat of `.git/gk/config` that two stats share only if no write landed between them: `git config` writes a lock\n * file and renames it over the original, so every write gives the file a new inode, and its size and times tell\n * apart a later write that lands on a freed inode — unless that write fell in the same clock tick as the change the\n * first stat saw, which `statTimeMs` rules out (see {@link isGkConfigStatRacy}): given the time the stat is taken,\n * a stat that racy is `undefined`, never matching. `'absent'` when there is no file, `undefined` when it can't be\n * stat'd.\n */\nfunction getGkConfigFingerprint(path: string, statTimeMs?: number): string | undefined {\n\ttry {\n\t\tconst stat = statSync(path, { bigint: true, throwIfNoEntry: false });\n\t\tif (stat == null) return 'absent';\n\t\tif (statTimeMs != null && isGkConfigStatRacy(stat.mtimeNs, stat.ctimeNs, statTimeMs)) return undefined;\n\n\t\treturn `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Canonicalizes a git config key for lookup against `--get-regexp` output, which git emits with the\n * section and variable-name lowercased and only the subsection case-preserved. Without this a\n * subsection-less key like `gk.defaultRemote` would miss the lowercased `gk.defaultremote` in the map.\n */\nexport function canonicalizeGitConfigKey(key: string): string {\n\tconst first = key.indexOf('.');\n\tif (first === -1) return key.toLowerCase();\n\n\tconst last = key.lastIndexOf('.');\n\t// `section.name` (no subsection) → fully lowercased; `section.subsection.name` → lowercase\n\t// section + name, preserve the case-sensitive subsection in the middle.\n\tif (first === last) return key.toLowerCase();\n\treturn `${key.slice(0, first).toLowerCase()}${key.slice(first, last)}${key.slice(last).toLowerCase()}`;\n}\n\n/**\n * Parses git config --get-regex output into a Map.\n * The output format is \"key value\" per line, where key and value are space-separated.\n *\n * A line with NO space at all is git's rendering of a bareword/valueless entry (`key`, no `=`) — git's\n * OWN config parser reads that as boolean TRUE (git-config(1): \"a variable defined without `= <value>`\n * is taken as true\"), confirmed against git's `format_config` (builtin/config.c): the key-delimiter is\n * appended unconditionally, then backed out ONLY when the stored value is NULL (the bareword case) —\n * an explicit empty value (`key =`) is a real, non-NULL empty string, so its line KEEPS the trailing\n * space and is already handled by the branch below, byte-distinct from a bareword line.\n *\n * By default a bareword line is skipped (the historical behavior, which reads as \"unset\" — wrong, but\n * safe); pass `includeValueless` to map it to the literal string `'true'` instead, so `parseGitBoolean`\n * and callers checking `.has()` for \"configured\" both read it correctly.\n */\nexport function parseConfigRegexOutput(\n\tdata: string | undefined,\n\toptions?: { includeValueless?: boolean },\n): Map<string, string> {\n\tconst configMap = new Map<string, string>();\n\tif (!data) return configMap;\n\n\tfor (const line of data.split('\\n')) {\n\t\tif (!line) continue;\n\n\t\tconst spaceIndex = line.indexOf(' ');\n\t\tif (spaceIndex === -1) {\n\t\t\tif (options?.includeValueless) {\n\t\t\t\tconfigMap.set(line, 'true');\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconfigMap.set(line.substring(0, spaceIndex), line.substring(spaceIndex + 1));\n\t}\n\n\treturn configMap;\n}\n\n/**\n * Parses `git config --null --get-regexp` output into every value of each key, in the order git printed them. Each\n * entry is its key, a newline and its value, ended by a NUL, so a value may hold newlines or spaces unambiguously.\n * A valueless entry (`key`, no `=`), which git prints with no newline, is skipped, as {@link parseConfigRegexOutput}\n * skips it by default.\n */\nexport function parseConfigRegexNullOutput(data: string | undefined): Map<string, string[]> {\n\tconst configMap = new Map<string, string[]>();\n\tif (!data) return configMap;\n\n\tfor (const entry of data.split('\\0')) {\n\t\tconst newlineIndex = entry.indexOf('\\n');\n\t\tif (newlineIndex === -1) continue;\n\n\t\tconst key = entry.substring(0, newlineIndex);\n\t\tconst value = entry.substring(newlineIndex + 1);\n\t\tconst values = configMap.get(key);\n\t\tif (values != null) {\n\t\t\tvalues.push(value);\n\t\t} else {\n\t\t\tconfigMap.set(key, [value]);\n\t\t}\n\t}\n\n\treturn configMap;\n}\n\nexport function parseGitBoolean(value: string | undefined): boolean {\n\tif (value == null) return false;\n\n\tconst normalized = value.toLowerCase().trim();\n\treturn normalized === 'true' || normalized === 'yes' || normalized === 'on' || normalized === '1';\n}\n\n/** Whether a git config value spells boolean-false (`false`/`no`/`off`/`0`/empty), per git's maybe-bool parsing. */\nexport function isGitBooleanFalse(value: string): boolean {\n\tconst normalized = value.toLowerCase().trim();\n\treturn (\n\t\tnormalized === '' || normalized === 'false' || normalized === 'no' || normalized === 'off' || normalized === '0'\n\t);\n}\n\n/** Splits `git config --null --get-regexp` output into its entries, in order; a key set without a value has none */\nexport function parseConfigListing(listing: string): [key: string, value: string | undefined][] {\n\tconst entries: [key: string, value: string | undefined][] = [];\n\tfor (const entry of listing.split('\\0')) {\n\t\tif (!entry) continue;\n\n\t\tconst separator = entry.indexOf('\\n');\n\t\tentries.push(\n\t\t\tseparator < 0 ? [entry, undefined] : [entry.substring(0, separator), entry.substring(separator + 1)],\n\t\t);\n\t}\n\treturn entries;\n}\n\n/**\n * Whether `listing` shows config that can differ by checkout over time: `extensions.worktreeConfig` (each checkout's\n * `config.worktree`), or a conditional include that is neither `hasconfig:` nor `gitdir:`, since `onbranch:` follows\n * each checkout's HEAD and an unknown condition might follow anything. A `gitdir:` include matching only some\n * checkouts is accepted as shared: it is the common identity setup, and treating it as differing would end sharing for\n * nearly everyone. The cost is a known gap: an edit to a file only those checkouts include goes unseen there, since\n * nothing watches it, until a change the shared listing does see.\n */\nexport function mayConfigDifferByCheckout(listing: string): boolean {\n\tfor (const [key, value] of parseConfigListing(listing)) {\n\t\tif (key === 'extensions.worktreeconfig') {\n\t\t\t// A key set without a value is true\n\t\t\tif (value == null || !isGitBooleanFalse(value)) return true;\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (!key.startsWith('includeif.')) continue;\n\n\t\tconst condition = key.substring('includeif.'.length, key.lastIndexOf('.'));\n\t\tif (\n\t\t\tcondition.startsWith('hasconfig:') ||\n\t\t\tcondition.startsWith('gitdir:') ||\n\t\t\tcondition.startsWith('gitdir/i:')\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nexport class ConfigGitSubProvider implements GitConfigSubProvider {\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t) {\n\t\tthis.cache.setGkConfigReconciler((repoPath, priorSnapshot) => this.reconcileGkConfig(repoPath, priorSnapshot));\n\t}\n\n\t/**\n\t * In-flight gk-config reconciles keyed by commonPath. `pending` records that another `'gkConfig'`\n\t * event landed mid-pass, so one trailing pass runs after the current one settles. `paths` holds every\n\t * worktree path that reported the change, mapped to its close generation at that moment — the pass\n\t * reads through one that's still open and notifies all of them.\n\t */\n\tprivate readonly _gkConfigReconciles = new Map<string, { pending: boolean; paths: Map<string, number> }>();\n\n\t/**\n\t * The latest pending `setGkConfig` write of each `commonPath\\0key`. A call writing the same value with the same\n\t * invalidation and error options joins it; any other call chains after it, so one key's writes land in call order. Cleared on settle.\n\t */\n\tprivate readonly _pendingGkConfigWrites = new Map<\n\t\tstring,\n\t\t{ value: string | undefined; skipKey: string; throwOnError: boolean; promise: Promise<void> }\n\t>();\n\n\t/** Writes each `.git/gk/config` one at a time — concurrent `git config` writes fail on its lock */\n\tprivate readonly _gkConfigWrites = new KeyedSequencer();\n\n\t@trace({ quiet: true })\n\tgetConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfig(global ? undefined : repoPath, key, () =>\n\t\t\tthis.getConfigCore(repoPath, key, options),\n\t\t);\n\t}\n\n\tprivate async getConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst args = ['config', '--get'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\tif (options?.type) {\n\t\t\targs.push(`--type=${options.type}`);\n\t\t}\n\t\targs.push(key);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t/**\n\t * A checkout's raw `core.excludesFile` and `core.ignorecase`, the config its gitignore filter loads. Every checkout\n\t * of a repository shares one read in the main checkout, unless the listing shows config that can differ by checkout\n\t * (see `mayConfigDifferByCheckout`) or the read failed; then a linked checkout reads its own.\n\t */\n\t@trace({ quiet: true })\n\tasync getIgnoreConfig(\n\t\trepoPath: string,\n\t): Promise<{ excludesFile: string | undefined; ignoreCase: string | undefined }> {\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst listing = await this.cache.getIgnoreConfigListing(repoPath, (path, cacheable, signal) =>\n\t\t\tthis.readIgnoreConfigListing(path, cacheable, signal),\n\t\t);\n\t\tif (listing != null && (repoPath === commonPath || !mayConfigDifferByCheckout(listing))) {\n\t\t\tlet excludesFile: string | undefined;\n\t\t\tlet ignoreCase: string | undefined;\n\t\t\t// A key can repeat, and git answers with the last\n\t\t\tfor (const [key, value] of parseConfigListing(listing)) {\n\t\t\t\tif (key === 'core.excludesfile') {\n\t\t\t\t\texcludesFile = value?.trim() || undefined;\n\t\t\t\t} else if (key === 'core.ignorecase') {\n\t\t\t\t\t// A key set without a value is true\n\t\t\t\t\tignoreCase = value == null ? 'true' : value.trim() || undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { excludesFile: excludesFile, ignoreCase: ignoreCase };\n\t\t}\n\n\t\tconst [excludesFileResult, ignoreCaseResult] = await Promise.allSettled([\n\t\t\tthis.getConfig(repoPath, 'core.excludesFile'),\n\t\t\tthis.getConfig(repoPath, 'core.ignorecase'),\n\t\t]);\n\t\treturn { excludesFile: getSettledValue(excludesFileResult), ignoreCase: getSettledValue(ignoreCaseResult) };\n\t}\n\n\tprivate async readIgnoreConfigListing(\n\t\tcwd: string,\n\t\tcacheable: CacheController,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<string | undefined> {\n\t\t// Conditional includes and `extensions.worktreeConfig` tell whether one checkout's listing holds for the others.\n\t\t// Exit 1 is nothing set, a real answer; a failure must not reject, as the filter's per-checkout reads never did\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: cwd, cancellation: cancellation, errors: 'ignore' },\n\t\t\t'config',\n\t\t\t'--null',\n\t\t\t'--get-regexp',\n\t\t\t'^(core\\\\.excludesfile|core\\\\.ignorecase|includeif\\\\..*|extensions\\\\.worktreeconfig)$',\n\t\t);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tif (result.completion.status !== 'exited' || (result.exitCode !== 0 && result.exitCode !== 1)) {\n\t\t\tcacheable.invalidate();\n\t\t\treturn undefined;\n\t\t}\n\t\treturn result.stdout;\n\t}\n\n\t@trace({ quiet: true })\n\tgetConfigRegex(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfigRegex(global ? undefined : repoPath, pattern, () =>\n\t\t\tthis.getConfigRegexCore(repoPath, pattern, options),\n\t\t);\n\t}\n\n\t/**\n\t * Every value of each key matching `pattern` (`git config --get-regexp`), for a key git lets repeat, like\n\t * `remote.<name>.fetch`: {@link getConfigRegex} keeps only each key's last value. Cached beside it, so the same\n\t * writes and config changes clear it. Rejects when git couldn't read the configuration.\n\t */\n\t@trace({ quiet: true })\n\tasync getConfigRegexAll(repoPath: string, pattern: string): Promise<Map<string, string[]>> {\n\t\t// Under a key no regex is spelled as, with each key's values joined by a NUL, which no value can hold\n\t\t// Read from, and cached under, the common path, never the caller's checkout: every worktree is served this one\n\t\t// entry, so it must not take in a `config.worktree` of whichever checkout happened to read first\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst joined = await this.cache.getConfigRegex(commonPath, `\\0all\\0${pattern}`, async () => {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: commonPath, errors: 'ignore' },\n\t\t\t\t'config',\n\t\t\t\t'--null',\n\t\t\t\t'--get-regexp',\n\t\t\t\tpattern,\n\t\t\t);\n\t\t\t// 1 is git's exit code for no key matching\n\t\t\tif (result.completion.status !== 'exited' || (result.exitCode !== 0 && result.exitCode !== 1)) {\n\t\t\t\tthrow new Error(`Unable to read the config keys matching '${pattern}'`);\n\t\t\t}\n\n\t\t\treturn new Map(\n\t\t\t\tArray.from(parseConfigRegexNullOutput(result.stdout), ([key, values]) => [key, values.join('\\0')]),\n\t\t\t);\n\t\t});\n\t\treturn new Map(Array.from(joined, ([key, value]) => [key, value.split('\\0')]));\n\t}\n\n\tprivate async getConfigRegexCore(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst args = ['config', '--get-regex'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\targs.push(pattern);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\t// Deliberately NOT `.trim()`-ed: an explicit-empty value's LAST line ends in a meaningful trailing\n\t\t// space, and `.trim()`-ing the whole (possibly multi-line) buffer strips it — `parseConfigRegexOutput`\n\t\t// already skips the blank line a trailing newline produces, so no trim is needed.\n\t\treturn parseConfigRegexOutput(result.stdout);\n\t}\n\n\t@debug()\n\tasync setConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean },\n\t): Promise<void> {\n\t\tawait this.setConfigCore(repoPath, key, value, options);\n\n\t\t// A branch's upstream (`remote`/`merge`) is read back through the cached branch list, which a\n\t\t// `'config'` reset alone leaves serving the old tracking info.\n\t\tif (repoPath != null && !options?.file && /^branch\\..+\\.(?:remote|merge)$/i.test(key)) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches');\n\t\t}\n\n\t\t// A `--global` or custom-`file` write isn't this repository's `.git/config`\n\t\tif (repoPath != null && !options?.file && !options?.global) {\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config']);\n\t\t}\n\t}\n\n\tprivate async setConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean; throwOnError?: boolean },\n\t): Promise<void> {\n\t\tconst args: string[] = ['config'];\n\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\targs.push(global ? '--global' : '--local');\n\t\t}\n\n\t\tif (value == null) {\n\t\t\targs.push('--unset', key);\n\t\t} else {\n\t\t\targs.push(key, value);\n\t\t}\n\n\t\tawait this.git.run(\n\t\t\t{ cwd: repoPath ?? '', runLocally: true, errors: options?.throwOnError ? 'throw' : undefined },\n\t\t\t...args,\n\t\t);\n\n\t\t// Only invalidate cache when not using a custom file (custom files aren't cached)\n\t\tif (!options?.file) {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\t// Invalidate the cached value for this key and clear all regex patterns for this scope\n\t\t\tthis.cache.deleteConfig(global ? undefined : repoPath, key);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug({ quiet: true })\n\tasync getCurrentUser(repoPath: string): Promise<GitUser | undefined> {\n\t\tif (!repoPath) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst cached = this.cache.currentUser.get(repoPath);\n\t\tif (cached != null) return cached;\n\t\t// If we found null, user data was not found - don't bother trying again\n\t\tif (cached === null) return undefined;\n\n\t\tconst user: GitUser = { name: undefined, email: undefined };\n\n\t\ttry {\n\t\t\tconst configMap = await this.getConfigRegex(repoPath, '^user\\\\.(name|email)$', {\n\t\t\t\trunGitLocally: true,\n\t\t\t});\n\t\t\tif (configMap.size) {\n\t\t\t\tuser.name = configMap.get('user.name');\n\t\t\t\tuser.email = configMap.get('user.email');\n\t\t\t} else {\n\t\t\t\tuser.name =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_NAME || process_env.GIT_COMMITTER_NAME || userInfo()?.username || undefined;\n\t\t\t\tif (!user.name) {\n\t\t\t\t\t// If we found no user data, mark it so we won't bother trying again\n\t\t\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tuser.email =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_EMAIL ||\n\t\t\t\t\tprocess_env.GIT_COMMITTER_EMAIL ||\n\t\t\t\t\tprocess_env.EMAIL ||\n\t\t\t\t\t`${user.name}@${hostname()}`;\n\t\t\t}\n\n\t\t\tconst author = `${user.name} <${user.email}>`;\n\t\t\t// Check if there is a mailmap for the current user\n\t\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'check-mailmap', author);\n\n\t\t\tif (result.stdout && result.stdout !== author) {\n\t\t\t\tconst match = mappedAuthorRegex.exec(result.stdout);\n\t\t\t\tif (match != null) {\n\t\t\t\t\t[, user.name, user.email] = match;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.cache.currentUser.set(repoPath, user);\n\t\t\treturn user;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\n\t\t\t// Mark it so we won't bother trying again\n\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@gate()\n\t@trace({ quiet: true, exit: r => `returned ${r}` })\n\tasync getDefaultWorktreePath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn getBestPath(normalizePath(joinPaths(basePath, '..')));\n\t}\n\n\t@gate()\n\t@trace({\n\t\tquiet: true,\n\t\texit: r =>\n\t\t\t`returned ${r.uri.toString(true)}, commonUri=${r.commonUri?.toString(true)}, parentUri=${r.parentUri?.toString(true)}`,\n\t})\n\tasync getGitDir(repoPath: string): Promise<GitDir> {\n\t\tconst cached = this.cache.gitDir.get(repoPath);\n\t\tif (cached != null) return cached;\n\n\t\tconst scope = getScopedLogger();\n\t\tconst repoInfo = await this.getRepositoryInfo(repoPath);\n\n\t\tlet gitDir: GitDir;\n\t\tif (!Array.isArray(repoInfo) && repoInfo != null) {\n\t\t\tgitDir = {\n\t\t\t\turi: fileUri(repoInfo.gitDir),\n\t\t\t\tcommonUri: repoInfo.commonGitDir ? fileUri(repoInfo.commonGitDir) : undefined,\n\t\t\t\tparentUri: repoInfo.superprojectPath ? fileUri(repoInfo.superprojectPath) : undefined,\n\t\t\t};\n\t\t} else {\n\t\t\tgitDir = {\n\t\t\t\turi: this.provider.getAbsoluteUri(joinPaths(repoPath, '.git'), repoPath),\n\t\t\t};\n\n\t\t\tconst gitDirPath = gitDir.uri.toString(true);\n\t\t\tscope?.warn(`rev-parse failed for '${repoPath}'; falling back to '${gitDirPath}'`);\n\t\t\tthis.context.hooks?.operations?.onGitDirResolveFailed?.(\n\t\t\t\trepoPath,\n\t\t\t\tgitDirPath,\n\t\t\t\t`rev_parse returned ${JSON.stringify(repoInfo)}`,\n\t\t\t);\n\t\t}\n\t\tthis.cache.gitDir.set(repoPath, gitDir);\n\n\t\treturn gitDir;\n\t}\n\n\t/**\n\t * Gets the path to the .git/gk/config file for storing GitKraken-specific metadata.\n\t * Uses commonPath for worktrees so all worktrees share the same data.\n\t */\n\tprivate async getGkConfigPath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\t// Use commonUri (main .git dir) for worktrees, otherwise use uri\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn joinPaths(basePath, 'gk', 'config');\n\t}\n\n\t@trace({ quiet: true })\n\tasync getGkConfig(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\toptions?: { throwOnError?: boolean },\n\t): Promise<string | undefined> {\n\t\tif (options?.throwOnError) return this.getGkConfigStrict(repoPath, key);\n\n\t\t// Served in-memory from a single bulk read of the whole `.git/gk/config` (no gk read needs `--type`).\n\t\t// Canonicalize the key the way git does for `--get-regexp` output so subsection-less keys\n\t\t// (e.g. `gk.defaultRemote`) match, and coerce an empty value to undefined to match the old\n\t\t// `git config --get` (`.trim() || undefined`) contract.\n\t\tconst map = await this.getGkConfigMap(repoPath);\n\t\treturn map.get(canonicalizeGitConfigKey(key)) || undefined;\n\t}\n\n\tprivate async getGkConfigStrict(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t): Promise<string | undefined> {\n\t\tawait this.migrateGkConfigFromGitConfig(this.cache.getCommonPath(repoPath));\n\t\tconst path = await this.getGkConfigPath(repoPath);\n\t\tif (path == null) throw new Error('Unable to locate GitKraken config storage');\n\n\t\ttry {\n\t\t\tawait fs.lstat(path);\n\t\t} catch (ex) {\n\t\t\tif ((ex as NodeJS.ErrnoException).code !== 'ENOENT') throw ex;\n\n\t\t\t// Windows also reports ENOENT for a child beneath a file, so absence needs a directory parent.\n\t\t\tfor (const parent of [dirname(path), dirname(dirname(path))]) {\n\t\t\t\tconst entry = await fs.lstat(parent).catch((parentError: unknown) => {\n\t\t\t\t\tif ((parentError as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n\n\t\t\t\t\tthrow parentError;\n\t\t\t\t});\n\t\t\t\tif (entry == null) continue;\n\n\t\t\t\tif (entry.isDirectory() || (entry.isSymbolicLink() && (await fs.stat(parent)).isDirectory())) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\n\t\t// Listing has no ambiguous \"no matching key\" exit code; storage failures must stop read-modify-write.\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, errors: 'throw', runLocally: true },\n\t\t\t'config',\n\t\t\t'--null',\n\t\t\t'--list',\n\t\t\t'-f',\n\t\t\tpath,\n\t\t);\n\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0) {\n\t\t\tthrow new Error('Unable to read GitKraken config');\n\t\t}\n\n\t\treturn parseConfigRegexNullOutput(result.stdout).get(canonicalizeGitConfigKey(key))?.at(-1) || undefined;\n\t}\n\n\t@trace({ quiet: true })\n\tasync getGkConfigRegex(repoPath: string, pattern: string): Promise<Map<string, string>> {\n\t\tconst all = await this.getGkConfigMap(repoPath);\n\n\t\t// Filter the bulk map in-memory. The old path passed `pattern` to `git config --get-regex`\n\t\t// (errors ignored → empty result on a bad pattern); preserve that no-throw contract here\n\t\t// rather than letting an invalid-JS pattern reject the read.\n\t\tlet re: RegExp;\n\t\ttry {\n\t\t\tre = new RegExp(pattern);\n\t\t} catch {\n\t\t\treturn new Map();\n\t\t}\n\n\t\tconst result = new Map<string, string>();\n\t\tfor (const [k, v] of all) {\n\t\t\tif (re.test(k)) {\n\t\t\t\tresult.set(k, v);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Reads the entire `.git/gk/config` in one `git config --get-regexp` and caches the parsed map\n\t * per commonPath. Per-key (getGkConfig) and per-namespace (getGkConfigRegex) lookups are served\n\t * from this single cached read — so a branch-overview render that fans out across several gk\n\t * namespaces costs one `git config` call instead of one per namespace.\n\t */\n\tprivate async getGkConfigMap(repoPath: string): Promise<Map<string, string>> {\n\t\t// Migrate BEFORE populating the cache, not inside the factory: a first-time migration calls\n\t\t// clearCaches('gkConfig'), which would otherwise evict the in-flight gkConfigMap entry the\n\t\t// cache wrapper synchronously stored and force a redundant bulk re-read. Checked here too, not only in the\n\t\t// migration: every gk lookup lands here, and entering the migration logs it.\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tif (!this._migratedRepos.has(commonPath)) {\n\t\t\tawait this.migrateGkConfigFromGitConfig(commonPath);\n\t\t}\n\t\treturn this.cache.getGkConfigMap(repoPath, async cacheable => {\n\t\t\tconst read = this.trackGkConfigMapRead(repoPath);\n\t\t\tconst { map, failed } = await this.getGkConfigRegexCore(repoPath, gkConfigAllPattern, read);\n\t\t\t// This map has no TTL and everything gk-related reads through it, so a failed read cached as an\n\t\t\t// empty map would report every branch as having no stored base or merge target for the session —\n\t\t\t// and would make the section-cleanup pre-checks skip real work. Only cache a read that happened.\n\t\t\tif (failed) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t\tfailedGkConfigMaps.set(map, read);\n\t\t\t}\n\t\t\treturn map;\n\t\t});\n\t}\n\n\t/** Records the read that is creating `repoPath`'s cached gk config map (see {@link gkConfigMapReads}) */\n\tprivate trackGkConfigMapRead(repoPath: string): GkConfigMapRead {\n\t\tlet reads = gkConfigMapReads.get(this.cache);\n\t\tif (reads == null) {\n\t\t\treads = new Map();\n\t\t\tgkConfigMapReads.set(this.cache, reads);\n\t\t}\n\n\t\tconst read: GkConfigMapRead = { spawned: false };\n\t\treads.set(this.cache.getCommonPath(repoPath), read);\n\t\treturn read;\n\t}\n\n\t/**\n\t * Hard-evicts the cached gk config map, unless `.git/gk/config` is unchanged since the read behind it stat'd the\n\t * file, or that read is yet to spawn. The stat is taken before the read, so a write landing between the two\n\t * only costs a re-read on its report, never a missed change. Returns the read it kept, if any.\n\t */\n\tprivate evictGkConfigMapIfChanged(repoPath: string): GkConfigMapRead | undefined {\n\t\tconst read = gkConfigMapReads.get(this.cache)?.get(this.cache.getCommonPath(repoPath));\n\t\tif (read != null) {\n\t\t\tif (!read.spawned) return read;\n\t\t\tif (\n\t\t\t\tread.path != null &&\n\t\t\t\tread.fingerprint != null &&\n\t\t\t\tgetGkConfigFingerprint(read.path) === read.fingerprint\n\t\t\t) {\n\t\t\t\treturn read;\n\t\t\t}\n\t\t}\n\n\t\tthis.cache.deleteGkConfigMap(repoPath);\n\t\treturn undefined;\n\t}\n\n\tprivate async getGkConfigRegexCore(\n\t\trepoPath: string,\n\t\tpattern: string,\n\t\tread: GkConfigMapRead,\n\t): Promise<{ map: Map<string, string>; failed: boolean }> {\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tread.spawned = true;\n\t\t\t// No `.git/gk/config` yet is a real answer, not a failure — a repo GitLens has never written to.\n\t\t\treturn { map: new Map(), failed: false };\n\t\t}\n\n\t\t// Taken before the spawn, never after: a write landing in between then only makes its report re-read the file.\n\t\t// The clock is read before the stat, so the stat is no earlier than the time it's checked against.\n\t\tread.path = gkConfigPath;\n\t\tread.fingerprint = getGkConfigFingerprint(gkConfigPath, gkConfigFingerprintClock.now());\n\t\tread.spawned = true;\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t'config',\n\t\t\t'--get-regex',\n\t\t\t'-f',\n\t\t\tgkConfigPath,\n\t\t\tpattern,\n\t\t);\n\t\t// `--get-regex` exits 1 when nothing matches, which IS an answer (an empty gk config).\n\t\tconst exited = result.completion.status === 'exited';\n\t\treturn {\n\t\t\t// Deliberately NOT `.trim()`-ed — see `getConfigRegexCore`'s comment: it strips the trailing\n\t\t\t// space that marks an explicit-empty value's LAST line, silently dropping it from the map.\n\t\t\tmap: parseConfigRegexOutput(result.stdout),\n\t\t\tfailed: !exited || (result.exitCode !== 0 && result.exitCode !== 1),\n\t\t};\n\t}\n\n\t@debug()\n\tasync setGkConfig(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[]; throwOnError?: boolean },\n\t): Promise<void> {\n\t\t// A self-write (with skip targets) and a user-driven write of the same value have different invalidation\n\t\t// semantics, so only a matching skip option may join: otherwise the user-driven write would skip invalidation.\n\t\t// Sorted, so the same targets in any order match.\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst skipKey = options?.skipInvalidation?.length ? options.skipInvalidation.toSorted().join(',') : '';\n\t\tconst pendingKey = `${commonPath}\\0${key}`;\n\t\tconst throwOnError = options?.throwOnError === true;\n\n\t\tconst previous = this._pendingGkConfigWrites.get(pendingKey);\n\t\tif (\n\t\t\tprevious != null &&\n\t\t\tprevious.value === value &&\n\t\t\tprevious.skipKey === skipKey &&\n\t\t\tprevious.throwOnError === throwOnError\n\t\t) {\n\t\t\treturn previous.promise;\n\t\t}\n\n\t\tconst promise =\n\t\t\tprevious != null\n\t\t\t\t? previous.promise.catch(() => {}).then(() => this.setGkConfigCore(repoPath, key, value, options))\n\t\t\t\t: this.setGkConfigCore(repoPath, key, value, options);\n\t\tconst pending = { value: value, skipKey: skipKey, throwOnError: throwOnError, promise: promise };\n\t\tthis._pendingGkConfigWrites.set(pendingKey, pending);\n\t\t// Swallow the cleanup chain's rejection so a failed write doesn't surface as an unhandled\n\t\t// rejection separate from the caller's own handling of the returned promise.\n\t\tvoid promise\n\t\t\t.finally(() => {\n\t\t\t\tif (this._pendingGkConfigWrites.get(pendingKey) === pending) {\n\t\t\t\t\tthis._pendingGkConfigWrites.delete(pendingKey);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(() => {});\n\t\treturn promise;\n\t}\n\n\tprivate async setGkConfigCore(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[]; throwOnError?: boolean },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tif (options?.throwOnError) throw new Error('Unable to locate GitKraken config storage');\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) {\n\t\t\tif (options?.throwOnError) throw new Error('Unable to prepare GitKraken config storage');\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Seed the reconcile snapshot from the pre-write file, so the write below can tell whether it changed the\n\t\t// value (first tracked-key write per repository only; the bulk read is cached for everything else)\n\t\tif (this.cache.needsGkConfigSnapshot(repoPath, key)) {\n\t\t\tawait this.getGkConfigMap(repoPath);\n\t\t}\n\n\t\tawait this._gkConfigWrites.run(gkConfigPath, undefined, () =>\n\t\t\tthis.setConfigCore(repoPath, key, value, { file: gkConfigPath, throwOnError: options?.throwOnError }),\n\t\t);\n\n\t\t// Invalidate the bulk map (so the next read re-fetches) and the derived caches for this key's\n\t\t// ref, then record the write, which fires `onDidChangeMergeTargets` when it changed a merge key —\n\t\t// except for a self-write (`skipInvalidation` given: an internal save while answering a read), which\n\t\t// nobody awaits; only another process's watcher reports that one.\n\t\t// The `.git/gk/config` file-watcher also fires `'gkConfig'` shortly after, which reconciles\n\t\t// the same change (see `reconcileGkConfig`) — recording our own write here keeps that later\n\t\t// reconcile from re-diffing this key as an external change.\n\t\tthis.cache.deleteGkConfig(repoPath, key, options);\n\t\tthis.cache.recordGkConfigWrite(repoPath, key, value, { announce: options?.skipInvalidation == null });\n\n\t\t// The watcher's report of this write can't reach the cached branches: the snapshot already holds the value\n\t\tconst metadataKey = parseBranchMetadataGkConfigKey(key);\n\t\tif (metadataKey != null) {\n\t\t\tthis.cache.patchBranchMetadata(repoPath, [\n\t\t\t\t{ branch: metadataKey.branch, key: metadataKey.key, value: value },\n\t\t\t]);\n\t\t}\n\t}\n\n\t@debug()\n\tasync removeGkConfigBranchSection(repoPath: string, ref: string): Promise<void> {\n\t\tif (await this.runGkConfigSectionCommand(repoPath, ref, '--remove-section', `branch.${ref}`)) {\n\t\t\tthis.cache.recordGkConfigSectionWrite(repoPath, ref);\n\t\t}\n\t}\n\n\t@debug()\n\tasync renameGkConfigBranchSection(repoPath: string, oldRef: string, newRef: string): Promise<void> {\n\t\t// Clear the destination first, unconditionally. `--rename-section` onto a section that already\n\t\t// exists APPENDS rather than replaces, leaving duplicate keys — and git resolves a duplicated key\n\t\t// to its LAST value, so the orphan left by an earlier branch of this name would win over the\n\t\t// metadata being moved. Doing this outside `runGkConfigSectionCommand` also covers the case where\n\t\t// the source has no gk keys at all, where its pre-check would otherwise skip the whole call and\n\t\t// leave the orphan in place for the renamed branch to inherit.\n\t\tawait this.removeGkConfigBranchSection(repoPath, newRef);\n\t\tif (\n\t\t\tawait this.runGkConfigSectionCommand(\n\t\t\t\trepoPath,\n\t\t\t\toldRef,\n\t\t\t\t'--rename-section',\n\t\t\t\t`branch.${oldRef}`,\n\t\t\t\t`branch.${newRef}`,\n\t\t\t)\n\t\t) {\n\t\t\tthis.cache.recordGkConfigSectionWrite(repoPath, oldRef, newRef);\n\t\t}\n\t}\n\n\t/**\n\t * Runs a section-level `git config` against `.git/gk/config` for `ref`'s section, skipping the\n\t * subprocess entirely when the bulk map (already cached on any warm path) shows that ref has no gk\n\t * keys — the overwhelmingly common case, since most branches never get gk metadata written at all.\n\t *\n\t * Never throws: callers invoke this as bookkeeping after a branch op has already succeeded, so a\n\t * failure here must not be reported as (or roll back) the branch/checkout/worktree operation itself.\n\t * The worst case is metadata that stays behind until the next op on that name.\n\t *\n\t * Returns whether `.git/gk/config` now reflects the command (it ran, or there was nothing to do), so the\n\t * caller can record its own write in the reconcile snapshot only when it actually landed.\n\t */\n\tprivate async runGkConfigSectionCommand(repoPath: string, ref: string, ...args: string[]): Promise<boolean> {\n\t\tconst scope = getScopedLogger();\n\t\ttry {\n\t\t\tconst prefix = `branch.${ref}.`;\n\t\t\tconst map = await this.getGkConfigMap(repoPath);\n\t\t\t// The shortcut needs a map with CONTENT to be trustworthy. A failed bulk read resolves empty\n\t\t\t// (see `getGkConfigMap`) — the cache refuses it, but this caller still holds it — and an empty map\n\t\t\t// read as authoritative would skip the removal entirely, leaving a deleted branch's base,\n\t\t\t// disposition and issue links behind for the next branch reusing that name. When it's empty, run\n\t\t\t// the command: worst case is a no-op on a section that isn't there.\n\t\t\tif (map.size && !some(map.keys(), k => k.startsWith(prefix))) return true;\n\n\t\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\t\tif (!gkConfigPath) return false;\n\t\t\t// `getGkConfigPath` only joins a path, so check the file: with no `.git/gk/config` there is\n\t\t\t// genuinely nothing to remove, and running the command would fail with a fatal we'd then warn\n\t\t\t// about on every branch delete in a repo that has never had gk metadata.\n\t\t\tif (!(await fsExists(gkConfigPath))) return true;\n\n\t\t\tconst result = await this._gkConfigWrites.run(gkConfigPath, undefined, () =>\n\t\t\t\tthis.git.run(\n\t\t\t\t\t{ cwd: repoPath, runLocally: true, errors: 'ignore' },\n\t\t\t\t\t'config',\n\t\t\t\t\t'-f',\n\t\t\t\t\tgkConfigPath,\n\t\t\t\t\t...args,\n\t\t\t\t),\n\t\t\t);\n\t\t\t// `errors: 'ignore'` is deliberate — see the note above on why this must never throw — but that\n\t\t\t// also means the `catch` below never sees a failed write, so without this the metadata silently\n\t\t\t// stays behind (or, on a rename, stays under the old name) with nothing recorded anywhere.\n\t\t\t// Reported, not thrown: the branch op it follows has already succeeded.\n\t\t\tconst succeeded = result.completion.status === 'exited' && result.exitCode === 0;\n\t\t\tif (!succeeded) {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`Failed to update gk config section for '${ref}' in '${repoPath}': ${\n\t\t\t\t\t\tresult.completion.status === 'exited'\n\t\t\t\t\t\t\t? `git config exited ${result.exitCode}`\n\t\t\t\t\t\t\t: `${result.completion.status} · ${result.completion.error.message}`\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// A whole section moved or vanished, which the per-key `deleteGkConfig` cascade can't express.\n\t\t\t// Drop the bulk map so the next read hits disk; the derived caches for the affected ref are\n\t\t\t// evicted by the caller's own `deleteBaseBranchName` plus the `'branches'` cascade it fires.\n\t\t\tthis.cache.deleteGkConfigMap(repoPath);\n\t\t\treturn succeeded;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to update gk config section for '${ref}' in '${repoPath}'`);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Reconciles a watcher-observed `'gkConfig'` change — registered with the shared `Cache` as its\n\t * gk-config reconciler (see the constructor). Hard-evicts the bulk map as the change is reported, unless the file\n\t * is unchanged since the map was read (see `evictGkConfigMapIfChanged`), then reads it (from disk, or the map\n\t * kept) and diffs it against `priorSnapshot` to cascade\n\t * `baseBranchName`/`branchOverviews` only for refs whose merge-relevant keys actually changed, and to evict the\n\t * branches only when a branch metadata key (another process's activity stamp) changed. The diff runs on a kept\n\t * map too: a read can land between an external write and its report, and already hold the change.\n\t *\n\t * Serialized per commonPath: a reconcile is a read-then-diff against a single shared baseline, so\n\t * two overlapping passes would both diff against the same snapshot and the later change could fall\n\t * out entirely. An event arriving mid-pass instead queues one trailing pass, which re-reads and\n\t * diffs against the baseline the previous pass just established.\n\t *\n\t * `.git/gk/config` lives in the common dir, so the watcher delivers one event per worktree sharing it\n\t * (see `WatchGroup.onCommonEvent`), and core's own write is reported too. Siblings never run concurrently — the\n\t * later ones fold into the in-flight pass — but folding in arms the trailing pass, which re-diffs the map unless\n\t * the file changed again. However many siblings land while a pass is running, they coalesce into that single\n\t * trailing pass; the cost is the price of never dropping a real change.\n\t * Every reporting path is retained because `Repository` matches the follow-up change event on exact\n\t * path, and a sibling worktree that never receives one would sit on stale derived values.\n\t */\n\tprivate reconcileGkConfig(\n\t\trepoPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t): GkReconcileOutcome | Promise<GkReconcileOutcome> {\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\n\t\tconst inflight = this._gkConfigReconciles.get(commonPath);\n\t\tif (inflight != null) {\n\t\t\t// Now, not in the trailing pass: a read between this report and that pass must see the change, and would\n\t\t\t// otherwise join the map read before it\n\t\t\tthis.evictGkConfigMapIfChanged(repoPath);\n\t\t\tinflight.paths.set(repoPath, this.cache.getCloseGeneration(repoPath));\n\t\t\tinflight.pending = true;\n\t\t\t// Handed to the in-flight pass's trailing loop, so it IS being reconciled — just not by this\n\t\t\t// call. Reporting a failure here would coarse-clear on every write in a burst.\n\t\t\treturn 'deferred';\n\t\t}\n\n\t\tconst entry = { pending: false, paths: new Map([[repoPath, this.cache.getCloseGeneration(repoPath)]]) };\n\t\tthis._gkConfigReconciles.set(commonPath, entry);\n\t\treturn this.runGkConfigReconcile(commonPath, priorSnapshot, entry);\n\t}\n\n\t/** Drops paths that closed since they reported the change; returns whichever remain. */\n\tprivate pruneClosedReconcilePaths(paths: Map<string, number>): Map<string, number> {\n\t\tfor (const [path, generation] of paths) {\n\t\t\tif (this.cache.getCloseGeneration(path) !== generation) {\n\t\t\t\tpaths.delete(path);\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\tprivate async runGkConfigReconcile(\n\t\tcommonPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t\tentry: { pending: boolean; paths: Map<string, number> },\n\t): Promise<GkReconcileOutcome> {\n\t\tconst scope = getScopedLogger();\n\t\tlet snapshot = priorSnapshot;\n\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tentry.pending = false;\n\n\t\t\t\t// Read through a path that's still open — one worktree closing must not drop the event for\n\t\t\t\t// its still-open siblings, which is why this reads from the surviving set rather than from\n\t\t\t\t// whichever path happened to report first.\n\t\t\t\tconst readPath = first(this.pruneClosedReconcilePaths(entry.paths).keys());\n\t\t\t\t// Every path that reported the change has since closed — nothing left to reconcile, and\n\t\t\t\t// nothing left holding a stale derived value either.\n\t\t\t\tif (readPath == null) return 'reconciled';\n\n\t\t\t\t// Runs before this pass's first await, so a first pass decides as the change is reported; a trailing one\n\t\t\t\t// re-checks the map the pass before it read\n\t\t\t\tconst kept = this.evictGkConfigMapIfChanged(readPath);\n\t\t\t\tlet freshMap = await this.getGkConfigMap(readPath);\n\t\t\t\t// A kept read that failed was started before this pass, which would otherwise have read the file\n\t\t\t\t// itself, so it gets one fresh read before the coarse fallback\n\t\t\t\tif (kept != null && failedGkConfigMaps.get(freshMap) === kept) {\n\t\t\t\t\tthis.cache.deleteGkConfigMap(readPath);\n\t\t\t\t\tfreshMap = await this.getGkConfigMap(readPath);\n\t\t\t\t}\n\t\t\t\t// A failed read diffed as the file's content would remove every key it missed\n\t\t\t\tif (failedGkConfigMaps.has(freshMap)) {\n\t\t\t\t\tthrow new Error(`Failed to read the gk config through '${readPath}'`);\n\t\t\t\t}\n\n\t\t\t\t// Paths may have closed while the re-read was in flight; if every one did, there's nothing\n\t\t\t\t// left to reconcile against.\n\t\t\t\tif (!this.pruneClosedReconcilePaths(entry.paths).size) return 'reconciled';\n\n\t\t\t\tif (this.cache.reconcileGkConfigMap(readPath, snapshot, freshMap)) {\n\t\t\t\t\t// Fire a follow-up change: a consumer that re-read on the original event may have done so\n\t\t\t\t\t// before this async cascade landed, so it would otherwise hold a stale derived value with\n\t\t\t\t\t// nothing left to prompt another re-read. Only the host hears it: it is not a write of\n\t\t\t\t\t// core's, and announcing it as one would evict the map just reconciled.\n\t\t\t\t\tfor (const path of entry.paths.keys()) {\n\t\t\t\t\t\tthis.provider.notifyHost(path, ['gkConfig']);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// `reconcileGkConfigMap` advanced the stored baseline to what we just read; a trailing pass\n\t\t\t\t// diffs its own read against that.\n\t\t\t\tsnapshot = this.cache.getGkConfigSnapshot(readPath);\n\t\t\t} while (entry.pending);\n\n\t\t\treturn 'reconciled';\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to reconcile gk config for '${commonPath}'`);\n\t\t\t// The caller falls back to the coarse clear — derived caches are otherwise left holding\n\t\t\t// pre-change values with nothing to correct them.\n\t\t\treturn 'failed';\n\t\t} finally {\n\t\t\tthis._gkConfigReconciles.delete(commonPath);\n\t\t}\n\t}\n\n\t@debug({ quiet: true })\n\tasync getSigningConfig(repoPath: string): Promise<SigningConfig> {\n\t\t// Fetch all signing-related config in one call\n\t\tconst configMap = await this.getConfigRegex(\n\t\t\trepoPath,\n\t\t\t'^(commit\\\\.gpgsign|gpg\\\\.format|user\\\\.signingkey|gpg\\\\.program|gpg\\\\.ssh\\\\.program|gpg\\\\.ssh\\\\.allowedsignersfile)$',\n\t\t);\n\n\t\t// Extract values (keys are lowercase in git output)\n\t\tconst enabledRaw = configMap.get('commit.gpgsign');\n\t\tconst format = configMap.get('gpg.format');\n\t\tconst signingKey = configMap.get('user.signingkey');\n\t\tconst gpgProgram = configMap.get('gpg.program');\n\t\tconst sshProgram = configMap.get('gpg.ssh.program');\n\t\tconst allowedSignersFile = configMap.get('gpg.ssh.allowedsignersfile');\n\n\t\t// Check if git config has commit signing enabled, falling back to the host-level override\n\t\t// (e.g. VS Code's `git.enableCommitSigning`) when `commit.gpgsign` is unset/false. The host\n\t\t// override can only enable signing, never force it off.\n\t\tconst isEnabled = parseGitBoolean(enabledRaw) || this.context.config?.signing?.enabled === true;\n\n\t\treturn {\n\t\t\tenabled: isEnabled,\n\t\t\tformat: (format as SigningFormat) ?? 'gpg',\n\t\t\tsigningKey: signingKey,\n\t\t\tgpgProgram: gpgProgram,\n\t\t\tsshProgram: sshProgram,\n\t\t\tallowedSignersFile: allowedSignersFile,\n\t\t};\n\t}\n\n\t@debug({ quiet: true })\n\tasync validateSigningSetup(repoPath: string): Promise<ValidationResult> {\n\t\tconst config = await this.getSigningConfig(repoPath);\n\n\t\tif (!config.signingKey) {\n\t\t\treturn { valid: false, error: 'No signing key configured' };\n\t\t}\n\n\t\t// Basic validation: just check that a signing key is configured\n\t\t// Git will handle the actual validation when signing commits\n\t\treturn { valid: true };\n\t}\n\n\t@debug()\n\tasync setSigningConfig(\n\t\trepoPath: string,\n\t\tconfig: Partial<SigningConfig>,\n\t\toptions?: { global?: boolean },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst entries: [GitConfigKeys, string][] = [];\n\t\tif (config.enabled != null) {\n\t\t\tentries.push(['commit.gpgsign', config.enabled ? 'true' : 'false']);\n\t\t}\n\t\tif (config.format != null) {\n\t\t\tentries.push(['gpg.format', config.format]);\n\t\t}\n\t\tif (config.signingKey != null) {\n\t\t\tentries.push(['user.signingkey', config.signingKey]);\n\t\t}\n\t\tif (config.gpgProgram != null) {\n\t\t\tentries.push(['gpg.program', config.gpgProgram]);\n\t\t}\n\t\tif (config.sshProgram != null) {\n\t\t\tentries.push(['gpg.ssh.program', config.sshProgram]);\n\t\t}\n\t\tif (config.allowedSignersFile != null) {\n\t\t\tentries.push(['gpg.ssh.allowedSignersFile', config.allowedSignersFile]);\n\t\t}\n\n\t\tlet wrote = false;\n\t\ttry {\n\t\t\tfor (const [key, value] of entries) {\n\t\t\t\tawait this.setConfigCore(repoPath, key, value, options);\n\t\t\t\twrote = true;\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t} finally {\n\t\t\t// One `config` announcement for the keys written, rather than one (each fanned out to every worktree) per key\n\t\t\tif (wrote && !options?.global) {\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config']);\n\t\t\t}\n\t\t}\n\t}\n\n\tgetSigningConfigFlags(config: SigningConfig): string[] {\n\t\tconst flags: string[] = [];\n\n\t\tif (config.gpgProgram) {\n\t\t\tflags.push('-c', `gpg.program=${config.gpgProgram}`);\n\t\t}\n\t\tif (config.format && config.format !== 'gpg') {\n\t\t\tflags.push('-c', `gpg.format=${config.format}`);\n\t\t}\n\t\tif (config.sshProgram) {\n\t\t\tflags.push('-c', `gpg.ssh.program=${config.sshProgram}`);\n\t\t}\n\t\tif (config.allowedSignersFile) {\n\t\t\tflags.push('-c', `gpg.ssh.allowedSignersFile=${config.allowedSignersFile}`);\n\t\t}\n\n\t\treturn flags;\n\t}\n\n\tasync getRepositoryInfo(\n\t\tcwd: string,\n\t): Promise<\n\t\t| { repoPath: string; gitDir: string; commonGitDir: string | undefined; superprojectPath: string | undefined }\n\t\t| [safe: true, repoPath: string]\n\t\t| [safe: false]\n\t\t| []\n\t> {\n\t\tlet result;\n\n\t\tif (this.context.workspace?.isTrusted === false) {\n\t\t\ttry {\n\t\t\t\tawait fs.stat(joinPaths(cwd, 'HEAD'));\n\t\t\t\tresult = await this.git.run(\n\t\t\t\t\t{ cwd: cwd, errors: 'throw', configs: ['-C', cwd] },\n\t\t\t\t\t'rev-parse',\n\t\t\t\t\t'--show-cdup',\n\t\t\t\t);\n\t\t\t\tif (!result.stdout.trim()) {\n\t\t\t\t\tLogger.warn(`Skipping (untrusted); bare clone repository detected in '${cwd}'`);\n\t\t\t\t\treturn emptyArray as [];\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throws, we should be good to open the repo\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tresult = await this.git.run(\n\t\t\t\t{ cwd: cwd, errors: 'throw' },\n\t\t\t\t'rev-parse',\n\t\t\t\t'--show-toplevel',\n\t\t\t\t'--git-dir',\n\t\t\t\t'--git-common-dir',\n\t\t\t\t'--show-superproject-working-tree',\n\t\t\t);\n\t\t\tif (!result.stdout) return emptyArray as [];\n\n\t\t\t// Output is 3-4 lines: show-toplevel, git-dir, git-common-dir, [show-superproject-working-tree]\n\t\t\t// The 4th line is only present for submodules\n\t\t\tconst lines = result.stdout.split('\\n').map(r => r.trimStart());\n\t\t\tconst [repoPath, dotGitPath, commonDotGitPath, superprojectPath] = lines;\n\n\t\t\tif (!repoPath) return emptyArray as [];\n\n\t\t\tconst normalizedRepoPath = normalizePath(repoPath.replace(/[\\r|\\n]+$/, ''));\n\n\t\t\tlet gitDir = dotGitPath;\n\t\t\tif (gitDir && !isAbsolute(gitDir)) {\n\t\t\t\tgitDir = joinPaths(cwd, gitDir);\n\t\t\t}\n\t\t\tgitDir = normalizePath(gitDir);\n\n\t\t\tlet commonGitDir: string | undefined;\n\t\t\tif (commonDotGitPath) {\n\t\t\t\tcommonGitDir = commonDotGitPath;\n\t\t\t\tif (!isAbsolute(commonGitDir)) {\n\t\t\t\t\tcommonGitDir = joinPaths(cwd, commonGitDir);\n\t\t\t\t}\n\t\t\t\tcommonGitDir = normalizePath(commonGitDir);\n\t\t\t\tif (commonGitDir === gitDir) {\n\t\t\t\t\tcommonGitDir = undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\trepoPath: normalizedRepoPath,\n\t\t\t\tgitDir: gitDir,\n\t\t\t\tcommonGitDir: commonGitDir,\n\t\t\t\tsuperprojectPath: superprojectPath?.trim() || undefined,\n\t\t\t};\n\t\t} catch (ex: any) {\n\t\t\tif (ex instanceof WorkspaceUntrustedError) return emptyArray as [];\n\n\t\t\tconst unsafeMatch =\n\t\t\t\t/(?:^fatal:\\s*detected dubious ownership in repository at '([^']+)'|unsafe repository \\('([^']+)' is owned by someone else\\))[\\s\\S]*(git config --global --add safe\\.directory [^\\n\\u2022]+)/m.exec(\n\t\t\t\t\tex.stderr,\n\t\t\t\t);\n\t\t\tif (unsafeMatch != null) {\n\t\t\t\tLogger.warn(\n\t\t\t\t\t`Skipping; unsafe repository detected in '${unsafeMatch[1] || unsafeMatch[2]}'; run '${\n\t\t\t\t\t\tunsafeMatch[3]\n\t\t\t\t\t}' to allow it`,\n\t\t\t\t);\n\t\t\t\treturn [false];\n\t\t\t}\n\n\t\t\tconst inDotGit = /this operation must be run in a work tree/.test(ex.stderr);\n\t\t\tif (inDotGit && this.context.workspace?.isTrusted !== false) {\n\t\t\t\tresult = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--is-bare-repository');\n\t\t\t\tif (result.stdout.trim() === 'true') {\n\t\t\t\t\tconst result = await this.revParseGitDir(cwd);\n\t\t\t\t\tconst repoPath = result?.commonPath ?? result?.path;\n\t\t\t\t\tif (repoPath?.length) {\n\t\t\t\t\t\tconst workTree = await this.git.rev_parse__work_tree_of_git_dir(repoPath);\n\t\t\t\t\t\treturn workTree ? this.getRepositoryInfo(workTree) : [true, repoPath];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (inDotGit || ex.code === 'ENOENT') {\n\t\t\t\tlet exists = inDotGit ? false : await fsExists(cwd);\n\t\t\t\tif (!exists) {\n\t\t\t\t\tdo {\n\t\t\t\t\t\tconst parent = dirname(cwd);\n\t\t\t\t\t\tif (parent === cwd || parent.length === 0) return emptyArray as [];\n\n\t\t\t\t\t\tcwd = parent;\n\t\t\t\t\t\texists = await fsExists(cwd);\n\t\t\t\t\t} while (!exists);\n\n\t\t\t\t\treturn this.getRepositoryInfo(cwd);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn emptyArray as [];\n\t\t}\n\t}\n\n\tprivate async revParseGitDir(cwd: string): Promise<{ path: string; commonPath?: string } | undefined> {\n\t\tconst result = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--git-dir', '--git-common-dir');\n\t\tif (!result.stdout) return undefined;\n\n\t\tlet [dotGitPath, commonDotGitPath] = result.stdout.split('\\n').map(r => r.trimStart());\n\n\t\tif (!isAbsolute(dotGitPath)) {\n\t\t\tdotGitPath = joinPaths(cwd, dotGitPath);\n\t\t}\n\t\tdotGitPath = normalizePath(dotGitPath);\n\n\t\tif (commonDotGitPath) {\n\t\t\tif (!isAbsolute(commonDotGitPath)) {\n\t\t\t\tcommonDotGitPath = joinPaths(cwd, commonDotGitPath);\n\t\t\t}\n\t\t\tcommonDotGitPath = normalizePath(commonDotGitPath);\n\n\t\t\treturn { path: dotGitPath, commonPath: commonDotGitPath !== dotGitPath ? commonDotGitPath : undefined };\n\t\t}\n\n\t\treturn { path: dotGitPath };\n\t}\n\n\t/**\n\t * Ensures the `.git/gk/` folder exists, but only if the parent `.git` directory already exists.\n\t * This prevents creating stray `.git` directories if the computed path is wrong.\n\t * Returns `true` if the folder was ensured, `false` if the parent `.git` does not exist.\n\t */\n\tprivate async ensureGkConfigFolder(\n\t\tgkConfigFolder: string,\n\t\tscope: ReturnType<typeof getScopedLogger>,\n\t): Promise<boolean> {\n\t\t// Verify the .git directory already exists — never create it from scratch\n\t\tconst dotGitDir = joinPaths(gkConfigFolder, '..');\n\t\ttry {\n\t\t\tawait fs.stat(dotGitDir);\n\t\t} catch {\n\t\t\tscope?.warn(`Skipping GK config write — expected git directory '${dotGitDir}' does not exist`);\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.mkdir(gkConfigFolder, { recursive: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigFolder}' directory`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate _migratedRepos = new Set<string>();\n\n\t/**\n\t * One-time migration of GK config entries from regular git config to `.git/gk/config`.\n\t * If `.git/gk/config` already exists, assumes migration is complete (or data is already there).\n\t * Preserves existing `.git/gk/config` values as source of truth (won't overwrite).\n\t * Removes migrated keys from regular git config to stop cluttering it.\n\t */\n\t@gate()\n\t@debug()\n\tprivate async migrateGkConfigFromGitConfig(repoPath: string): Promise<void> {\n\t\tif (this._migratedRepos.has(repoPath)) return;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\t// If .git/gk/config already exists, consider migration done — it was either\n\t\t// already migrated or values were written there directly via setGkConfig\n\t\ttry {\n\t\t\tawait fs.stat(gkConfigPath);\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// file doesn't exist, proceed with migration\n\t\t}\n\n\t\t// If .git/gk/config doesn't exist, create an empty file to prevent multiple migration attempts in future sessions\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;\n\n\t\ttry {\n\t\t\tawait fs.writeFile(gkConfigPath, new Uint8Array());\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigPath}' file`);\n\t\t}\n\n\t\t// Read legacy gk-* keys from regular git config\n\t\tlet migrateConfig: Map<string, string>;\n\t\ttry {\n\t\t\tmigrateConfig = await this.getConfigRegexCore(repoPath, '^branch\\\\..*\\\\.gk-', { runGitLocally: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, 'Failed to read legacy GK config entries');\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!migrateConfig.size) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tscope?.info(`Migrating ${String(migrateConfig.size)} GK config entries from git config to .git/gk/config`);\n\n\t\t// Copy legacy entries to .git/gk/config\n\t\tfor (const [key, value] of [...migrateConfig]) {\n\t\t\ttry {\n\t\t\t\tawait this._gkConfigWrites.run(gkConfigPath, undefined, () =>\n\t\t\t\t\tthis.setConfigCore(repoPath, key, value, { file: gkConfigPath }),\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to migrate key '${key}' to GK config`);\n\t\t\t\t// If we failed to migrate, delete it from the list so we won't try to remove it from git config later\n\t\t\t\tmigrateConfig.delete(key);\n\t\t\t}\n\t\t}\n\n\t\t// Remove legacy keys from regular git config (use --unset-all defensively)\n\t\tfor (const [key] of migrateConfig) {\n\t\t\ttry {\n\t\t\t\tawait this.git.run(\n\t\t\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t\t\t'config',\n\t\t\t\t\t'--local',\n\t\t\t\t\t'--unset-all',\n\t\t\t\t\tkey,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to remove migrated key '${key}' from git config`);\n\t\t\t}\n\t\t}\n\n\t\t// Clear caches since we modified both config files\n\t\tthis.cache.clearCaches(repoPath, 'config', 'gkConfig');\n\n\t\tthis._migratedRepos.add(repoPath);\n\t}\n}\n"],"mappings":"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"}
|
package/dist/plus/ai/errors.d.ts
CHANGED
|
@@ -36,7 +36,10 @@ export declare function classifyNetworkError(ex: unknown): AIErrorReason.NoNetwo
|
|
|
36
36
|
*
|
|
37
37
|
* `RequestTooLarge` and `NoRequestData` are therefore deliberately excluded — they're properties of
|
|
38
38
|
* the one request that failed.
|
|
39
|
+
*
|
|
40
|
+
* An `AuthenticationRequiredError` (expired GitKraken session) counts too: it isn't an `AIError`, but
|
|
41
|
+
* every remaining request will fail identically until the user signs in again.
|
|
39
42
|
*/
|
|
40
|
-
export declare function isAIUnavailableError(ex: unknown): ex is AIError;
|
|
43
|
+
export declare function isAIUnavailableError(ex: unknown): ex is AIError | AuthenticationRequiredError;
|
|
41
44
|
//#endregion
|
|
42
45
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/plus/ai/errors.js
CHANGED
|
@@ -139,8 +139,12 @@ function classifyNetworkError(ex) {
|
|
|
139
139
|
*
|
|
140
140
|
* `RequestTooLarge` and `NoRequestData` are therefore deliberately excluded — they're properties of
|
|
141
141
|
* the one request that failed.
|
|
142
|
+
*
|
|
143
|
+
* An `AuthenticationRequiredError` (expired GitKraken session) counts too: it isn't an `AIError`, but
|
|
144
|
+
* every remaining request will fail identically until the user signs in again.
|
|
142
145
|
*/
|
|
143
146
|
function isAIUnavailableError(ex) {
|
|
147
|
+
if (ex instanceof AuthenticationRequiredError) return true;
|
|
144
148
|
if (!(ex instanceof AIError)) return false;
|
|
145
149
|
switch (ex.reason) {
|
|
146
150
|
case 0:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../../../plus/ai/src/errors.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\n\nexport const enum AIErrorReason {\n\tDeniedByOrganization,\n\tDeniedByUser,\n\tNoEntitlement,\n\tNoRequestData,\n\tRateLimitExceeded,\n\tRateLimitOrFundsExceeded,\n\tRequestTooLarge,\n\tModelNotSupported,\n\tServiceCapacityExceeded,\n\tUnauthorized,\n\tUserQuotaExceeded,\n\tNoNetwork,\n\tUnreachable,\n}\n\nexport class AIError extends Error {\n\treadonly diagnosticMessage: string;\n\treadonly original?: Error;\n\treadonly reason: AIErrorReason | undefined;\n\n\tconstructor(reason: AIErrorReason, original?: Error) {\n\t\tlet message: string;\n\t\tlet diagnosticMessage: string;\n\t\tswitch (reason) {\n\t\t\tcase AIErrorReason.NoEntitlement:\n\t\t\t\tdiagnosticMessage = 'You do not have the required entitlement to use this feature';\n\t\t\t\tmessage = l10n.t('You do not have the required entitlement to use this feature');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.RequestTooLarge:\n\t\t\t\tdiagnosticMessage = 'The request is too large';\n\t\t\t\tmessage = l10n.t('The request is too large');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.UserQuotaExceeded:\n\t\t\t\tdiagnosticMessage = 'You have exceeded your user token limit';\n\t\t\t\tmessage = l10n.t('You have exceeded your user token limit');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.RateLimitExceeded:\n\t\t\t\tdiagnosticMessage = 'Rate limit exceeded';\n\t\t\t\tmessage = l10n.t('Rate limit exceeded');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.RateLimitOrFundsExceeded:\n\t\t\t\tdiagnosticMessage = 'Rate limit exceeded or your account is out of funds';\n\t\t\t\tmessage = l10n.t('Rate limit exceeded or your account is out of funds');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.ServiceCapacityExceeded:\n\t\t\t\tdiagnosticMessage = 'Service capacity exceeded';\n\t\t\t\tmessage = l10n.t('Service capacity exceeded');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.NoNetwork:\n\t\t\t\tdiagnosticMessage = 'Unable to reach the AI service. Please check your internet connection.';\n\t\t\t\tmessage = l10n.t('Unable to reach the AI service. Please check your internet connection.');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.Unreachable:\n\t\t\t\tdiagnosticMessage = 'The AI service is temporarily unreachable.';\n\t\t\t\tmessage = l10n.t('The AI service is temporarily unreachable.');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.NoRequestData:\n\t\t\t\tdiagnosticMessage = original?.message ?? 'No data was provided for the request';\n\t\t\t\tmessage = original?.message ?? l10n.t('No data was provided for the request');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.ModelNotSupported:\n\t\t\t\tdiagnosticMessage = 'Model not supported for this request';\n\t\t\t\tmessage = l10n.t('Model not supported for this request');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.Unauthorized:\n\t\t\t\tdiagnosticMessage = 'You are not authorized to use the specified provider or model';\n\t\t\t\tmessage = l10n.t('You are not authorized to use the specified provider or model');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.DeniedByOrganization:\n\t\t\t\tdiagnosticMessage = 'Your organization has denied access to the specified provider or model';\n\t\t\t\tmessage = l10n.t('Your organization has denied access to the specified provider or model');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.DeniedByUser:\n\t\t\t\tdiagnosticMessage = 'You have denied access to the specified provider or model';\n\t\t\t\tmessage = l10n.t('You have denied access to the specified provider or model');\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tdiagnosticMessage = original?.message ?? 'An unknown error occurred';\n\t\t\t\tmessage = original?.message ?? l10n.t('An unknown error occurred');\n\t\t\t\tbreak;\n\t\t}\n\n\t\tsuper(message);\n\n\t\tthis.diagnosticMessage = diagnosticMessage;\n\t\tthis.original = original;\n\t\tthis.reason = reason;\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n\n\tget diagnosticString(): string {\n\t\treturn `${this.name}: ${this.diagnosticMessage}`;\n\t}\n}\n\nexport class AuthenticationRequiredError extends Error {\n\tconstructor() {\n\t\tsuper(l10n.t('Authentication required'));\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\nexport class AINoRequestDataError extends AIError {\n\tconstructor(message?: string) {\n\t\tsuper(AIErrorReason.NoRequestData, message ? new Error(message) : undefined);\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\nconst noNetworkErrorCodes = new Set([\n\t'ENOTFOUND',\n\t'ECONNREFUSED',\n\t'EAI_AGAIN',\n\t'EHOSTUNREACH',\n\t'ENETUNREACH',\n\t'ENETDOWN',\n\t'UND_ERR_CONNECT_TIMEOUT',\n]);\n\nconst unreachableErrorCodes = new Set(['ECONNRESET', 'ETIMEDOUT', 'UND_ERR_SOCKET']);\n\nexport function classifyNetworkError(ex: unknown): AIErrorReason.NoNetwork | AIErrorReason.Unreachable | undefined {\n\tlet current: unknown = ex;\n\tlet sawFetchFailed = false;\n\tfor (let depth = 0; depth < 5 && current != null; depth++) {\n\t\tif (!(current instanceof Error)) break;\n\n\t\tif (current.name === 'TypeError' && current.message === 'fetch failed') {\n\t\t\tsawFetchFailed = true;\n\t\t}\n\t\tconst code = (current as { code?: unknown }).code;\n\t\tif (typeof code === 'string') {\n\t\t\tif (noNetworkErrorCodes.has(code)) return AIErrorReason.NoNetwork;\n\t\t\tif (unreachableErrorCodes.has(code)) return AIErrorReason.Unreachable;\n\t\t}\n\n\t\tcurrent = (current as { cause?: unknown }).cause;\n\t}\n\treturn sawFetchFailed ? AIErrorReason.NoNetwork : undefined;\n}\n\n/**\n * Whether an error means AI itself is unavailable, rather than this particular request being\n * unacceptable. The distinction matters to any caller running a loop: an unavailable-AI failure will\n * repeat identically for every remaining item, so the loop should stop and say so, while a\n * request-shaped failure (too large, no data) may well succeed on the next item.\n *\n * `RequestTooLarge` and `NoRequestData` are therefore deliberately excluded — they're properties of\n * the one request that failed.\n */\nexport function isAIUnavailableError(ex: unknown): ex is AIError {\n\tif (!(ex instanceof AIError)) return false;\n\n\tswitch (ex.reason) {\n\t\tcase AIErrorReason.DeniedByOrganization:\n\t\tcase AIErrorReason.DeniedByUser:\n\t\tcase AIErrorReason.NoEntitlement:\n\t\tcase AIErrorReason.RateLimitExceeded:\n\t\tcase AIErrorReason.RateLimitOrFundsExceeded:\n\t\tcase AIErrorReason.ServiceCapacityExceeded:\n\t\tcase AIErrorReason.Unauthorized:\n\t\tcase AIErrorReason.UserQuotaExceeded:\n\t\tcase AIErrorReason.NoNetwork:\n\t\tcase AIErrorReason.Unreachable:\n\t\tcase AIErrorReason.ModelNotSupported:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../../../plus/ai/src/errors.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\n\nexport const enum AIErrorReason {\n\tDeniedByOrganization,\n\tDeniedByUser,\n\tNoEntitlement,\n\tNoRequestData,\n\tRateLimitExceeded,\n\tRateLimitOrFundsExceeded,\n\tRequestTooLarge,\n\tModelNotSupported,\n\tServiceCapacityExceeded,\n\tUnauthorized,\n\tUserQuotaExceeded,\n\tNoNetwork,\n\tUnreachable,\n}\n\nexport class AIError extends Error {\n\treadonly diagnosticMessage: string;\n\treadonly original?: Error;\n\treadonly reason: AIErrorReason | undefined;\n\n\tconstructor(reason: AIErrorReason, original?: Error) {\n\t\tlet message: string;\n\t\tlet diagnosticMessage: string;\n\t\tswitch (reason) {\n\t\t\tcase AIErrorReason.NoEntitlement:\n\t\t\t\tdiagnosticMessage = 'You do not have the required entitlement to use this feature';\n\t\t\t\tmessage = l10n.t('You do not have the required entitlement to use this feature');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.RequestTooLarge:\n\t\t\t\tdiagnosticMessage = 'The request is too large';\n\t\t\t\tmessage = l10n.t('The request is too large');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.UserQuotaExceeded:\n\t\t\t\tdiagnosticMessage = 'You have exceeded your user token limit';\n\t\t\t\tmessage = l10n.t('You have exceeded your user token limit');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.RateLimitExceeded:\n\t\t\t\tdiagnosticMessage = 'Rate limit exceeded';\n\t\t\t\tmessage = l10n.t('Rate limit exceeded');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.RateLimitOrFundsExceeded:\n\t\t\t\tdiagnosticMessage = 'Rate limit exceeded or your account is out of funds';\n\t\t\t\tmessage = l10n.t('Rate limit exceeded or your account is out of funds');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.ServiceCapacityExceeded:\n\t\t\t\tdiagnosticMessage = 'Service capacity exceeded';\n\t\t\t\tmessage = l10n.t('Service capacity exceeded');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.NoNetwork:\n\t\t\t\tdiagnosticMessage = 'Unable to reach the AI service. Please check your internet connection.';\n\t\t\t\tmessage = l10n.t('Unable to reach the AI service. Please check your internet connection.');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.Unreachable:\n\t\t\t\tdiagnosticMessage = 'The AI service is temporarily unreachable.';\n\t\t\t\tmessage = l10n.t('The AI service is temporarily unreachable.');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.NoRequestData:\n\t\t\t\tdiagnosticMessage = original?.message ?? 'No data was provided for the request';\n\t\t\t\tmessage = original?.message ?? l10n.t('No data was provided for the request');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.ModelNotSupported:\n\t\t\t\tdiagnosticMessage = 'Model not supported for this request';\n\t\t\t\tmessage = l10n.t('Model not supported for this request');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.Unauthorized:\n\t\t\t\tdiagnosticMessage = 'You are not authorized to use the specified provider or model';\n\t\t\t\tmessage = l10n.t('You are not authorized to use the specified provider or model');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.DeniedByOrganization:\n\t\t\t\tdiagnosticMessage = 'Your organization has denied access to the specified provider or model';\n\t\t\t\tmessage = l10n.t('Your organization has denied access to the specified provider or model');\n\t\t\t\tbreak;\n\t\t\tcase AIErrorReason.DeniedByUser:\n\t\t\t\tdiagnosticMessage = 'You have denied access to the specified provider or model';\n\t\t\t\tmessage = l10n.t('You have denied access to the specified provider or model');\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tdiagnosticMessage = original?.message ?? 'An unknown error occurred';\n\t\t\t\tmessage = original?.message ?? l10n.t('An unknown error occurred');\n\t\t\t\tbreak;\n\t\t}\n\n\t\tsuper(message);\n\n\t\tthis.diagnosticMessage = diagnosticMessage;\n\t\tthis.original = original;\n\t\tthis.reason = reason;\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n\n\tget diagnosticString(): string {\n\t\treturn `${this.name}: ${this.diagnosticMessage}`;\n\t}\n}\n\nexport class AuthenticationRequiredError extends Error {\n\tconstructor() {\n\t\tsuper(l10n.t('Authentication required'));\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\nexport class AINoRequestDataError extends AIError {\n\tconstructor(message?: string) {\n\t\tsuper(AIErrorReason.NoRequestData, message ? new Error(message) : undefined);\n\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\nconst noNetworkErrorCodes = new Set([\n\t'ENOTFOUND',\n\t'ECONNREFUSED',\n\t'EAI_AGAIN',\n\t'EHOSTUNREACH',\n\t'ENETUNREACH',\n\t'ENETDOWN',\n\t'UND_ERR_CONNECT_TIMEOUT',\n]);\n\nconst unreachableErrorCodes = new Set(['ECONNRESET', 'ETIMEDOUT', 'UND_ERR_SOCKET']);\n\nexport function classifyNetworkError(ex: unknown): AIErrorReason.NoNetwork | AIErrorReason.Unreachable | undefined {\n\tlet current: unknown = ex;\n\tlet sawFetchFailed = false;\n\tfor (let depth = 0; depth < 5 && current != null; depth++) {\n\t\tif (!(current instanceof Error)) break;\n\n\t\tif (current.name === 'TypeError' && current.message === 'fetch failed') {\n\t\t\tsawFetchFailed = true;\n\t\t}\n\t\tconst code = (current as { code?: unknown }).code;\n\t\tif (typeof code === 'string') {\n\t\t\tif (noNetworkErrorCodes.has(code)) return AIErrorReason.NoNetwork;\n\t\t\tif (unreachableErrorCodes.has(code)) return AIErrorReason.Unreachable;\n\t\t}\n\n\t\tcurrent = (current as { cause?: unknown }).cause;\n\t}\n\treturn sawFetchFailed ? AIErrorReason.NoNetwork : undefined;\n}\n\n/**\n * Whether an error means AI itself is unavailable, rather than this particular request being\n * unacceptable. The distinction matters to any caller running a loop: an unavailable-AI failure will\n * repeat identically for every remaining item, so the loop should stop and say so, while a\n * request-shaped failure (too large, no data) may well succeed on the next item.\n *\n * `RequestTooLarge` and `NoRequestData` are therefore deliberately excluded — they're properties of\n * the one request that failed.\n *\n * An `AuthenticationRequiredError` (expired GitKraken session) counts too: it isn't an `AIError`, but\n * every remaining request will fail identically until the user signs in again.\n */\nexport function isAIUnavailableError(ex: unknown): ex is AIError | AuthenticationRequiredError {\n\tif (ex instanceof AuthenticationRequiredError) return true;\n\tif (!(ex instanceof AIError)) return false;\n\n\tswitch (ex.reason) {\n\t\tcase AIErrorReason.DeniedByOrganization:\n\t\tcase AIErrorReason.DeniedByUser:\n\t\tcase AIErrorReason.NoEntitlement:\n\t\tcase AIErrorReason.RateLimitExceeded:\n\t\tcase AIErrorReason.RateLimitOrFundsExceeded:\n\t\tcase AIErrorReason.ServiceCapacityExceeded:\n\t\tcase AIErrorReason.Unauthorized:\n\t\tcase AIErrorReason.UserQuotaExceeded:\n\t\tcase AIErrorReason.NoNetwork:\n\t\tcase AIErrorReason.Unreachable:\n\t\tcase AIErrorReason.ModelNotSupported:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -234,7 +234,7 @@ var MistralProvider = class extends OpenAICompatibleProviderBase {
|
|
|
234
234
|
return super.fetchCore(action, model, apiKey, request, signal);
|
|
235
235
|
}
|
|
236
236
|
async handleFetchFailure(rsp, action, model, retries, maxInputTokens, body, sentTools) {
|
|
237
|
-
if (rsp.status !== 404 && rsp.status !== 429) {
|
|
237
|
+
if (rsp.status !== 404 && rsp.status !== 429 && rsp.status !== 401 && rsp.status !== 403) {
|
|
238
238
|
let json;
|
|
239
239
|
try {
|
|
240
240
|
json = body != null ? JSON.parse(body) : await rsp.json();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mistralProvider.js","names":["provider"],"sources":["../../../../../plus/ai/src/providers/mistralProvider.ts"],"sourcesContent":["import { mistralProviderDescriptor as provider } from '../constants.js';\nimport { AIError, AIErrorReason } from '../errors.js';\nimport type { AIActionType, AIModel } from '../models/model.js';\nimport { getReducedMaxInputTokens } from '../utils/ai.utils.js';\nimport { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase.js';\n\ntype MistralModel = AIModel<typeof provider.id>;\nconst models: MistralModel[] = [\n\t{\n\t\tid: 'mistral-medium-latest',\n\t\tname: 'Mistral Medium 3.5',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'mistral-medium-3-5',\n\t\tname: 'Mistral Medium 3.5',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-medium-2505',\n\t\tname: 'Mistral Medium',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'zai-glm-5-2',\n\t\tname: 'GLM 5.2',\n\t\tmaxTokens: { input: 1000000, output: 128000 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'mistral-small-latest',\n\t\tname: 'Mistral Small 4',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\tdefault: true,\n\t},\n\t{\n\t\tid: 'mistral-small-2603',\n\t\tname: 'Mistral Small 4',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-small-2503',\n\t\tname: 'Mistral Small',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-large-latest',\n\t\tname: 'Mistral Large 3',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'mistral-large-2512',\n\t\tname: 'Mistral Large 3',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-large-2411',\n\t\tname: 'Mistral Large',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'ministral-14b-latest',\n\t\tname: 'Ministral 3 14B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'ministral-14b-2512',\n\t\tname: 'Ministral 3 14B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'ministral-8b-latest',\n\t\tname: 'Ministral 3 8B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'ministral-8b-2512',\n\t\tname: 'Ministral 3 8B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'ministral-3b-latest',\n\t\tname: 'Ministral 3 3B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'ministral-3b-2512',\n\t\tname: 'Ministral 3 3B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'codestral-latest',\n\t\tname: 'Codestral',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'codestral-2508',\n\t\tname: 'Codestral',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'codestral-2501',\n\t\tname: 'Codestral',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'devstral-small-latest',\n\t\tname: 'Devstral Small',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'devstral-small-2505',\n\t\tname: 'Devstral Small',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n];\n\nexport class MistralProvider extends OpenAICompatibleProviderBase<typeof provider.id> {\n\treadonly id = provider.id;\n\treadonly name = provider.name;\n\treadonly supportsTools = true;\n\tprotected readonly descriptor = provider;\n\tprotected readonly config = {\n\t\tkeyUrl: 'https://console.mistral.ai/api-keys',\n\t\tkeyValidator: /^[a-zA-Z0-9]{32}$/,\n\t};\n\n\tgetModels(): Promise<readonly AIModel<typeof provider.id>[]> {\n\t\treturn Promise.resolve(models);\n\t}\n\n\tprotected getUrl(_model: AIModel<typeof provider.id>): string {\n\t\treturn 'https://api.mistral.ai/v1/chat/completions';\n\t}\n\n\tprotected override fetchCore<TAction extends AIActionType>(\n\t\taction: TAction,\n\t\tmodel: AIModel<typeof provider.id>,\n\t\tapiKey: string,\n\t\trequest: object,\n\t\tsignal: AbortSignal | undefined,\n\t): Promise<Response> {\n\t\tif ('max_completion_tokens' in request) {\n\t\t\tconst { max_completion_tokens: max, ...rest } = request;\n\t\t\trequest = max ? { max_tokens: max, ...rest } : rest;\n\t\t}\n\t\treturn super.fetchCore(action, model, apiKey, request, signal);\n\t}\n\n\tprotected override async handleFetchFailure<TAction extends AIActionType>(\n\t\trsp: Response,\n\t\taction: TAction,\n\t\tmodel: AIModel<typeof provider.id>,\n\t\tretries: number,\n\t\tmaxInputTokens: number,\n\t\tbody?: string,\n\t\tsentTools?: boolean,\n\t): Promise<{ retry: true; maxInputTokens: number; withoutTools?: boolean }> {\n\t\tif (rsp.status !== 404 && rsp.status !== 429) {\n\t\t\tlet json;\n\t\t\ttry {\n\t\t\t\tjson = (body != null ? JSON.parse(body) : await rsp.json()) as MistralError | undefined;\n\t\t\t} catch {}\n\n\t\t\tdebugger;\n\n\t\t\tconst message = `${json?.type}: ${json?.message?.detail\n\t\t\t\t?.map(d => `${d.msg} (${d.type} @ ${d.loc.join(',')})`)\n\t\t\t\t.join(', ')}`;\n\n\t\t\tif (json?.type === 'invalid_request_error') {\n\t\t\t\tif (message?.includes('prompt is too long')) {\n\t\t\t\t\tif (retries < 3) {\n\t\t\t\t\t\treturn { retry: true, maxInputTokens: getReducedMaxInputTokens(maxInputTokens, retries + 1) };\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow new AIError(\n\t\t\t\t\t\tAIErrorReason.RequestTooLarge,\n\t\t\t\t\t\tnew Error(`(${this.name}) ${rsp.status}: ${message || rsp.statusText}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This override throws for every non-404/429 status, so the base class's tools-rejection\n\t\t\t// recovery would never run for Mistral — check it here to keep the text-only fallback\n\t\t\tif (sentTools && this.isToolsRejection(rsp.status, message)) {\n\t\t\t\treturn { retry: true, maxInputTokens: maxInputTokens, withoutTools: true };\n\t\t\t}\n\n\t\t\tthrow new Error(`(${this.name}) ${rsp.status}: ${message || rsp.statusText}`);\n\t\t}\n\n\t\treturn super.handleFetchFailure(rsp, action, model, retries, maxInputTokens, body, sentTools);\n\t}\n}\n\ninterface MistralError {\n\tobject: 'error';\n\ttype: 'invalid_request_error' | string;\n\tmessage: {\n\t\tdetail: {\n\t\t\ttype: string;\n\t\t\tmsg: string;\n\t\t\tloc: string[];\n\t\t\tinput: number;\n\t\t}[];\n\t};\n\tcode: unknown | null;\n\tparam: unknown | null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mistralProvider.js","names":["provider"],"sources":["../../../../../plus/ai/src/providers/mistralProvider.ts"],"sourcesContent":["import { mistralProviderDescriptor as provider } from '../constants.js';\nimport { AIError, AIErrorReason } from '../errors.js';\nimport type { AIActionType, AIModel } from '../models/model.js';\nimport { getReducedMaxInputTokens } from '../utils/ai.utils.js';\nimport { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase.js';\n\ntype MistralModel = AIModel<typeof provider.id>;\nconst models: MistralModel[] = [\n\t{\n\t\tid: 'mistral-medium-latest',\n\t\tname: 'Mistral Medium 3.5',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'mistral-medium-3-5',\n\t\tname: 'Mistral Medium 3.5',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-medium-2505',\n\t\tname: 'Mistral Medium',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'zai-glm-5-2',\n\t\tname: 'GLM 5.2',\n\t\tmaxTokens: { input: 1000000, output: 128000 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'mistral-small-latest',\n\t\tname: 'Mistral Small 4',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\tdefault: true,\n\t},\n\t{\n\t\tid: 'mistral-small-2603',\n\t\tname: 'Mistral Small 4',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-small-2503',\n\t\tname: 'Mistral Small',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-large-latest',\n\t\tname: 'Mistral Large 3',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'mistral-large-2512',\n\t\tname: 'Mistral Large 3',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'mistral-large-2411',\n\t\tname: 'Mistral Large',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'ministral-14b-latest',\n\t\tname: 'Ministral 3 14B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'ministral-14b-2512',\n\t\tname: 'Ministral 3 14B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'ministral-8b-latest',\n\t\tname: 'Ministral 3 8B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'ministral-8b-2512',\n\t\tname: 'Ministral 3 8B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'ministral-3b-latest',\n\t\tname: 'Ministral 3 3B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'ministral-3b-2512',\n\t\tname: 'Ministral 3 3B',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'codestral-latest',\n\t\tname: 'Codestral',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t},\n\t{\n\t\tid: 'codestral-2508',\n\t\tname: 'Codestral',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'codestral-2501',\n\t\tname: 'Codestral',\n\t\tmaxTokens: { input: 262144, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'devstral-small-latest',\n\t\tname: 'Devstral Small',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n\t{\n\t\tid: 'devstral-small-2505',\n\t\tname: 'Devstral Small',\n\t\tmaxTokens: { input: 131072, output: 4096 },\n\t\tprovider: provider,\n\t\thidden: true,\n\t},\n];\n\nexport class MistralProvider extends OpenAICompatibleProviderBase<typeof provider.id> {\n\treadonly id = provider.id;\n\treadonly name = provider.name;\n\treadonly supportsTools = true;\n\tprotected readonly descriptor = provider;\n\tprotected readonly config = {\n\t\tkeyUrl: 'https://console.mistral.ai/api-keys',\n\t\tkeyValidator: /^[a-zA-Z0-9]{32}$/,\n\t};\n\n\tgetModels(): Promise<readonly AIModel<typeof provider.id>[]> {\n\t\treturn Promise.resolve(models);\n\t}\n\n\tprotected getUrl(_model: AIModel<typeof provider.id>): string {\n\t\treturn 'https://api.mistral.ai/v1/chat/completions';\n\t}\n\n\tprotected override fetchCore<TAction extends AIActionType>(\n\t\taction: TAction,\n\t\tmodel: AIModel<typeof provider.id>,\n\t\tapiKey: string,\n\t\trequest: object,\n\t\tsignal: AbortSignal | undefined,\n\t): Promise<Response> {\n\t\tif ('max_completion_tokens' in request) {\n\t\t\tconst { max_completion_tokens: max, ...rest } = request;\n\t\t\trequest = max ? { max_tokens: max, ...rest } : rest;\n\t\t}\n\t\treturn super.fetchCore(action, model, apiKey, request, signal);\n\t}\n\n\tprotected override async handleFetchFailure<TAction extends AIActionType>(\n\t\trsp: Response,\n\t\taction: TAction,\n\t\tmodel: AIModel<typeof provider.id>,\n\t\tretries: number,\n\t\tmaxInputTokens: number,\n\t\tbody?: string,\n\t\tsentTools?: boolean,\n\t): Promise<{ retry: true; maxInputTokens: number; withoutTools?: boolean }> {\n\t\t// 401/403 fall through to the base, which maps them to `AIError(Unauthorized)`\n\t\tif (rsp.status !== 404 && rsp.status !== 429 && rsp.status !== 401 && rsp.status !== 403) {\n\t\t\tlet json;\n\t\t\ttry {\n\t\t\t\tjson = (body != null ? JSON.parse(body) : await rsp.json()) as MistralError | undefined;\n\t\t\t} catch {}\n\n\t\t\tdebugger;\n\n\t\t\tconst message = `${json?.type}: ${json?.message?.detail\n\t\t\t\t?.map(d => `${d.msg} (${d.type} @ ${d.loc.join(',')})`)\n\t\t\t\t.join(', ')}`;\n\n\t\t\tif (json?.type === 'invalid_request_error') {\n\t\t\t\tif (message?.includes('prompt is too long')) {\n\t\t\t\t\tif (retries < 3) {\n\t\t\t\t\t\treturn { retry: true, maxInputTokens: getReducedMaxInputTokens(maxInputTokens, retries + 1) };\n\t\t\t\t\t}\n\n\t\t\t\t\tthrow new AIError(\n\t\t\t\t\t\tAIErrorReason.RequestTooLarge,\n\t\t\t\t\t\tnew Error(`(${this.name}) ${rsp.status}: ${message || rsp.statusText}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This override throws for every non-404/429 status, so the base class's tools-rejection\n\t\t\t// recovery would never run for Mistral — check it here to keep the text-only fallback\n\t\t\tif (sentTools && this.isToolsRejection(rsp.status, message)) {\n\t\t\t\treturn { retry: true, maxInputTokens: maxInputTokens, withoutTools: true };\n\t\t\t}\n\n\t\t\tthrow new Error(`(${this.name}) ${rsp.status}: ${message || rsp.statusText}`);\n\t\t}\n\n\t\treturn super.handleFetchFailure(rsp, action, model, retries, maxInputTokens, body, sentTools);\n\t}\n}\n\ninterface MistralError {\n\tobject: 'error';\n\ttype: 'invalid_request_error' | string;\n\tmessage: {\n\t\tdetail: {\n\t\t\ttype: string;\n\t\t\tmsg: string;\n\t\t\tloc: string[];\n\t\t\tinput: number;\n\t\t}[];\n\t};\n\tcode: unknown | null;\n\tparam: unknown | null;\n}\n"],"mappings":"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"}
|
|
@@ -321,6 +321,7 @@ var OpenAICompatibleProviderBase = class {
|
|
|
321
321
|
json = body != null ? JSON.parse(body) : await rsp.json();
|
|
322
322
|
} catch {}
|
|
323
323
|
if (Array.isArray(json)) json = json[0];
|
|
324
|
+
if (rsp.status === 401 || rsp.status === 403) throw new AIError(9, /* @__PURE__ */ new Error(`(${this.name}) ${rsp.status}: ${json?.error?.message || rsp.statusText}`));
|
|
324
325
|
if (json?.error?.code === "context_length_exceeded") {
|
|
325
326
|
if (retries < 3) return {
|
|
326
327
|
retry: true,
|