@bman654/clodex 2.18.9 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -1
- package/dist/{chunk-VFYRDLDD.js → chunk-Q2YFYQQT.js} +76 -5
- package/dist/chunk-Q2YFYQQT.js.map +1 -0
- package/dist/claude-wrapper.js +17 -7
- package/dist/claude-wrapper.js.map +1 -1
- package/dist/cli.js +298 -63
- package/dist/cli.js.map +1 -1
- package/docs/background-agents.md +4 -3
- package/package.json +1 -1
- package/dist/chunk-VFYRDLDD.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/oauth-account-selection.ts","../src/registry/io.ts","../src/paths.ts","../src/registry/types.ts","../src/registry/lock.ts","../src/registry/oauth-account-storage.ts","../src/registry/migrate.ts","../src/registry/validate.ts","../src/config.ts","../src/claude-binary.ts","../src/binary-lookup.ts","../src/listener-ready.ts","../src/parent-notice.ts","../src/server-runtime.ts","../src/network-env.ts","../src/wrapper-env.ts","../src/process-exit.ts","../src/patch-manifest.ts","../src/patch-backup.ts"],"sourcesContent":["/** Environment override for selecting one named OAuth account slot. */\nexport const OAUTH_ACCOUNT_ENV = 'CLODEX_OAUTH_ACCOUNT';\n","// src/registry/io.ts — load/save providers.json with secure permissions\n\nimport { randomUUID } from 'node:crypto';\nimport {\n chmodSync,\n closeSync,\n copyFileSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeSync,\n} from 'node:fs';\nimport { dirname } from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\nimport { getAppHome, getProvidersPath } from '../paths.js';\nimport type { ProviderRegistry, RegistryModelsCache, RegistryProvider } from './types.js';\nimport {\n OAUTH_ACCOUNT_NAME_RE,\n REGISTRY_SCHEMA_VERSION,\n REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_SLOTS,\n REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT,\n REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES,\n REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT,\n} from './types.js';\nimport {\n assertRegistryWriteOwnership,\n RegistryLockLostError,\n withRegistryWriteLockSync,\n} from './lock.js';\nimport { migrateRegistry } from './migrate.js';\nimport {\n getOAuthAccountSlot,\n migrateActiveOAuthAccountStorage,\n} from './oauth-account-storage.js';\nimport { isValidProviderId } from './validate.js';\n\nconst DIR_MODE = 0o700;\nconst FILE_MODE = 0o600;\n\nexport function ensureSecureAppHome(): void {\n const home = getAppHome();\n mkdirSync(home, { recursive: true, mode: DIR_MODE });\n try {\n chmodSync(home, DIR_MODE);\n } catch {\n // best-effort on platforms that restrict chmod\n }\n}\n\nexport function writeSecureFile(path: string, content: string): void {\n ensureSecureAppHome();\n mkdirSync(dirname(path), { recursive: true, mode: DIR_MODE });\n const fd = openSync(path, 'wx', FILE_MODE);\n try {\n const payload = Buffer.from(content);\n let offset = 0;\n while (offset < payload.length) {\n const written = writeSync(fd, payload, offset, payload.length - offset);\n if (written <= 0) {\n throw new Error(`Could not complete secure file write: ${path}`);\n }\n offset += written;\n }\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(path, FILE_MODE);\n } catch {\n // best-effort\n }\n}\n\nexport function syncParentDirectory(path: string): void {\n let fd: number | undefined;\n try {\n fd = openSync(dirname(path), 'r');\n fsyncSync(fd);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'EINVAL' && code !== 'ENOTSUP' && code !== 'EPERM') throw error;\n } finally {\n if (fd !== undefined) closeSync(fd);\n }\n}\n\nfunction parseProvider(\n raw: unknown,\n diag?: (message: string) => void,\n): RegistryProvider | null {\n if (!raw || typeof raw !== 'object') return null;\n const p = raw as Record<string, unknown>;\n if (typeof p.id !== 'string' || !isValidProviderId(p.id)) return null;\n if (typeof p.templateId !== 'string' || !p.templateId) return null;\n if (typeof p.name !== 'string' || !p.name) return null;\n if (typeof p.enabled !== 'boolean') return null;\n if (typeof p.authRef !== 'string' || !p.authRef) return null;\n if (typeof p.addedAt !== 'string' || !p.addedAt) return null;\n const api = p.api;\n if (!api || typeof api !== 'object') return null;\n\n const provider: RegistryProvider = {\n id: p.id,\n templateId: p.templateId,\n name: p.name,\n enabled: p.enabled,\n authRef: p.authRef,\n api: api as RegistryProvider['api'],\n addedAt: p.addedAt,\n };\n\n if (hasOwn(p, 'defaultAuthRef')) {\n if (typeof p.defaultAuthRef !== 'string' || !p.defaultAuthRef) return null;\n provider.defaultAuthRef = p.defaultAuthRef;\n }\n\n if (p.subscriptionFilter === 'free') {\n provider.subscriptionFilter = p.subscriptionFilter;\n }\n if (typeof p.preserveModelPricing === 'boolean') {\n provider.preserveModelPricing = p.preserveModelPricing;\n }\n if (p.authType === 'api' || p.authType === 'oauth' || p.authType === 'none') {\n provider.authType = p.authType;\n }\n if (hasOwn(p, 'authAccounts')) {\n const slots = parseAuthAccounts(p.authAccounts);\n if (slots === null) return null;\n provider.authAccounts = slots;\n }\n if (hasOwn(p, 'activeAuthAccount')) {\n if (!isAccountName(p.activeAuthAccount)) return null;\n provider.activeAuthAccount = p.activeAuthAccount;\n }\n if (typeof p.refreshedAt === 'string') provider.refreshedAt = p.refreshedAt;\n if (hasOwn(p, 'defaultModelsCache')) {\n const defaultModelsCache = parseModelsCache(p.defaultModelsCache);\n if (!defaultModelsCache) return null;\n provider.defaultModelsCache = defaultModelsCache;\n }\n const modelsCache = parseModelsCache(p.modelsCache);\n if (modelsCache) provider.modelsCache = modelsCache;\n else if (hasOwn(p, 'modelsCache')) {\n diag?.(`Provider registry dropped an invalid model cache for provider \"${p.id}\".`);\n }\n return provider;\n}\n\nfunction hasOwn(record: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(record, key);\n}\n\n/**\n * Shape check only, deliberately not a slot-membership check: see the\n * `activeAuthAccount` doc comment on RegistryProvider for why a stale-but-\n * well-formed name must survive the load and fail at apply time instead.\n */\nfunction isAccountName(raw: unknown): raw is string {\n return typeof raw === 'string' && OAUTH_ACCOUNT_NAME_RE.test(raw);\n}\n\nfunction parseModelsCache(raw: unknown): RegistryModelsCache | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const cache = raw as Record<string, unknown>;\n if (typeof cache.fetchedAt !== 'string' || !Array.isArray(cache.models)) return null;\n if (cache.models.some(model => !model || typeof model !== 'object' || Array.isArray(model))) {\n return null;\n }\n return {\n fetchedAt: cache.fetchedAt,\n models: cache.models as RegistryModelsCache['models'],\n };\n}\n\n/**\n * Named OAuth account slots must survive a registry load intact and are\n * fail-closed: a silently dropped slot would revert a CLODEX_OAUTH_ACCOUNT\n * launch to the default identity and let credential reconciliation delete the\n * slot's tokens as unreferenced. A malformed slot therefore invalidates the\n * whole provider record instead of being skipped.\n */\nfunction parseAuthAccounts(raw: unknown): RegistryProvider['authAccounts'] | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const out: NonNullable<RegistryProvider['authAccounts']> = {};\n for (const [name, value] of Object.entries(raw as Record<string, unknown>)) {\n if (!OAUTH_ACCOUNT_NAME_RE.test(name)) return null;\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const slot = value as Record<string, unknown>;\n if (typeof slot.authRef !== 'string' || !slot.authRef) return null;\n if (typeof slot.addedAt !== 'string' || !slot.addedAt) return null;\n const modelsCache = hasOwn(slot, 'modelsCache')\n ? parseModelsCache(slot.modelsCache)\n : undefined;\n if (hasOwn(slot, 'modelsCache') && !modelsCache) return null;\n out[name] = {\n authRef: slot.authRef,\n addedAt: slot.addedAt,\n ...(modelsCache ? { modelsCache } : {}),\n };\n }\n return out;\n}\n\nfunction hasValidStrictProviderFields(raw: unknown): boolean {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return false;\n const provider = raw as Record<string, unknown>;\n if (hasOwn(provider, 'subscriptionFilter') && provider.subscriptionFilter !== 'free') {\n return false;\n }\n if (hasOwn(provider, 'preserveModelPricing') && typeof provider.preserveModelPricing !== 'boolean') {\n return false;\n }\n if (\n hasOwn(provider, 'authType')\n && provider.authType !== 'api'\n && provider.authType !== 'oauth'\n && provider.authType !== 'none'\n ) {\n return false;\n }\n if (hasOwn(provider, 'refreshedAt') && typeof provider.refreshedAt !== 'string') {\n return false;\n }\n if (hasOwn(provider, 'authAccounts') && parseAuthAccounts(provider.authAccounts) === null) {\n return false;\n }\n if (hasOwn(provider, 'activeAuthAccount') && !isAccountName(provider.activeAuthAccount)) {\n return false;\n }\n if (hasOwn(provider, 'defaultAuthRef')\n && (typeof provider.defaultAuthRef !== 'string' || !provider.defaultAuthRef)) {\n return false;\n }\n if (hasOwn(provider, 'defaultModelsCache')\n && parseModelsCache(provider.defaultModelsCache) === null) {\n return false;\n }\n if (hasOwn(provider, 'modelsCache')) {\n if (parseModelsCache(provider.modelsCache) === null) return false;\n }\n return true;\n}\n\nfunction hasPresentInvalidAuthType(raw: unknown): boolean {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return false;\n const provider = raw as Record<string, unknown>;\n return hasOwn(provider, 'authType')\n && provider.authType !== 'api'\n && provider.authType !== 'oauth'\n && provider.authType !== 'none';\n}\n\n/**\n * Validate the cross-field credential-storage contract for the schema that\n * supplied it. Versions 1-4 are valid migration input only when the parked\n * field is absent. Version 5 requires every live OAuth selector to name a slot\n * and to materialize that exact slot in `authRef`; otherwise launch must fail\n * closed rather than falling back to the provider default.\n */\nfunction hasValidSelectionStorage(\n provider: RegistryProvider,\n schemaVersion: number,\n): boolean {\n const name = provider.activeAuthAccount?.trim();\n const hasDefault = provider.defaultAuthRef !== undefined;\n const hasDefaultCache = provider.defaultModelsCache !== undefined;\n\n if (schemaVersion < REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES\n && Object.values(provider.authAccounts ?? {}).some(account => account.modelsCache !== undefined)) {\n return false;\n }\n if (name && schemaVersion < REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT) {\n return false;\n }\n if (schemaVersion <= REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES\n && (hasDefault || hasDefaultCache)) {\n return false;\n }\n if (!name) return !hasDefault && !hasDefaultCache;\n if (provider.authType !== 'oauth') return !hasDefault && !hasDefaultCache;\n if (schemaVersion <= REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES) {\n // A well-formed but missing legacy slot remains repairable. Migration does\n // not materialize it, and current launch-time selection still throws.\n return true;\n }\n if (schemaVersion !== REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT) {\n // Preserve the lenient reader's forward-compatible behavior. Strict reads\n // reject unsupported versions before reaching this branch.\n return true;\n }\n const selected = getOAuthAccountSlot(provider, name);\n return hasDefault\n && selected !== undefined\n && provider.authRef === selected.authRef\n // A downgraded launch consumes top-level authRef AND modelsCache. Require\n // the cache to be the selected slot's proven cache (or both absent), or it\n // could launch the right credential with another account's entitlements.\n && isDeepStrictEqual(provider.modelsCache, selected.modelsCache);\n}\n\nfunction parseRegistry(\n raw: unknown,\n diag?: (message: string) => void,\n): ProviderRegistry {\n const empty: ProviderRegistry = { schemaVersion: REGISTRY_SCHEMA_VERSION, providers: [] };\n if (!raw || typeof raw !== 'object') return empty;\n const data = raw as Record<string, unknown>;\n const schemaVersion =\n typeof data.schemaVersion === 'number' ? data.schemaVersion : REGISTRY_SCHEMA_VERSION;\n const providers: RegistryProvider[] = [];\n if (Array.isArray(data.providers)) {\n for (const [index, entry] of data.providers.entries()) {\n const parsed = parseProvider(entry, diag);\n const invalidKnownSelectionAuthType = schemaVersion >= REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT\n && schemaVersion <= REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT\n && parsed?.activeAuthAccount !== undefined\n && hasPresentInvalidAuthType(entry);\n const validSelectionStorage = parsed\n ? hasValidSelectionStorage(parsed, schemaVersion)\n : false;\n if (parsed && !invalidKnownSelectionAuthType && validSelectionStorage) {\n providers.push(parsed);\n } else {\n const id = entry && typeof entry === 'object' && typeof (entry as Record<string, unknown>).id === 'string'\n ? ` \"${(entry as Record<string, unknown>).id}\"`\n : ` at index ${index}`;\n const reason = parsed && !validSelectionStorage\n ? ' because its OAuth account selection storage is inconsistent'\n : '';\n diag?.(`Provider registry dropped invalid provider${id}${reason}.`);\n }\n }\n }\n const registry: ProviderRegistry = {\n schemaVersion,\n providers,\n };\n if (typeof data.importedAt === 'string') registry.importedAt = data.importedAt;\n if (typeof data.pricingCacheAt === 'string') registry.pricingCacheAt = data.pricingCacheAt;\n return registry;\n}\n\nfunction parseRegistryStrict(raw: unknown): ProviderRegistry {\n if (!raw || typeof raw !== 'object') {\n throw new Error('Provider registry must be a JSON object.');\n }\n const data = raw as Record<string, unknown>;\n if (\n data.schemaVersion !== REGISTRY_SCHEMA_VERSION\n && data.schemaVersion !== REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_SLOTS\n && data.schemaVersion !== REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT\n && data.schemaVersion !== REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES\n && data.schemaVersion !== REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT\n ) {\n throw new Error('Provider registry has an unsupported schema version.');\n }\n if (!Array.isArray(data.providers)) {\n throw new Error('Provider registry is missing its providers list.');\n }\n for (const entry of data.providers) {\n const provider = parseProvider(entry);\n if (!provider || !hasValidStrictProviderFields(entry)\n || !hasValidSelectionStorage(provider, data.schemaVersion)) {\n throw new Error('Provider registry contains an invalid provider entry.');\n }\n }\n return parseRegistry(raw);\n}\n\nfunction readRegistryStrict(path: string): ProviderRegistry {\n return parseRegistryStrict(JSON.parse(readFileSync(path, 'utf8')));\n}\n\nclass RegistryMigrationValidationError extends Error {\n constructor(\n readonly registryPath: string,\n cause: unknown,\n ) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n super(detail, { cause });\n this.name = 'RegistryMigrationValidationError';\n }\n}\n\nclass RegistryMigrationReadError extends Error {\n constructor(\n readonly registryPath: string,\n cause: unknown,\n ) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n super(detail, { cause });\n this.name = 'RegistryMigrationReadError';\n }\n}\n\nclass RegistrySaveValidationError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'RegistrySaveValidationError';\n }\n}\n\nclass RegistryPersistenceError extends Error {\n constructor(\n readonly registryPath: string,\n cause: unknown,\n ) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n super(detail, { cause });\n this.name = 'RegistryPersistenceError';\n }\n}\n\nclass RegistryDurabilityCheckError extends Error {\n constructor(\n readonly registryPath: string,\n cause: unknown,\n ) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n super(detail, { cause });\n this.name = 'RegistryDurabilityCheckError';\n }\n}\n\nfunction selectedAccountFilesystemError(\n path: string,\n cause: unknown,\n action: string,\n): Error {\n const detail = cause instanceof Error ? cause.message : String(cause);\n return new Error(\n 'Could not safely persist the selected OAuth account before launch. '\n + `Could not ${action} the provider registry at ${path}: ${detail} `\n + 'Check filesystem permissions, storage health, and free disk space, then retry.',\n { cause },\n );\n}\n\nfunction hasMaterializedActiveAccount(registry: ProviderRegistry): boolean {\n return registry.schemaVersion === REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT\n && registry.providers.some(provider => provider.defaultAuthRef !== undefined);\n}\n\nexport function loadRegistry(\n path = getProvidersPath(),\n diag?: (message: string) => void,\n): ProviderRegistry {\n if (!existsSync(path)) {\n return { schemaVersion: REGISTRY_SCHEMA_VERSION, providers: [] };\n }\n let registry: ProviderRegistry;\n try {\n const raw = JSON.parse(readFileSync(path, 'utf8'));\n registry = parseRegistry(raw, diag);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n diag?.(`Could not read the provider registry at ${path}; treating it as empty: ${detail}`);\n return { schemaVersion: REGISTRY_SCHEMA_VERSION, providers: [] };\n }\n\n const migration = migrateRegistry(registry);\n if (!migration.changed) {\n // A previous publication may have renamed ANY schema version before its\n // parent-directory durability barrier failed. This includes clearing a v5\n // selection back to v1/v2. Re-sync every successfully parsed winner so a\n // plain retry repairs both selection and downgrade transitions.\n try {\n syncParentDirectory(path);\n } catch (error) {\n if (hasMaterializedActiveAccount(registry)) {\n throw selectedAccountFilesystemError(path, error, 'durably sync');\n }\n const detail = error instanceof Error ? error.message : String(error);\n diag?.(`Could not durably sync the unchanged provider registry at ${path}; continuing read-only: ${detail}`);\n }\n return registry;\n }\n\n let winner = registry;\n let materializedActiveAccount = migration.materializedActiveAccount;\n try {\n withRegistryWriteLockSync(() => {\n if (!existsSync(path)) {\n if (materializedActiveAccount) {\n throw new Error('the provider registry disappeared during migration');\n }\n return;\n }\n // The lock may have been contended. Re-read and migrate the winner rather\n // than publishing or returning the stale pre-lock snapshot.\n let serialized: string;\n try {\n serialized = readFileSync(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error('the provider registry disappeared during migration', { cause: error });\n }\n throw new RegistryMigrationReadError(path, error);\n }\n let raw: unknown;\n try {\n raw = JSON.parse(serialized);\n } catch (error) {\n throw new RegistryMigrationValidationError(path, error);\n }\n // Classify the winner's identity requirement before strict validation.\n // A malformed sibling can make the strict parse throw, but it must not\n // hide a valid selected provider whose downgrade-visible projection\n // would otherwise remain in memory only.\n const lenientCurrent = parseRegistry(raw);\n const lenientMigration = migrateRegistry(lenientCurrent);\n winner = lenientCurrent;\n materializedActiveAccount = lenientMigration.materializedActiveAccount\n || hasMaterializedActiveAccount(lenientCurrent);\n\n let current: ProviderRegistry;\n try {\n current = parseRegistryStrict(raw);\n } catch (error) {\n throw new RegistryMigrationValidationError(path, error);\n }\n const currentMigration = migrateRegistry(current);\n winner = current;\n // The winner under the lock, not the stale pre-lock snapshot, decides\n // whether a failed write could split launch identity across processes.\n materializedActiveAccount = currentMigration.materializedActiveAccount\n || hasMaterializedActiveAccount(current);\n if (currentMigration.changed) {\n try {\n saveRegistry(current, path);\n } catch (error) {\n if (error instanceof RegistrySaveValidationError) {\n throw new RegistryMigrationValidationError(path, error);\n }\n throw error;\n }\n } else {\n // Another process may have renamed this already-migrated winner but\n // failed its parent-directory fsync before we acquired the lock. The\n // authoritative reread must repair that barrier too, not only the\n // optimistic no-migration read above.\n try {\n syncParentDirectory(path);\n } catch (error) {\n throw new RegistryDurabilityCheckError(path, error);\n }\n }\n }, { lockPath: `${path}.lock` });\n return winner;\n } catch (error) {\n if (error instanceof RegistryDurabilityCheckError) {\n if (materializedActiveAccount) {\n throw selectedAccountFilesystemError(\n error.registryPath,\n error,\n 'durably sync',\n );\n }\n throw new Error(\n `Could not durably sync the provider registry at ${error.registryPath}: ${error.message} `\n + 'Check filesystem permissions, storage health, and free disk space, then retry.',\n { cause: error },\n );\n }\n if (materializedActiveAccount) {\n if (error instanceof RegistryMigrationValidationError) {\n throw new Error(\n 'Could not safely persist the selected OAuth account before launch. '\n + `The provider registry at ${error.registryPath} is invalid: ${error.message} `\n + `Repair it or restore ${error.registryPath}.bak, then retry.`,\n { cause: error },\n );\n }\n if (error instanceof RegistryMigrationReadError) {\n throw selectedAccountFilesystemError(\n error.registryPath,\n error,\n 'read',\n );\n }\n if (error instanceof RegistryPersistenceError) {\n throw selectedAccountFilesystemError(\n error.registryPath,\n error,\n 'durably write',\n );\n }\n if (error && typeof error === 'object'\n && typeof (error as NodeJS.ErrnoException).code === 'string') {\n // Lock timeout/lost-lease errors are intentionally untyped and retain\n // the process-contention guidance below. Raw errno failures from lock\n // directory/file I/O need filesystem recovery instead.\n throw selectedAccountFilesystemError(path, error, 'access the lock for');\n }\n const detail = error instanceof Error ? ` ${error.message}` : '';\n throw new Error(\n 'Could not safely persist the selected OAuth account before launch.'\n + `${detail} Stop other Clodex processes and retry.`,\n { cause: error },\n );\n }\n // The historical provider-id rename is presentation-only and remains a\n // best-effort migration. Identity materialization above is not.\n return winner;\n }\n}\n\n/**\n * Load a registry for destructive decisions. Unlike `loadRegistry`, read,\n * parse, and provider-shape errors propagate so callers cannot confuse an\n * unreadable registry with an empty one.\n */\nexport function loadRegistryStrict(path = getProvidersPath()): ProviderRegistry {\n if (!existsSync(path)) {\n return { schemaVersion: REGISTRY_SCHEMA_VERSION, providers: [] };\n }\n const registry = readRegistryStrict(path);\n migrateRegistry(registry);\n return registry;\n}\n\nexport function saveRegistry(registry: ProviderRegistry, path = getProvidersPath()): void {\n assertRegistryWriteOwnership(path);\n // Programmatic callers and strict legacy loads may hand this function a\n // v3/v4 selector. Upgrade it under the same write lock so this build never\n // publishes a selector that a downgraded launch will ignore.\n if (registry.schemaVersion >= REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT\n && registry.schemaVersion <= REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES) {\n migrateActiveOAuthAccountStorage(registry);\n }\n for (const provider of registry.providers) {\n if (!hasValidSelectionStorage(\n provider,\n REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT,\n )) {\n throw new RegistrySaveValidationError(\n 'Provider registry contains invalid OAuth account selection storage.',\n );\n }\n }\n // Slot state fences older writers via the schema version (see types.ts);\n // slot-free registries return to v1 so old builds interoperate again.\n // Highest state present wins. Version 3 fences the selector from older\n // writers; version 5 also projects it into `authRef`, which older lenient\n // launchers already read.\n const hasMaterializedSelector = registry.providers.some(\n provider => provider.defaultAuthRef !== undefined,\n );\n const hasAccountModelCaches = registry.providers.some(provider => (\n Object.values(provider.authAccounts ?? {}).some(account => account.modelsCache !== undefined)\n ));\n const hasSelector = registry.providers.some(provider => provider.activeAuthAccount !== undefined);\n const hasSlots = registry.providers.some(\n provider => provider.authAccounts && Object.keys(provider.authAccounts).length > 0,\n );\n const schemaVersion = hasMaterializedSelector\n ? REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT\n : hasAccountModelCaches\n ? REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES\n : hasSelector\n ? REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT\n : hasSlots\n ? REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_SLOTS\n : REGISTRY_SCHEMA_VERSION;\n const serializedRegistry = { ...registry, schemaVersion };\n // Validate the exact JSON shape about to be published. This catches values\n // omitted by JSON.stringify (for example an accidentally undefined authRef)\n // instead of letting the next load discover the provider has disappeared.\n try {\n parseRegistryStrict(JSON.parse(JSON.stringify(serializedRegistry)));\n } catch (error) {\n throw new RegistrySaveValidationError(\n error instanceof Error ? error.message : String(error),\n { cause: error },\n );\n }\n const payload = `${JSON.stringify(serializedRegistry, null, 2)}\\n`;\n const backup = `${path}.bak`;\n if (existsSync(path)) {\n try {\n copyFileSync(path, backup);\n } catch {\n // backup is best-effort\n }\n }\n const tmp = `${path}.${process.pid}.${randomUUID()}.tmp`;\n try {\n writeSecureFile(tmp, payload);\n assertRegistryWriteOwnership(path);\n renameSync(tmp, path);\n syncParentDirectory(path);\n } catch (error) {\n if (error instanceof RegistryLockLostError) throw error;\n throw new RegistryPersistenceError(path, error);\n } finally {\n try {\n unlinkSync(tmp);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new RegistryPersistenceError(path, err);\n }\n }\n }\n}\n\nexport function emptyRegistry(): ProviderRegistry {\n return { schemaVersion: REGISTRY_SCHEMA_VERSION, providers: [] };\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport const APP_DIR_NAME = 'clodex';\n\ninterface HomeEnv {\n HOME?: string;\n CLODEX_HOME?: string;\n USERPROFILE?: string;\n}\n\nfunction userHome(env: HomeEnv = process.env): string {\n return env.HOME ?? env.USERPROFILE ?? homedir();\n}\n\nexport function resolveAppHomeOverride(env: HomeEnv = process.env): string | undefined {\n const override = env.CLODEX_HOME;\n return override?.trim() || undefined;\n}\n\nexport function getAppHome(env: HomeEnv = process.env): string {\n const override = resolveAppHomeOverride(env);\n if (override) return override;\n return join(userHome(env), `.${APP_DIR_NAME}`);\n}\n\nexport function getConfigPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'config.json');\n}\n\nexport function getLocalPatchesPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'local-patches.mjs');\n}\n\nexport function getProvidersPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'providers.json');\n}\n\nexport function getCredentialCleanupPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'credential-cleanup.json');\n}\n\nexport function getLogsPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'logs');\n}\n","// src/registry/types.ts — native provider registry schema (no secrets)\n\nimport type { FreeStatus } from '../free-models.js';\nimport type { ModelRuntimeCompatibility } from '../model-runtime-compatibility.js';\n\nexport const REGISTRY_SCHEMA_VERSION = 1;\n\n/**\n * Written whenever any provider carries named OAuth account slots. Builds\n * >= 1.3.0 fail closed on an unknown schema version in every MUTATING path\n * (parseRegistryStrict throws), so those builds cannot load a slot-bearing\n * registry, drop the unknown field, and save the providers back slot-less.\n * Releases 0.1.0-1.2.2 have no strict loader and silently strip slot state;\n * the credentials remain recoverable in the credential store. A registry\n * whose last slot is removed is written back at version 1, so older builds\n * interoperate again the moment no slot state exists.\n */\nexport const REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_SLOTS = 2;\n\n/**\n * Written whenever any provider carries `activeAuthAccount`.\n *\n * A DISTINCT version, not a reuse of the slot version: a build from before the\n * stored selector existed accepts version 2, parses the slots, silently\n * ignores the unknown `activeAuthAccount`, and saves the registry back without\n * it. Version 3 stops that, because its strict loader throws on a version it\n * does not know. A registry whose selector is cleared falls back to 2 (or 1),\n * so older builds interoperate again as soon as no selector state exists.\n *\n * Versions 3 and 4 are also migration sources for downgrade-safe selection\n * storage. They still carry the provider default in `authRef`; current builds\n * project the selected slot into that field and park the default separately\n * before writing version 5.\n */\nexport const REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT = 3;\n\n/**\n * Written when a named OAuth slot carries its own model-entitlement cache.\n * A cache is not a credential, but silently dropping it would make a temporary\n * account reuse the persisted account's catalog. Version 4 therefore fences\n * older mutating builds that know about slots but not their cache isolation.\n */\nexport const REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES = 4;\n\n/**\n * Written whenever `defaultAuthRef` parks a provider's own OAuth credential\n * while `authRef` points at the selected named slot. A pre-selector build's\n * lenient loader ignores both the version and the new fields, but already\n * reads `authRef`, so it launches as the selected identity. Its strict\n * mutation paths reject version 5 and cannot discard the parked default.\n */\nexport const REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT = 5;\n\n/**\n * Shape rule for a named OAuth account-slot name — the single home. Slot\n * names land in credential-store scopes and env values, and the registry\n * parser must accept exactly what `validateOAuthAccountName` admits, or a\n * saved slot fails to survive a load.\n */\nexport const OAUTH_ACCOUNT_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,31}$/;\n\nexport type RegistrySubscriptionFilter = 'free';\n\nexport interface CachedModel {\n id: string;\n name: string;\n upstreamModelId: string;\n family?: string;\n brand?: string;\n contextWindow?: number;\n /** Highest input window the model accepts, reachable via the `max` context stop. */\n maxContextWindow?: number;\n /** Share of the raw window a client fills; mirrors the Codex catalog field. */\n effectiveContextPercent?: number;\n /** Input size above which the provider bills the whole request at a higher rate. */\n pricingBoundary?: number;\n /** How the provider prices above the boundary, for the user-facing warning. */\n pricingBoundaryNote?: string;\n /** Largest output the model accepts, independent of the input window. */\n maxOutputTokens?: number;\n cost?: { input: number; output: number; cache_read?: number; cache_write?: number };\n isFree?: boolean;\n freeStatus?: FreeStatus;\n modelFormat: 'anthropic' | 'openai' | 'cloud-code';\n /** Per-model override — wins over provider-level api.npm */\n npm?: string;\n /** Per-model override — wins over provider-level api.url */\n apiUrl?: string;\n sourceBackend?: string;\n /** Provider-reported request parameters, e.g. OpenRouter supported_parameters. */\n supportedParameters?: string[];\n /** Broad model metadata: model can produce reasoning/thinking output. */\n reasoning?: boolean;\n /** Streaming/interleaved reasoning field name from metadata, e.g. reasoning_content. */\n interleavedReasoningField?: string;\n /** Backend capability: model requires the Responses-Lite request shape (x-openai-internal-codex-responses-lite). */\n useResponsesLite?: boolean;\n /** Backend capability: model must use the WebSocket Responses transport instead of HTTP. */\n preferWebSockets?: boolean;\n /** Minimum Codex client version reported by the ChatGPT catalog. */\n minimalClientVersion?: string;\n /**\n * Codex client version at which the ChatGPT Codex catalog omitted this model\n * although discovery's own fetch listed it. Recorded for every omitted row; it\n * bounds compatibility only for Responses-Lite models, whose requests send that\n * version, and can be stricter than the published minimum.\n */\n withheldAtClientVersion?: string;\n /** Supported input modalities preserved from curated provider metadata. */\n modalities?: ('text' | 'image')[];\n /** Provider-neutral per-model wire quirks. */\n compatibility?: ModelRuntimeCompatibility;\n}\n\nexport interface RegistryModelsCache {\n fetchedAt: string;\n models: CachedModel[];\n}\n\nexport interface RegistryOAuthAccount {\n authRef: string;\n addedAt: string;\n /** Catalog discovered with this named slot's credential. */\n modelsCache?: RegistryModelsCache;\n}\n\nexport interface RegistryProvider {\n id: string;\n templateId: string;\n name: string;\n enabled: boolean;\n authRef: string;\n /**\n * The provider's own OAuth credential while a named slot is selected.\n * During that time `authRef` deliberately points at the selected slot so a\n * downgraded pre-selector build launches as the same identity. Clearing the\n * selection restores this value to `authRef` and removes the field.\n */\n defaultAuthRef?: string;\n /** Catalog discovered with the provider default while a named slot is selected. */\n defaultModelsCache?: RegistryModelsCache;\n authType?: 'api' | 'oauth' | 'none';\n /**\n * Named OAuth account slots beyond the default credential\n * (`clodex providers auth openai --account <name>`). Each slot owns a\n * disjoint credential-store lineage; CLODEX_OAUTH_ACCOUNT selects one for a\n * launch without replacing the provider-owned default credential.\n */\n authAccounts?: Record<string, RegistryOAuthAccount>;\n /**\n * The `authAccounts` slot every launch uses, so the running identity does not\n * depend on remembering an environment variable. Absent means the provider's\n * own default credential. CLODEX_OAUTH_ACCOUNT still overrides it for a\n * single run. While present on an OAuth provider, persisted `authRef` is the\n * selected slot and `defaultAuthRef` parks the provider default for rollback.\n *\n * Legacy v3/v4 bytes enforce only the name shape so a missing slot remains\n * loadable and repairable; applying it still fails loud. V5 additionally\n * requires membership and exact authRef/cache projection, because a missing\n * materialized slot is corruption rather than a legacy repair state.\n */\n activeAuthAccount?: string;\n subscriptionFilter?: RegistrySubscriptionFilter;\n /** Keep provider/curated costs instead of replacing them with the global pricing cache. */\n preserveModelPricing?: boolean;\n api: {\n npm?: string;\n url?: string;\n id?: string;\n /** Static headers sent on every upstream request (e.g. a plan/auth-tracking header a custom endpoint requires). */\n headers?: Record<string, string>;\n };\n modelsCache?: RegistryModelsCache;\n addedAt: string;\n refreshedAt?: string;\n}\n\nexport interface ProviderRegistry {\n schemaVersion: number;\n providers: RegistryProvider[];\n importedAt?: string;\n pricingCacheAt?: string;\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { createHash, randomUUID } from 'node:crypto';\nimport {\n closeSync,\n fstatSync,\n fsyncSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { userInfo } from 'node:os';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport { getProvidersPath } from '../paths.js';\n\nconst DEFAULT_WAIT_MS = 30_000;\nconst DEFAULT_CREDENTIAL_MUTATION_WAIT_MS = 150_000;\nconst DEFAULT_RETRY_MS = 25;\n\ninterface RegistryLockOwner {\n pid: number;\n startedAt: number;\n token: string;\n}\n\ninterface RegistryLockSnapshot {\n raw: string;\n device: number;\n inode: number;\n modifiedAt: number;\n}\n\ninterface RegistryLockOptions {\n lockPath?: string;\n waitMs?: number;\n retryMs?: number;\n now?: () => number;\n isAlive?: (pid: number) => boolean;\n}\n\ninterface RegistryWriteLockOptions extends RegistryLockOptions {\n /**\n * Abandons the wait for the lock: once aborted, the returned promise rejects with the signal's\n * reason instead of acquiring. It never interrupts an operation that already holds the lock.\n */\n signal?: AbortSignal;\n}\n\ninterface RegistryLockContext {\n leases: ReadonlyMap<string, RegistryLockLease>;\n}\n\nexport interface RegistryLockLease {\n active: boolean;\n readonly lockPath: string;\n readonly token: string;\n readonly device: number;\n readonly inode: number;\n assertOwned: () => void;\n release: () => void;\n}\n\nconst registryLockContext = new AsyncLocalStorage<RegistryLockContext>();\n\nexport class RegistryLockLostError extends Error {\n constructor(lockPath: string) {\n super(`Provider registry lock ownership was lost before write: ${lockPath}`);\n this.name = 'RegistryLockLostError';\n }\n}\n\nexport function getRegistryLockPath(): string {\n return `${getProvidersPath()}.lock`;\n}\n\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\nfunction parseLockOwner(raw: string): RegistryLockOwner | null {\n try {\n const parsed = JSON.parse(raw) as Partial<RegistryLockOwner>;\n if (!Number.isInteger(parsed.pid) || (parsed.pid ?? 0) <= 0) return null;\n if (\n typeof parsed.startedAt !== 'number' ||\n !Number.isFinite(parsed.startedAt)\n )\n return null;\n if (typeof parsed.token !== 'string' || parsed.token.length === 0)\n return null;\n return parsed as RegistryLockOwner;\n } catch {\n return null;\n }\n}\n\nfunction createLockRecord(\n lockPath: string,\n owner: RegistryLockOwner,\n): RegistryLockSnapshot | null {\n const raw = JSON.stringify(owner);\n const tempPath = `${lockPath}.${process.pid}.${owner.token}.tmp`;\n let fd: number | undefined;\n try {\n fd = openSync(tempPath, 'wx', 0o600);\n writeFileSync(fd, raw);\n fsyncSync(fd);\n const stats = fstatSync(fd);\n try {\n linkSync(tempPath, lockPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') return null;\n throw err;\n }\n return {\n raw,\n device: stats.dev,\n inode: stats.ino,\n modifiedAt: stats.mtimeMs,\n };\n } finally {\n if (fd !== undefined) closeSync(fd);\n try {\n unlinkSync(tempPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n }\n}\n\nfunction lockFileMatchesLease(lease: RegistryLockLease): boolean {\n let fd: number | undefined;\n try {\n fd = openSync(lease.lockPath, 'r');\n const openedStats = fstatSync(fd);\n const owner = parseLockOwner(readFileSync(fd, 'utf8'));\n const pathStats = statSync(lease.lockPath);\n return (\n owner?.token === lease.token &&\n openedStats.dev === lease.device &&\n openedStats.ino === lease.inode &&\n pathStats.dev === lease.device &&\n pathStats.ino === lease.inode\n );\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n } finally {\n if (fd !== undefined) closeSync(fd);\n }\n}\n\nfunction createLease(\n lockPath: string,\n owner: RegistryLockOwner,\n snapshot: RegistryLockSnapshot,\n): RegistryLockLease {\n const lease: RegistryLockLease = {\n active: true,\n lockPath,\n token: owner.token,\n device: snapshot.device,\n inode: snapshot.inode,\n assertOwned: () => {\n if (!lease.active || !lockFileMatchesLease(lease)) {\n lease.active = false;\n throw new RegistryLockLostError(lockPath);\n }\n },\n release: () => {\n if (!lease.active) return;\n lease.active = false;\n if (lockFileMatchesLease(lease)) unlinkSync(lockPath);\n },\n };\n return lease;\n}\n\nexport function assertRegistryWriteOwnership(\n registryPath = getProvidersPath(),\n): void {\n const lockPath = `${registryPath}.lock`;\n const lease = registryLockContext.getStore()?.leases.get(lockPath);\n if (!lease) throw new RegistryLockLostError(lockPath);\n lease.assertOwned();\n}\n\nfunction getStaleLockSnapshot(\n lockPath: string,\n alive: (pid: number) => boolean,\n): RegistryLockSnapshot | null {\n const raw = readFileSync(lockPath, 'utf8');\n const stats = statSync(lockPath);\n const snapshot: RegistryLockSnapshot = {\n raw,\n device: stats.dev,\n inode: stats.ino,\n modifiedAt: stats.mtimeMs,\n };\n const owner = parseLockOwner(raw);\n if (owner) return alive(owner.pid) ? null : snapshot;\n return snapshot;\n}\n\nfunction removeStaleLock(\n lockPath: string,\n expected?: RegistryLockSnapshot,\n): boolean {\n try {\n if (expected) {\n const raw = readFileSync(lockPath, 'utf8');\n const stats = statSync(lockPath);\n if (\n raw !== expected.raw ||\n stats.dev !== expected.device ||\n stats.ino !== expected.inode ||\n stats.mtimeMs !== expected.modifiedAt\n )\n return false;\n }\n unlinkSync(lockPath);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n return false;\n }\n}\n\nfunction tryAcquireReaperGuard(\n lockPath: string,\n now: number,\n alive: (pid: number) => boolean,\n): RegistryLockLease | null {\n const guardPath = `${lockPath}.reap`;\n for (let attempt = 0; attempt < 3; attempt += 1) {\n const owner: RegistryLockOwner = {\n pid: process.pid,\n startedAt: now,\n token: randomUUID(),\n };\n try {\n const snapshot = createLockRecord(guardPath, owner);\n if (snapshot) return createLease(guardPath, owner, snapshot);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') continue;\n throw err;\n }\n\n let stale: RegistryLockSnapshot | null = null;\n try {\n stale = getStaleLockSnapshot(guardPath, alive);\n } catch (readErr) {\n if ((readErr as NodeJS.ErrnoException).code === 'ENOENT') continue;\n throw readErr;\n }\n if (!stale) return null;\n if (!removeStaleLock(guardPath, stale)) continue;\n }\n return null;\n}\n\nexport function tryAcquireRegistryLock(\n lockPath = getRegistryLockPath(),\n options: Pick<RegistryLockOptions, 'now' | 'isAlive'> = {},\n): RegistryLockLease | null {\n const now = options.now?.() ?? Date.now();\n const alive = options.isAlive ?? isPidAlive;\n mkdirSync(dirname(lockPath), { recursive: true, mode: 0o700 });\n\n for (let attempt = 0; attempt < 3; attempt += 1) {\n const owner: RegistryLockOwner = {\n pid: process.pid,\n startedAt: now,\n token: randomUUID(),\n };\n try {\n const snapshot = createLockRecord(lockPath, owner);\n if (snapshot) return createLease(lockPath, owner, snapshot);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') continue;\n throw err;\n }\n\n let stale: RegistryLockSnapshot | null = null;\n try {\n stale = getStaleLockSnapshot(lockPath, alive);\n } catch (readErr) {\n if ((readErr as NodeJS.ErrnoException).code === 'ENOENT') continue;\n throw readErr;\n }\n if (!stale) return null;\n const reaperLease = tryAcquireReaperGuard(lockPath, now, alive);\n if (!reaperLease) return null;\n try {\n let currentStale: RegistryLockSnapshot | null = null;\n try {\n currentStale = getStaleLockSnapshot(lockPath, alive);\n } catch (readErr) {\n if ((readErr as NodeJS.ErrnoException).code === 'ENOENT') continue;\n throw readErr;\n }\n if (!currentStale) return null;\n if (!removeStaleLock(lockPath, currentStale)) continue;\n } finally {\n reaperLease.release();\n }\n }\n return null;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (!signal) return new Promise((resolve) => setTimeout(resolve, ms));\n return new Promise((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal.reason);\n };\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n}\n\nfunction lockTimeoutError(\n lockPath: string,\n waitMs: number,\n alive: (pid: number) => boolean,\n): Error {\n let owner: RegistryLockOwner | null = null;\n try {\n owner = parseLockOwner(readFileSync(lockPath, 'utf8'));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n if (owner && alive(owner.pid)) {\n return new Error(\n `Timed out after ${waitMs}ms waiting for lock held by clodex process ` +\n `(pid ${owner.pid}): ${lockPath}`,\n );\n }\n return new Error(\n `Timed out after ${waitMs}ms waiting for lock: ${lockPath}`,\n );\n}\n\nexport async function withRegistryWriteLock<T>(\n operation: () => Promise<T> | T,\n options: RegistryWriteLockOptions = {},\n): Promise<T> {\n const lockPath = options.lockPath ?? getRegistryLockPath();\n const inheritedLeases = registryLockContext.getStore()?.leases;\n if (inheritedLeases?.get(lockPath)?.active) return operation();\n\n const waitMs = options.waitMs ?? DEFAULT_WAIT_MS;\n const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;\n const now = options.now ?? Date.now;\n const deadline = now() + waitMs;\n let lease: RegistryLockLease | null = null;\n\n while (!lease) {\n options.signal?.throwIfAborted();\n lease = tryAcquireRegistryLock(lockPath, {\n now,\n isAlive: options.isAlive,\n });\n if (lease) break;\n if (now() >= deadline)\n throw lockTimeoutError(lockPath, waitMs, options.isAlive ?? isPidAlive);\n await sleep(retryMs, options.signal);\n }\n\n const leases = new Map(inheritedLeases);\n leases.set(lockPath, lease);\n const context: RegistryLockContext = { leases };\n return registryLockContext.run(context, async () => {\n try {\n return await operation();\n } finally {\n lease.release();\n }\n });\n}\n\nexport function withRegistryWriteLockSync<T>(\n operation: () => T,\n options: RegistryLockOptions = {},\n): T {\n const lockPath = options.lockPath ?? getRegistryLockPath();\n const inheritedLeases = registryLockContext.getStore()?.leases;\n if (inheritedLeases?.get(lockPath)?.active) return operation();\n\n const waitMs = options.waitMs ?? DEFAULT_WAIT_MS;\n const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;\n const now = options.now ?? Date.now;\n const deadline = now() + waitMs;\n let lease: RegistryLockLease | null = null;\n\n while (!lease) {\n lease = tryAcquireRegistryLock(lockPath, {\n now,\n isAlive: options.isAlive,\n });\n if (lease) break;\n if (now() >= deadline)\n throw lockTimeoutError(lockPath, waitMs, options.isAlive ?? isPidAlive);\n sleepSync(retryMs);\n }\n\n const leases = new Map(inheritedLeases);\n leases.set(lockPath, lease);\n const context: RegistryLockContext = { leases };\n return registryLockContext.run(context, () => {\n try {\n return operation();\n } finally {\n lease.release();\n }\n });\n}\n\nexport function getCredentialMutationLockPath(authRef: string): string {\n const digest = createHash('sha256')\n .update('clodex-credential-mutation\\0')\n .update(authRef)\n .digest('hex');\n return join(getCredentialLockRoot(), `${digest}.lock`);\n}\n\nfunction getNativeCredentialRoot(): string {\n const nativeHome = userInfo().homedir;\n if (!nativeHome || !isAbsolute(nativeHome)) {\n throw new Error('Could not determine the native user home for credential coordination');\n }\n return join(nativeHome, '.clodex');\n}\n\nexport function getCredentialLockRoot(): string {\n return join(getNativeCredentialRoot(), 'credential-locks');\n}\n\nexport function getCredentialStateRoot(): string {\n return join(getNativeCredentialRoot(), 'keyring-state');\n}\n\nexport function withCredentialMutationLock<T>(\n authRef: string,\n operation: () => Promise<T> | T,\n options: Pick<RegistryLockOptions, 'waitMs' | 'retryMs'> = {},\n): Promise<T> {\n return withRegistryWriteLock(operation, {\n ...options,\n lockPath: getCredentialMutationLockPath(authRef),\n waitMs: options.waitMs ?? DEFAULT_CREDENTIAL_MUTATION_WAIT_MS,\n });\n}\n\nexport function getProviderMutationLockPath(providerSlot: string): string {\n const digest = createHash('sha256')\n .update('clodex-provider-mutation\\0')\n .update(providerSlot)\n .digest('hex');\n return `${getProvidersPath()}.provider-${digest}.lock`;\n}\n\nexport function withProviderMutationLock<T>(\n providerSlot: string,\n operation: () => Promise<T> | T,\n): Promise<T> {\n return withRegistryWriteLock(operation, {\n lockPath: getProviderMutationLockPath(providerSlot),\n });\n}\n","import type { ProviderRegistry, RegistryProvider } from './types.js';\n\n/**\n * Resolve only an account slot the registry actually owns. Account names such\n * as `constructor` are valid, so ordinary bracket lookup can otherwise return\n * an inherited Object.prototype member and turn a stale selector into data.\n */\nexport function getOAuthAccountSlot(\n provider: Pick<RegistryProvider, 'authAccounts'>,\n name: string,\n): NonNullable<RegistryProvider['authAccounts']>[string] | undefined {\n const accounts = provider.authAccounts;\n return accounts && Object.prototype.hasOwnProperty.call(accounts, name)\n ? accounts[name]\n : undefined;\n}\n\n/** The provider-owned credential, whether or not a named slot is selected. */\nexport function providerDefaultAuthRef(\n provider: Pick<RegistryProvider, 'authRef' | 'defaultAuthRef'>,\n): string {\n return provider.defaultAuthRef ?? provider.authRef;\n}\n\n/**\n * Persist one named selection in the downgrade-visible `authRef` field while\n * retaining the provider default for an exact rollback.\n */\nexport function storeActiveOAuthAccount(\n provider: RegistryProvider,\n name: string,\n selectedAuthRef: string,\n): boolean {\n const previousAuthRef = provider.authRef;\n const previousDefaultAuthRef = provider.defaultAuthRef;\n const previousDefaultModelsCache = provider.defaultModelsCache;\n const previousAccount = provider.activeAuthAccount;\n if (provider.defaultAuthRef === undefined) {\n provider.defaultAuthRef = provider.authRef;\n if (provider.modelsCache) provider.defaultModelsCache = provider.modelsCache;\n }\n provider.authRef = selectedAuthRef;\n provider.activeAuthAccount = name;\n return previousAuthRef !== provider.authRef\n || previousDefaultAuthRef !== provider.defaultAuthRef\n || previousDefaultModelsCache !== provider.defaultModelsCache\n || previousAccount !== provider.activeAuthAccount;\n}\n\n/** Restore the parked provider default and remove all persisted selection state. */\nexport function clearActiveOAuthAccount(provider: RegistryProvider): boolean {\n const previousAuthRef = provider.authRef;\n const previousDefaultAuthRef = provider.defaultAuthRef;\n const previousDefaultModelsCache = provider.defaultModelsCache;\n const previousAccount = provider.activeAuthAccount;\n const hasMaterializedSelection = provider.defaultAuthRef !== undefined;\n provider.authRef = providerDefaultAuthRef(provider);\n if (hasMaterializedSelection) {\n if (provider.defaultModelsCache) {\n provider.modelsCache = provider.defaultModelsCache;\n provider.refreshedAt = provider.defaultModelsCache.fetchedAt;\n } else {\n delete provider.modelsCache;\n delete provider.refreshedAt;\n }\n }\n delete provider.defaultAuthRef;\n delete provider.defaultModelsCache;\n delete provider.activeAuthAccount;\n return previousAuthRef !== provider.authRef\n || previousDefaultAuthRef !== provider.defaultAuthRef\n || previousDefaultModelsCache !== provider.defaultModelsCache\n || previousAccount !== provider.activeAuthAccount;\n}\n\n/**\n * Upgrade OAuth selectors written before schema v5. In v3/v4, `authRef` is\n * defined as the provider default, so it is safe to park. The top-level model\n * cache is not proof of account ownership, however: only a v4 slot cache is.\n * Never copy an ambiguous top cache into the selected slot. Project the slot's\n * proven cache when one exists, otherwise clear the top cache and fail closed\n * until that account is refreshed.\n *\n * A selector whose slot is missing is deliberately left untouched. There is\n * no selected credential to materialize, and guessing the default would turn\n * a broken selection into a silent identity fallback.\n */\nexport function migrateActiveOAuthAccountStorage(registry: ProviderRegistry): boolean {\n let changed = false;\n for (const provider of registry.providers) {\n const name = provider.activeAuthAccount?.trim();\n if (provider.authType !== 'oauth' || !name || provider.defaultAuthRef !== undefined) continue;\n const selected = getOAuthAccountSlot(provider, name);\n if (!selected) continue;\n\n provider.defaultAuthRef = provider.authRef;\n provider.authRef = selected.authRef;\n // Account-owned caches did not exist until v4. A stray cache in older\n // bytes has no schema-backed ownership provenance and must not be paired\n // with the selected credential.\n if (registry.schemaVersion >= 4 && selected.modelsCache) {\n provider.modelsCache = selected.modelsCache;\n provider.refreshedAt = selected.modelsCache.fetchedAt;\n } else {\n delete provider.modelsCache;\n delete provider.refreshedAt;\n }\n changed = true;\n }\n return changed;\n}\n","import {\n REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT,\n REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES,\n type ProviderRegistry,\n} from './types.js';\nimport { migrateActiveOAuthAccountStorage } from './oauth-account-storage.js';\n\n// Rename {id:'openai', authType:'oauth'} → {id:'openai-oauth'} so it can coexist\n// with the API-key 'openai' provider. Preserves the original authRef so the\n// keyring credential isn't orphaned.\nexport function migrateOAuthOpenAiProvider(registry: ProviderRegistry): boolean {\n if (registry.providers.some(p => p.id === 'openai-oauth')) return false;\n\n const idx = registry.providers.findIndex(\n p => p.id === 'openai' && p.authType === 'oauth',\n );\n if (idx < 0) return false;\n\n const existing = registry.providers[idx]!;\n registry.providers[idx] = {\n ...existing,\n id: 'openai-oauth',\n templateId: existing.templateId || 'openai',\n name: existing.name === 'OpenAI' ? 'OpenAI (ChatGPT)' : existing.name,\n };\n return true;\n}\n\nexport interface RegistryMigrationResult {\n changed: boolean;\n /**\n * The selected credential/cache identity was projected into downgrade-visible\n * top-level fields. Returning those bytes before they are durable would put\n * the current process and an older process on different identities.\n */\n materializedActiveAccount: boolean;\n}\n\n/** Apply every supported in-memory registry migration. */\nexport function migrateRegistry(registry: ProviderRegistry): RegistryMigrationResult {\n const renamed = migrateOAuthOpenAiProvider(registry);\n // Schema v5 is authoritative. Never infer a missing parked default there:\n // its top-level authRef is already the selected slot, not the value to park.\n const materialized = registry.schemaVersion >= REGISTRY_SCHEMA_VERSION_WITH_ACTIVE_ACCOUNT\n && registry.schemaVersion <= REGISTRY_SCHEMA_VERSION_WITH_ACCOUNT_MODEL_CACHES\n ? migrateActiveOAuthAccountStorage(registry)\n : false;\n return {\n changed: renamed || materialized,\n materializedActiveAccount: materialized,\n };\n}\n","// src/registry/validate.ts\n\n/** Stable provider slug: lowercase alphanumeric + internal hyphens. */\nexport const PROVIDER_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;\n\nexport function isValidProviderId(id: string): boolean {\n return PROVIDER_ID_PATTERN.test(id);\n}\n\nexport function slugifyProviderId(displayName: string): string {\n const base = displayName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n if (!base) return 'custom-provider';\n if (isValidProviderId(base)) return base;\n const trimmed = base.replace(/^-+|-+$/g, '');\n return isValidProviderId(trimmed) ? trimmed : `custom-${trimmed.slice(0, 40)}`;\n}\n\nexport function customProviderId(displayName: string): string {\n const slug = slugifyProviderId(displayName);\n return slug.startsWith('custom-') ? slug : `custom-${slug}`;\n}\n","import type { UserPreferences } from './types.js';\nimport { randomUUID } from 'node:crypto';\nimport { readFileSync, renameSync, unlinkSync } from 'node:fs';\nimport { getConfigPath } from './paths.js';\nimport { syncParentDirectory, writeSecureFile } from './registry/io.js';\nimport {\n assertRegistryWriteOwnership,\n withRegistryWriteLock,\n withRegistryWriteLockSync,\n} from './registry/lock.js';\n\nfunction readJsonFile(path: string): UserPreferences | null {\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8'));\n return parsed && typeof parsed === 'object' ? parsed as UserPreferences : null;\n } catch {\n return null;\n }\n}\n\nfunction readConfig(): UserPreferences {\n return readJsonFile(getConfigPath()) ?? {};\n}\n\nfunction writeConfig(config: UserPreferences): void {\n const configPath = getConfigPath();\n assertRegistryWriteOwnership(configPath);\n const payload = `${JSON.stringify(config, null, 2)}\\n`;\n const tmp = `${configPath}.${process.pid}.${randomUUID()}.tmp`;\n try {\n writeSecureFile(tmp, payload);\n assertRegistryWriteOwnership(configPath);\n renameSync(tmp, configPath);\n syncParentDirectory(configPath);\n } finally {\n try {\n unlinkSync(tmp);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n }\n}\n\nfunction updateConfig<T>(mutate: (config: UserPreferences) => T): T {\n const configPath = getConfigPath();\n return withRegistryWriteLockSync(() => {\n const config = readJsonFile(configPath) ?? {};\n const result = mutate(config);\n writeConfig(config);\n return result;\n }, { lockPath: `${configPath}.lock` });\n}\n\ninterface AsyncConfigUpdate<T> {\n result: T;\n write: boolean;\n}\n\nasync function updateConfigAsync<T>(\n mutate: (\n config: UserPreferences,\n ) => Promise<AsyncConfigUpdate<T>> | AsyncConfigUpdate<T>,\n): Promise<T> {\n const configPath = getConfigPath();\n return withRegistryWriteLock(async () => {\n const config = readJsonFile(configPath) ?? {};\n const update = await mutate(config);\n if (update.write) writeConfig(config);\n return update.result;\n }, { lockPath: `${configPath}.lock` });\n}\n\nexport function loadPreferences(): UserPreferences {\n const config = readConfig();\n return {\n lastModel: config.lastModel,\n lastProvider: config.lastProvider,\n recentModelsByProvider: config.recentModelsByProvider,\n favoriteModels: config.favoriteModels,\n modelAliases: config.modelAliases,\n modelContextModes: config.modelContextModes,\n claudeBridgeMode: config.claudeBridgeMode,\n serverBridgeMode: config.serverBridgeMode,\n appPathOverrides: config.appPathOverrides,\n localPatchesEnabled: config.localPatchesEnabled,\n recentLaunchFolders: config.recentLaunchFolders,\n server: config.server,\n };\n}\n\nexport function savePreferences(prefs: Partial<Pick<UserPreferences, 'lastModel' | 'lastProvider' | 'recentModelsByProvider' | 'favoriteModels' | 'modelAliases' | 'modelContextModes' | 'claudeBridgeMode' | 'serverBridgeMode' | 'appPathOverrides' | 'localPatchesEnabled' | 'recentLaunchFolders'>>): void {\n updateConfig(config => {\n if (prefs.lastModel !== undefined) config.lastModel = prefs.lastModel;\n if (prefs.lastProvider !== undefined) config.lastProvider = prefs.lastProvider;\n if (prefs.recentModelsByProvider !== undefined) config.recentModelsByProvider = prefs.recentModelsByProvider;\n if (prefs.favoriteModels !== undefined) config.favoriteModels = prefs.favoriteModels;\n if (prefs.modelAliases !== undefined) config.modelAliases = prefs.modelAliases;\n if (prefs.modelContextModes !== undefined) config.modelContextModes = prefs.modelContextModes;\n if (prefs.claudeBridgeMode !== undefined) config.claudeBridgeMode = prefs.claudeBridgeMode;\n if (prefs.serverBridgeMode !== undefined) config.serverBridgeMode = prefs.serverBridgeMode;\n if (prefs.appPathOverrides !== undefined) config.appPathOverrides = prefs.appPathOverrides;\n if (prefs.localPatchesEnabled !== undefined) config.localPatchesEnabled = prefs.localPatchesEnabled;\n if (prefs.recentLaunchFolders !== undefined) config.recentLaunchFolders = prefs.recentLaunchFolders;\n });\n}\n\nexport function getAppPathOverride(appId: string): string | undefined {\n const value = loadPreferences().appPathOverrides?.[appId];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nexport function setAppPathOverride(appId: string, path: string | null): Record<string, string> {\n return updateConfig(config => {\n const next = { ...(config.appPathOverrides ?? {}) };\n const trimmed = path?.trim() ?? '';\n if (trimmed) next[appId] = trimmed;\n else delete next[appId];\n config.appPathOverrides = next;\n if (Object.keys(next).length === 0) delete config.appPathOverrides;\n return next;\n });\n}\n\n/**\n * Resolve the bridge mode for a command. An explicit flag applies to that run only —\n * it is persisted as the command's default ONLY when the caller opts in (--save-mode).\n * With no flag, the saved per-command default applies; with no saved default, proxy.\n */\nexport function resolveBridgeMode(\n command: 'claude' | 'server',\n explicit: import('./types.js').BridgeMode | undefined,\n opts: { persist?: boolean } = {},\n): import('./types.js').BridgeMode {\n const key = command === 'claude' ? 'claudeBridgeMode' : 'serverBridgeMode';\n if (explicit) {\n if (opts.persist === true) savePreferences({ [key]: explicit });\n return explicit;\n }\n return loadPreferences()[key] ?? 'proxy';\n}\n\nconst MAX_RECENT_MODELS = 3;\nconst MAX_RECENT_LAUNCH_FOLDERS = 6;\n\nexport function recordLaunchFolder(folder: string): string[] {\n const trimmed = folder.trim();\n if (!trimmed) return loadPreferences().recentLaunchFolders ?? [];\n return updateConfig(config => {\n const prev = config.recentLaunchFolders ?? [];\n const next = [trimmed, ...prev.filter(path => path !== trimmed)].slice(0, MAX_RECENT_LAUNCH_FOLDERS);\n config.recentLaunchFolders = next;\n return next;\n });\n}\n\nexport function recordLaunchSelection(\n _agent: 'claude',\n providerId: string,\n modelId: string,\n prefs: UserPreferences,\n): void {\n const prevRecent = prefs.recentModelsByProvider?.[providerId] ?? [];\n const updatedRecent = [modelId, ...prevRecent.filter(id => id !== modelId)].slice(0, MAX_RECENT_MODELS);\n savePreferences({\n lastProvider: providerId,\n lastModel: modelId,\n recentModelsByProvider: { ...prefs.recentModelsByProvider, [providerId]: updatedRecent },\n });\n}\n\nconst SERVER_PASSWORD_SERVICE = 'clodex-server-password';\nconst SERVER_PASSWORD_ACCOUNT = 'server-password';\n\nasync function getServerPasswordKeyring(): Promise<any | null> {\n try {\n const { Entry } = await import('@napi-rs/keyring');\n return new Entry(SERVER_PASSWORD_SERVICE, SERVER_PASSWORD_ACCOUNT);\n } catch {\n return null;\n }\n}\n\nexport async function getSavedServerPassword(): Promise<string | null> {\n const keyring = await getServerPasswordKeyring();\n if (!keyring) return readConfig().server?.savedPassword ?? null;\n\n const savedPassword = await updateConfigAsync(async config => {\n const server = config.server;\n const password = server?.savedPassword;\n if (!password) return { result: null, write: false };\n try {\n await keyring.setPassword(password);\n delete server.savedPassword;\n if (Object.keys(server).length === 0) delete config.server;\n return { result: password, write: true };\n } catch {\n // Fallback: keep in config.json if keyring fails\n return { result: password, write: false };\n }\n });\n if (savedPassword) return savedPassword;\n\n try {\n return await keyring.getPassword();\n } catch {\n return null;\n }\n}\n\nexport async function setSavedServerPassword(password: string): Promise<void> {\n const keyring = await getServerPasswordKeyring();\n if (keyring) {\n try {\n await keyring.setPassword(password);\n return;\n } catch {\n // Fallback\n }\n }\n await updateConfigAsync(config => {\n config.server = {\n ...(config.server ?? {}),\n savedPassword: password,\n };\n return { result: undefined, write: true };\n });\n}\n\nexport async function clearSavedServerPassword(): Promise<void> {\n const keyring = await getServerPasswordKeyring();\n if (keyring) {\n try {\n await keyring.deletePassword();\n } catch {\n // Ignore\n }\n }\n await updateConfigAsync(config => {\n if (!config.server) return { result: undefined, write: false };\n delete config.server.savedPassword;\n if (Object.keys(config.server).length === 0) delete config.server;\n return { result: undefined, write: true };\n });\n}\n\nexport function getServerExposedProviders(): string[] | null {\n const list = readConfig().server?.exposedProviders;\n return list && list.length > 0 ? list : null;\n}\n\nexport function setServerExposedProviders(providerIds: string[]): void {\n updateConfig(config => {\n config.server = {\n ...(config.server ?? {}),\n exposedProviders: providerIds,\n };\n });\n}\n\nexport function getServerMaskGatewayIds(): boolean {\n return readConfig().server?.maskGatewayIds ?? true;\n}\n\nexport function setServerMaskGatewayIds(mask: boolean): void {\n updateConfig(config => {\n config.server = {\n ...(config.server ?? {}),\n maskGatewayIds: mask,\n };\n });\n}\n\nexport function getServerFavoritesOnly(): boolean {\n return readConfig().server?.favoritesOnly ?? false;\n}\n\nexport function setServerFavoritesOnly(favoritesOnly: boolean): void {\n updateConfig(config => {\n config.server = {\n ...(config.server ?? {}),\n favoritesOnly,\n };\n });\n}\n\nexport function getServerListenMode(): 'local' | 'network' {\n return readConfig().server?.listenMode === 'network' ? 'network' : 'local';\n}\n\nexport function setServerListenMode(listenMode: 'local' | 'network'): void {\n updateConfig(config => {\n config.server = {\n ...(config.server ?? {}),\n listenMode,\n };\n });\n}\n","// src/claude-binary.ts\n//\n// Claude Code binary discovery and version probing, kept OUT of launch.ts so the\n// `clodex-claude` wrapper can import discovery without pulling in launchClaude\n// and everything the launch path needs. The wrapper runs for every spawned agent\n// process, so its import graph is an invariant (see CLAUDE.md), and tsup places\n// anything both entry points touch in the shared chunk they both load.\nimport { execFileSync, execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { getAppPathOverride } from './config.js';\nimport { findBinaryOnPath } from './binary-lookup.js';\n\nconst isWindows = process.platform === 'win32';\n\nconst FALLBACK_PATHS = isWindows\n ? [\n join(process.env['APPDATA'] ?? homedir(), 'npm', 'claude.cmd'),\n join(process.env['APPDATA'] ?? homedir(), 'npm', 'claude'),\n join(homedir(), 'AppData', 'Roaming', 'npm', 'claude.cmd'),\n ]\n : [\n join(homedir(), '.local', 'bin', 'claude'),\n join(homedir(), '.npm', 'bin', 'claude'),\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n\nexport function findClaudeBinary(): string | null {\n const environmentOverride = process.env['CLODEX_CLAUDE_PATH'];\n if (environmentOverride?.trim()) {\n return existsSync(environmentOverride) ? environmentOverride : null;\n }\n\n const override = getAppPathOverride('claude');\n if (override) return existsSync(override) ? override : null;\n\n return findBinaryOnPath('claude', FALLBACK_PATHS);\n}\n\n/** Version reported when the installed claude cannot be probed. */\nconst FALLBACK_CLAUDE_VERSION = '2.1.183';\n\nconst VERSION_PROBE_TIMEOUT_MS = 15_000;\n\n/**\n * Probe `--version` of ONE SPECIFIC claude binary, returning null when it cannot\n * be executed or prints nothing version-shaped.\n *\n * Callers that key destructive state on the answer — the patcher names its\n * pristine backups after this version and restores them over the live install —\n * MUST use this and fail loudly on null. A guessed version tags a backup with\n * bytes it does not contain, and restoring it downgrades the user's Claude Code.\n */\nexport function getClaudeVersionForBinary(binaryPath: string): string | null {\n try {\n // A JavaScript entry point — an npm install's `cli.js` — is not an\n // executable. POSIX ran it anyway via its `#!` line, but only while the file\n // kept its executable bit, and Windows cannot run it at all (cmd would hand\n // a `.js` to the Windows Script Host). Run it with the Node already running\n // clodex instead; the version still comes from THIS exact file, which is the\n // invariant that matters.\n // POSIX: exec the file directly so a path containing spaces still works.\n // Windows: `claude` is often a .cmd shim, which needs a shell — keep the\n // quoted shell invocation there.\n const result = /\\.[cm]?js$/i.test(binaryPath)\n ? execFileSync(process.execPath, [binaryPath, '--version'], {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout: VERSION_PROBE_TIMEOUT_MS,\n })\n : isWindows\n ? execSync(`\"${binaryPath}\" --version`, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout: VERSION_PROBE_TIMEOUT_MS,\n })\n : execFileSync(binaryPath, ['--version'], {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout: VERSION_PROBE_TIMEOUT_MS,\n });\n return result.match(/(\\d+\\.\\d+\\.\\d+)/)?.[1] ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Version of the claude found on PATH (or via the configured overrides), with a\n * known-good fallback. This is a best-effort string for request metadata — it is\n * NOT the version of any particular file, because `findClaudeBinary()` can\n * return a wrapper shim that differs from the real installation.\n */\nexport function getInstalledClaudeVersion(): string {\n const claudePath = findClaudeBinary();\n if (!claudePath) return FALLBACK_CLAUDE_VERSION;\n return getClaudeVersionForBinary(claudePath) ?? FALLBACK_CLAUDE_VERSION;\n}\n","import { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\n\nexport interface FindBinaryOnPathOptions {\n verifyWhichResult?: boolean;\n isWindows?: boolean;\n exists?: (path: string) => boolean;\n runWhich?: (name: string, isWindows: boolean) => string;\n}\n\nexport function findBinaryOnPath(\n name: string,\n fallbackPaths: string[],\n options: FindBinaryOnPathOptions = {},\n): string | null {\n const isWindows = options.isWindows ?? process.platform === 'win32';\n const exists = options.exists ?? existsSync;\n // argv form, never a shell string — the binary name must not be shell-interpretable\n // (defense-in-depth originally added in d887984, must survive refactors).\n const runWhich = options.runWhich ?? ((binary, win) =>\n execFileSync(win ? 'where.exe' : 'which', [binary], {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }));\n\n try {\n const lines = runWhich(name, isWindows)\n .trim()\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n const path = (isWindows ? lines.find(line => line.toLowerCase().endsWith('.cmd')) : null)\n ?? lines[0];\n if (path && (!options.verifyWhichResult || exists(path))) return path;\n } catch {\n // Fall through to fallback paths.\n }\n\n for (const path of fallbackPaths) {\n if (exists(path)) return path;\n }\n return null;\n}\n","import { connect, type AddressInfo, type Server } from 'node:net';\nimport { setTimeout as delay } from 'node:timers/promises';\n\nconst LISTENER_READY_TIMEOUT_MS = 1_000;\nconst LISTENER_READY_RETRY_MS = 5;\nconst TCP_PROBE_TIMEOUT_MS = 50;\n\nfunction connectHost(address: string): string {\n if (address === '0.0.0.0') return '127.0.0.1';\n if (address === '::') return '::1';\n return address;\n}\n\n/** Return a reachable host formatted for use in an HTTP URL. */\nexport function tcpListenerUrlHost(address: string): string {\n const host = connectHost(address);\n return host.includes(':') ? `[${host}]` : host;\n}\n\ntype TcpListenerProbeResult = 'ready' | 'timeout' | 'unreachable';\n\nfunction probeTcpListener(\n host: string,\n port: number,\n timeoutMs: number,\n): Promise<TcpListenerProbeResult> {\n return new Promise(resolve => {\n const socket = connect({ host, port });\n let settled = false;\n const finish = (result: TcpListenerProbeResult) => {\n if (settled) return;\n settled = true;\n socket.destroy();\n resolve(result);\n };\n socket.once('connect', () => finish('ready'));\n socket.once('error', error => {\n finish(\n (error as NodeJS.ErrnoException).code === 'ETIMEDOUT'\n ? 'timeout'\n : 'unreachable',\n );\n });\n socket.setTimeout(timeoutMs, () => finish('timeout'));\n });\n}\n\ninterface TcpListenerWaitOptions {\n now?: () => number;\n probe?: (\n host: string,\n port: number,\n timeoutMs: number,\n ) => Promise<TcpListenerProbeResult>;\n retryFailure?: (result: Exclude<TcpListenerProbeResult, 'ready'>) => boolean;\n delay?: (ms: number) => Promise<void>;\n}\n\n/**\n * Probe every candidate once per round and return the first reachable\n * candidate in caller-provided priority order. All retry rounds share one\n * overall deadline.\n */\nexport async function waitForTcpListenerCandidate<T extends { port: number }>(\n host: string,\n candidates: readonly T[],\n timeoutMs = LISTENER_READY_TIMEOUT_MS,\n options: TcpListenerWaitOptions = {},\n): Promise<T | null> {\n if (candidates.length === 0) return null;\n\n const now = options.now ?? Date.now;\n const probe = options.probe ?? probeTcpListener;\n const retryFailure = options.retryFailure ?? (() => true);\n const wait = options.delay ?? (ms => delay(ms));\n const deadline = now() + timeoutMs;\n let pendingCandidates = [...candidates];\n\n do {\n const remaining = Math.max(1, deadline - now());\n const results = await Promise.all(\n pendingCandidates.map(candidate => probe(\n host,\n candidate.port,\n Math.min(remaining, TCP_PROBE_TIMEOUT_MS),\n )),\n );\n const readyIndex = results.findIndex(result => result === 'ready');\n if (readyIndex >= 0) return pendingCandidates[readyIndex] ?? null;\n\n pendingCandidates = pendingCandidates.filter((_candidate, index) => {\n const result = results[index];\n return result !== undefined && result !== 'ready' && retryFailure(result);\n });\n if (pendingCandidates.length === 0) return null;\n\n const retryDelay = Math.min(LISTENER_READY_RETRY_MS, deadline - now());\n if (retryDelay <= 0) return null;\n await wait(retryDelay);\n } while (now() < deadline);\n\n return null;\n}\n\n/** Retry a TCP probe until the listener answers or the deadline expires. */\nexport async function waitForTcpListener(\n host: string,\n port: number,\n timeoutMs = LISTENER_READY_TIMEOUT_MS,\n options: TcpListenerWaitOptions = {},\n): Promise<boolean> {\n return (await waitForTcpListenerCandidate(host, [{ port }], timeoutMs, options)) !== null;\n}\n\nasync function closeAfterReadinessFailure(server: Server): Promise<void> {\n if (!server.listening) return;\n await new Promise<void>(resolve => server.close(() => resolve()));\n}\n\n/** Bind a TCP server and wait until the bound socket accepts connections. */\nexport async function listenTcpServer(\n server: Server,\n port: number,\n host: string,\n): Promise<AddressInfo> {\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => server.off('error', onError);\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n server.once('error', onError);\n try {\n server.listen(port, host, () => {\n cleanup();\n resolve();\n });\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n\n const address = server.address();\n if (!address || typeof address === 'string') {\n await closeAfterReadinessFailure(server);\n throw new Error('TCP server did not bind to a network address');\n }\n\n const probeHost = connectHost(address.address);\n if (await waitForTcpListener(probeHost, address.port)) return address;\n\n await closeAfterReadinessFailure(server);\n throw new Error(\n `TCP listener did not become reachable within ${LISTENER_READY_TIMEOUT_MS}ms: `\n + `${probeHost}:${address.port}`,\n );\n}\n","// src/parent-notice.ts\nimport { writeSync } from 'node:fs';\n\n/**\n * The one channel bounded parent-side diagnostics use to reach the terminal.\n *\n * `launchClaude` replaces `process.stdout.write`/`process.stderr.write` for the\n * entire lifetime of the spawned Claude Code, because the child inherits the\n * terminal and a stray parent write corrupts its TUI. clodex's gateway/MITM,\n * however, runs in that same process and handles every translated request while\n * the child is alive — so the few warnings that are documented as reaching\n * stderr (the reasoning- and tool-argument normalization canaries, retry and\n * timeout configuration notices) were written straight into the mute and were never seen.\n *\n * This channel is deliberately OPT-IN and enumerable: a caller must import\n * `emitParentNotice` to reach the terminal. There is no prefix sniffing or other\n * content-based rule, because a prefix is not a safety boundary — diagnostic\n * data gets interpolated into messages, and a future writer would silently\n * inherit the bypass. What can reach the terminal is exactly the set of\n * `emitParentNotice` call sites.\n *\n * It does NOT interleave with the child. A notice raised while Claude Code owns\n * the terminal is handed to the sink `launchClaude` installs, which queues it\n * and flushes after the child exits; painting it live lands wherever the child\n * left the shared cursor — mid-frame, or right after the prompt where it reads\n * as typed input (both observed against real Claude Code). Deferred visibility\n * is the point; the `--debug-file` copy remains immediate.\n *\n * Notices must stay bounded, low-volume, and single-line: every call site is\n * already deduplicated and hard-capped by its own caller, and this module\n * additionally forces one trailing newline, strips C0/C1 control characters (so\n * no escape sequence can ever repaint anything), and truncates.\n */\n\nexport type ParentNoticeSink = (line: string) => void;\n\n/** Longer than every notice clodex emits; a guard, not a budget. */\nconst MAX_NOTICE_CHARS = 2000;\n\n/**\n * C0 controls, DEL, and the C1 range. C1 matters because `U+009B`/`U+009C`/\n * `U+009D` are the 8-bit CSI/ST/OSC introducers: a terminal that honors them\n * would act on an escape sequence even with every ESC byte removed, and some\n * notice fields (a tool name, a differing property name) originate upstream.\n */\nconst CONTROL_CHARS = /[\\u0000-\\u001f\\u007f-\\u009f]/g;\n\nlet activeSink: ParentNoticeSink | null = null;\nlet stderrErrorGuardInstalled = false;\n\n/** Normalizes a notice into exactly one printable, newline-terminated line. */\nfunction toNoticeLine(message: string): string {\n const flattened = message.replace(CONTROL_CHARS, ' ').trimEnd();\n const bounded = flattened.length > MAX_NOTICE_CHARS\n ? `${flattened.slice(0, MAX_NOTICE_CHARS)}...`\n : flattened;\n return `${bounded}\\n`;\n}\n\n/**\n * Makes a failed stderr write survivable.\n *\n * A closed pipe — `clodex claude 2>&1 | head -n 1` and every other truncating\n * log consumer — reports EPIPE ASYNCHRONOUSLY through the stream's `error`\n * event, after `write()` has already returned, so a synchronous `try/catch`\n * around the call cannot contain it. With no listener Node terminates the\n * process, which on the launch path would tear the in-process gateway away from\n * a still-running Claude Code. A diagnostic must never do that.\n */\nfunction guardStderrErrors(): void {\n if (stderrErrorGuardInstalled) return;\n stderrErrorGuardInstalled = true;\n try {\n // Installed only once clodex actually writes a notice, and never removed:\n // the failure it absorbs (EPIPE/EBADF on a stream clodex does not own) is\n // one no writer here can act on anyway.\n process.stderr.on('error', () => { /* swallowed — see above */ });\n } catch { /* a diagnostic must never break a request */ }\n}\n\n/**\n * Writes already-normalized lines to the terminal. Safe to call with an empty\n * list, and safe to call when stderr is a closed pipe.\n */\nexport function writeParentNoticeLines(lines: readonly string[]): void {\n if (lines.length === 0) return;\n guardStderrErrors();\n try {\n // The write callback and the stream `error` listener both absorb the same\n // failure; which one Node reports through depends on when the peer closed.\n process.stderr.write(lines.join(''), () => { /* swallowed */ });\n } catch { /* a diagnostic must never break a request */ }\n}\n\n/**\n * Last-resort flush for a `process.on('exit')` handler, where the event loop is\n * already done and an asynchronous stream write would never be delivered.\n * `writeSync` reports a closed pipe synchronously, so the catch is sufficient.\n */\nexport function writeParentNoticeLinesSync(lines: readonly string[]): void {\n if (lines.length === 0) return;\n try {\n writeSync(2, lines.join(''));\n } catch { /* EPIPE/EBADF/EAGAIN on a dying process — nothing to do */ }\n}\n\n/**\n * Raises one bounded diagnostic for the terminal that started clodex.\n *\n * With no sink installed this is an ordinary `process.stderr.write`, resolved at\n * call time so the usual test spies still observe it. While `launchClaude` holds\n * the terminal for Claude Code, the installed sink takes the line instead and\n * decides when it is safe to paint.\n *\n * A diagnostic must never break a request, so every failure is swallowed.\n */\nexport function emitParentNotice(message: string): void {\n const line = toNoticeLine(message);\n try {\n if (activeSink) activeSink(line);\n else writeParentNoticeLines([line]);\n } catch { /* a notice must never break a request */ }\n}\n\n/**\n * Routes notices to `sink` instead of the terminal until the returned release\n * function is called. Used by `launchClaude`, which owns the mute and the\n * terminal-safety decision this channel defers to.\n */\nexport function installParentNoticeSink(sink: ParentNoticeSink): () => void {\n const previous = activeSink;\n activeSink = sink;\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (activeSink === sink) activeSink = previous;\n };\n}\n","// src/server-runtime.ts\n//\n// Runtime-state advertisement for the standalone `clodex server` command.\n// Each registering server ADDS its own record (keyed by pid) to\n// ~/.clodex/server-runtime.json on startup and removes ONLY its own record on\n// graceful shutdown, so other processes (notably the `clodex-claude` wrapper\n// bin) can discover every running server's mode, port, and CA path without any\n// hardcoding. The file holds an ARRAY of records; the legacy single-object\n// shape (pre multi-server) is tolerated on read as a one-element list. Stale\n// detection is the READER's job: a crashed server leaves its record behind, so\n// readers must validate pid liveness before trusting it. Writers additionally\n// prune dead-pid records while they hold the write lock.\n//\n// Concurrency: read-modify-write cycles are serialized by a short-lived pid\n// lock (~/.clodex/server-runtime.lock — same pattern as the patcher's\n// patch.lock: O_EXCL create, pid + staleness, ESRCH liveness) and the file is\n// replaced via write-temp-then-rename so a reader never sees a torn write. A\n// crashed lock holder cannot deadlock registration: the lock goes stale after\n// 10 seconds or when its pid dies, and after a brief bounded wait a writer\n// proceeds lockless (best-effort — same exposure as the old single-slot write).\n//\n// NOTE: only the standalone `clodex server` command writes this file. The\n// per-session MITM proxy spawned by `clodex claude --proxy` is private to that\n// session and must NOT advertise itself here. `clodex server --no-discovery`\n// (or CLODEX_NO_DISCOVERY=1) also opts a server out of registration entirely.\n\nimport {\n closeSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n rmSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getAppHome } from './paths.js';\n\nexport interface ServerRuntimeState {\n mode: 'endpoint' | 'proxy';\n port: number;\n pid: number;\n /** Proxy mode only: absolute path to the CA bundle a client must trust. */\n caPath?: string;\n startedAt: string;\n}\n\ninterface HomeEnv {\n HOME?: string;\n CLODEX_HOME?: string;\n USERPROFILE?: string;\n}\n\nexport function getServerRuntimePath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'server-runtime.json');\n}\n\nexport function getServerRuntimeLockPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'server-runtime.lock');\n}\n\n/** `--no-discovery` flag, with CLODEX_NO_DISCOVERY=1 as the env fallback. */\nexport function isDiscoveryDisabled(\n flag: boolean | undefined,\n env: { CLODEX_NO_DISCOVERY?: string } = process.env,\n): boolean {\n if (flag !== undefined) return flag;\n const raw = env.CLODEX_NO_DISCOVERY?.trim().toLowerCase();\n return raw === '1' || raw === 'true';\n}\n\nfunction isPort(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 65535;\n}\n\n/** Validate one runtime record. Returns null for anything malformed. */\nexport function parseServerRuntimeRecord(value: unknown): ServerRuntimeState | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const record = value as Record<string, unknown>;\n\n const mode = record['mode'];\n if (mode !== 'endpoint' && mode !== 'proxy') return null;\n if (!isPort(record['port'])) return null;\n const pid = record['pid'];\n if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0) return null;\n const startedAt = typeof record['startedAt'] === 'string' ? record['startedAt'] : '';\n\n const caPath = record['caPath'];\n if (mode === 'proxy') {\n // A proxy-mode server without a CA path is unusable to clients — treat as invalid.\n if (typeof caPath !== 'string' || !caPath.trim()) return null;\n return { mode, port: record['port'], pid, caPath, startedAt };\n }\n return { mode, port: record['port'], pid, startedAt };\n}\n\n/**\n * Parse a raw server-runtime.json payload into a list of records. Tolerates\n * BOTH shapes: the current array of records and the legacy single object\n * (wrapped as a one-element list). Malformed input or records are skipped —\n * never throws.\n */\nexport function parseServerRuntimeStates(raw: string): ServerRuntimeState[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return [];\n }\n const items = Array.isArray(parsed) ? parsed : [parsed];\n const states: ServerRuntimeState[] = [];\n for (const item of items) {\n const state = parseServerRuntimeRecord(item);\n if (state) states.push(state);\n }\n return states;\n}\n\n/** kill(pid, 0) liveness probe: EPERM still means the process exists. */\nexport function isPidAlive(\n pid: number,\n kill: (pid: number, signal: number) => unknown = process.kill.bind(process),\n): boolean {\n try {\n kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException)?.code === 'EPERM';\n }\n}\n\n// ── Write lock (pid + staleness, patcher pattern) ───────────────────────────\n\nconst RUNTIME_LOCK_STALE_MS = 10_000;\nconst RUNTIME_LOCK_WAIT_MS = 500;\nconst RUNTIME_LOCK_RETRY_MS = 25;\n\ninterface RuntimeLockContent {\n pid: number;\n startedAt: number;\n}\n\nfunction tryAcquireRuntimeLock(\n lockPath: string,\n opts: { now?: number; isAlive?: (pid: number) => boolean } = {},\n): (() => void) | null {\n const now = opts.now ?? Date.now();\n const alive = opts.isAlive ?? isPidAlive;\n mkdirSync(dirname(lockPath), { recursive: true, mode: 0o700 });\n\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const fd = openSync(lockPath, 'wx');\n const content: RuntimeLockContent = { pid: process.pid, startedAt: now };\n writeFileSync(fd, JSON.stringify(content));\n closeSync(fd);\n return () => {\n try {\n unlinkSync(lockPath);\n } catch {\n // already gone\n }\n };\n } catch {\n // Lock exists — check staleness.\n let stale = false;\n try {\n const existing = JSON.parse(readFileSync(lockPath, 'utf8')) as RuntimeLockContent;\n stale = !existing.pid\n || !alive(existing.pid)\n || (typeof existing.startedAt === 'number' && now - existing.startedAt > RUNTIME_LOCK_STALE_MS);\n } catch {\n stale = true; // unreadable lock file → stale\n }\n if (!stale) return null;\n try {\n unlinkSync(lockPath);\n } catch {\n // raced with the owner's cleanup — retry loop handles it\n }\n }\n }\n return null;\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n}\n\n/**\n * Run a read-modify-write mutation under the runtime lock. The lock is only\n * ever held for a few milliseconds, so after a short bounded wait the mutation\n * proceeds WITHOUT the lock rather than dropping a registration — the atomic\n * rename still prevents torn files; the worst case is a lost concurrent\n * update, which is no worse than the old single-slot behavior.\n */\nfunction withRuntimeWriteLock(env: HomeEnv, mutate: () => void): void {\n const lockPath = getServerRuntimeLockPath(env);\n let release: (() => void) | null = null;\n const deadline = Date.now() + RUNTIME_LOCK_WAIT_MS;\n for (;;) {\n release = tryAcquireRuntimeLock(lockPath);\n if (release || Date.now() >= deadline) break;\n sleepSync(RUNTIME_LOCK_RETRY_MS);\n }\n try {\n mutate();\n } finally {\n release?.();\n }\n}\n\nfunction readAllRecords(env: HomeEnv): ServerRuntimeState[] {\n let raw: string;\n try {\n raw = readFileSync(getServerRuntimePath(env), 'utf8');\n } catch {\n return [];\n }\n return parseServerRuntimeStates(raw);\n}\n\n/** Atomic replace: write a temp file in the same directory, then rename over. */\nfunction atomicWriteRecords(path: string, records: ServerRuntimeState[]): void {\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n const tmpPath = `${path}.${process.pid}.tmp`;\n writeFileSync(tmpPath, `${JSON.stringify(records, null, 2)}\\n`, { encoding: 'utf8', mode: 0o600 });\n renameSync(tmpPath, path);\n}\n\nexport interface RuntimeMutateOptions {\n isAlive?: (pid: number) => boolean;\n}\n\n/**\n * Add or update this server's own record (keyed by pid), pruning records whose\n * pids are dead. Best-effort — a state-file failure must never take the server\n * down.\n */\nexport function registerServerRuntimeState(\n state: ServerRuntimeState,\n env: HomeEnv = process.env,\n options: RuntimeMutateOptions = {},\n): void {\n const alive = options.isAlive ?? isPidAlive;\n try {\n withRuntimeWriteLock(env, () => {\n const records = readAllRecords(env).filter(\n record => record.pid !== state.pid && alive(record.pid),\n );\n records.push(state);\n atomicWriteRecords(getServerRuntimePath(env), records);\n });\n } catch {\n // Discovery is optional; the server itself keeps running.\n }\n}\n\n/**\n * Remove ONLY this server's own record (by pid) on graceful shutdown, pruning\n * dead-pid records along the way. Missing file/record is fine. When no live\n * records remain the file is removed entirely.\n */\nexport function unregisterServerRuntimeState(\n pid: number = process.pid,\n env: HomeEnv = process.env,\n options: RuntimeMutateOptions = {},\n): void {\n const alive = options.isAlive ?? isPidAlive;\n try {\n withRuntimeWriteLock(env, () => {\n const records = readAllRecords(env).filter(\n record => record.pid !== pid && alive(record.pid),\n );\n if (records.length === 0) {\n rmSync(getServerRuntimePath(env), { force: true });\n } else {\n atomicWriteRecords(getServerRuntimePath(env), records);\n }\n });\n } catch {\n // Stale records are handled by readers via pid liveness.\n }\n}\n\nexport interface ReadServerRuntimeOptions {\n isAlive?: (pid: number) => boolean;\n}\n\n/**\n * Read every advertised server record whose process is still alive. Missing or\n * malformed files yield an empty list. Read-only: stale records are ignored\n * here and physically pruned on the next registration/unregistration.\n */\nexport function readLiveServerRuntimeStates(\n env: HomeEnv = process.env,\n options: ReadServerRuntimeOptions = {},\n): ServerRuntimeState[] {\n const alive = options.isAlive ?? isPidAlive;\n return readAllRecords(env).filter(state => alive(state.pid));\n}\n\n/**\n * Wrapper selection policy: order candidate servers by preference —\n * 1. proxy mode before endpoint mode (bridging through the MITM proxy keeps\n * Claude Code's own Anthropic auth, the recommended setup);\n * 2. within a mode, newest startedAt first.\n * If only an endpoint server is live it is used; with no live server the\n * wrapper launches claude untouched (both handled by the caller).\n */\nexport function orderWrapperServerCandidates(records: ServerRuntimeState[]): ServerRuntimeState[] {\n return [...records].sort((a, b) => {\n if (a.mode !== b.mode) return a.mode === 'proxy' ? -1 : 1;\n return (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0);\n });\n}\n\n/**\n * Read the single preferred live server (selection policy above), or null when\n * none is advertised/alive.\n */\nexport function readLiveServerRuntimeState(\n env: HomeEnv = process.env,\n options: ReadServerRuntimeOptions = {},\n): ServerRuntimeState | null {\n return orderWrapperServerCandidates(readLiveServerRuntimeStates(env, options))[0] ?? null;\n}\n","export const PROXY_ENV_VARS = [\n 'HTTPS_PROXY',\n 'HTTP_PROXY',\n 'https_proxy',\n 'http_proxy',\n] as const;\n\nexport const CHILD_NETWORK_ENV_VARS = [\n ...PROXY_ENV_VARS,\n 'NO_PROXY',\n 'no_proxy',\n 'NODE_EXTRA_CA_CERTS',\n] as const;\n\nexport const NETWORK_ENV_CONTRACT_VAR = 'CLAUDE_CODE_CLODEX_NETWORK_ENV';\n\ntype ChildNetworkEnvVar = typeof CHILD_NETWORK_ENV_VARS[number];\ntype NetworkEnvValue = string | null;\n\ninterface NetworkEnvContract {\n version: 1;\n original: Partial<Record<ChildNetworkEnvVar, NetworkEnvValue>>;\n injected: Partial<Record<ChildNetworkEnvVar, NetworkEnvValue>>;\n}\n\nconst childNetworkEnvVarSet = new Set<string>(CHILD_NETWORK_ENV_VARS);\n\nfunction networkEnvValue(env: NodeJS.ProcessEnv, name: ChildNetworkEnvVar): NetworkEnvValue {\n return typeof env[name] === 'string' ? env[name]! : null;\n}\n\nfunction isNetworkValueRecord(value: unknown): value is Record<string, NetworkEnvValue> {\n return Boolean(value && typeof value === 'object' && !Array.isArray(value)\n && Object.entries(value).every(([key, entry]) =>\n childNetworkEnvVarSet.has(key) && (typeof entry === 'string' || entry === null)));\n}\n\nfunction parseNetworkEnvContract(value: string | undefined): NetworkEnvContract | undefined {\n if (value === undefined) return undefined;\n try {\n const parsed: unknown = JSON.parse(value);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined;\n const candidate = parsed as Partial<NetworkEnvContract>;\n const original = candidate.original;\n const injected = candidate.injected;\n if (candidate.version !== 1\n || !isNetworkValueRecord(original)\n || !isNetworkValueRecord(injected)) {\n return undefined;\n }\n if (!Object.keys(original).every(key => key in injected)\n || !Object.keys(injected).every(key => key in original)) {\n return undefined;\n }\n return { version: 1, original, injected };\n } catch {\n return undefined;\n }\n}\n\nfunction setNetworkEnvValue(\n env: NodeJS.ProcessEnv,\n name: ChildNetworkEnvVar,\n value: NetworkEnvValue,\n): void {\n if (value === null) delete env[name];\n else env[name] = value;\n}\n\n/**\n * Recover the external network environment only where the inherited values\n * still equal a prior Clodex injection. Values changed by settings or another\n * wrapper layer remain authoritative.\n */\nexport function networkEnvBaseline(baseEnv: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...baseEnv };\n const contract = parseNetworkEnvContract(baseEnv[NETWORK_ENV_CONTRACT_VAR]);\n delete env[NETWORK_ENV_CONTRACT_VAR];\n if (!contract) return env;\n\n for (const name of CHILD_NETWORK_ENV_VARS) {\n if (!(name in contract.original) || !(name in contract.injected)) continue;\n if (networkEnvValue(baseEnv, name) !== contract.injected[name]) continue;\n setNetworkEnvValue(env, name, contract.original[name] ?? null);\n }\n return env;\n}\n\n/**\n * Attach a compare-before-revert contract for the network values changed\n * between an external baseline and the child environment Clodex will launch.\n */\nexport function recordNetworkEnvMutation(\n baseline: NodeJS.ProcessEnv,\n injectedEnv: NodeJS.ProcessEnv,\n): void {\n const original: NetworkEnvContract['original'] = {};\n const injected: NetworkEnvContract['injected'] = {};\n for (const name of CHILD_NETWORK_ENV_VARS) {\n const before = networkEnvValue(baseline, name);\n const after = networkEnvValue(injectedEnv, name);\n if (before === after) continue;\n original[name] = before;\n injected[name] = after;\n }\n if (Object.keys(original).length === 0) {\n delete injectedEnv[NETWORK_ENV_CONTRACT_VAR];\n return;\n }\n injectedEnv[NETWORK_ENV_CONTRACT_VAR] = JSON.stringify({\n version: 1,\n original,\n injected,\n } satisfies NetworkEnvContract);\n}\n","// src/wrapper-env.ts\n//\n// Pure env computation for the `clodex-claude` wrapper bin. Given the process\n// env and a live `clodex server` runtime state (or null), returns the env to\n// launch the Claude Code binary with. Kept dependency-free so the wrapper\n// stays tiny and fast — it runs for every Claude-Code-spawned agent process.\n\nimport type { ServerRuntimeState } from './server-runtime.js';\nimport {\n networkEnvBaseline,\n PROXY_ENV_VARS,\n recordNetworkEnvMutation,\n} from './network-env.js';\n\nexport const REQUIRE_SERVER_ENV = 'CLODEX_REQUIRE_SERVER';\n\n/**\n * Whether the wrapper's spawn fallback needs a shell for `target` on `platform`.\n *\n * Only Windows, and only for `.cmd`/`.bat` launcher scripts: Node refuses to spawn those without\n * a shell (the `spawn EINVAL` class), and cmd.exe is their interpreter. A native executable — the\n * `claude.exe` the VS Code extension hands the wrapper, or the npm package's own `bin/claude.exe` —\n * must be spawned directly, because a shell hop through cmd.exe rewrites the arguments Claude Code\n * is given (`%VAR%` expansion, quote and metacharacter handling), and the extension passes JSON,\n * quoted and empty arguments that have to arrive untouched.\n *\n * npm's other two Windows shims are out of reach either way: the `.ps1` needs PowerShell and the\n * extensionless one is a POSIX shell script, and Node can spawn neither on Windows. A\n * `CLODEX_CLAUDE_PATH` naming one of those fails at spawn; clodex's own Windows discovery selects\n * the `.cmd`.\n */\nexport function wrapperSpawnShell(platform: NodeJS.Platform, target: string): boolean {\n return platform === 'win32' && /\\.(cmd|bat)$/i.test(target);\n}\n\nexport function removeAnthropicProxyBypass(env: NodeJS.ProcessEnv): void {\n const noProxyValues = [env['NO_PROXY'], env['no_proxy']]\n .filter((value): value is string => value !== undefined);\n if (noProxyValues.length === 0) return;\n\n const filtered = [...new Set(noProxyValues\n .flatMap(value => value.split(','))\n .map(value => value.trim())\n .filter(Boolean)\n .filter(value => {\n const entry = value.toLowerCase().replace(/^https?:\\/\\//, '');\n const host = entry.replace(/:\\d+$/, '');\n if (host === '*') return false;\n const suffix = host.startsWith('*.') ? host.slice(1) : host;\n const bypassesAnthropic = suffix.startsWith('.')\n ? 'api.anthropic.com'.endsWith(suffix)\n : 'api.anthropic.com' === suffix || 'api.anthropic.com'.endsWith(`.${suffix}`);\n return !bypassesAnthropic;\n }))]\n .join(',');\n if (filtered) {\n env['NO_PROXY'] = filtered;\n env['no_proxy'] = filtered;\n } else {\n delete env['NO_PROXY'];\n delete env['no_proxy'];\n }\n}\n\n/**\n * Any non-empty key satisfies the local endpoint gateway (`isAuthorized`\n * accepts everything when no server password is set, i.e. local listen mode).\n */\nexport const LOCAL_GATEWAY_API_KEY = 'clodex-local';\n\nexport function wrapperRequiresServer(env: NodeJS.ProcessEnv): boolean {\n return env[REQUIRE_SERVER_ENV] === '1';\n}\n\n/** The extension's top-level host shape, shared with missing-path handling. */\nexport function wrapperIsTopLevelVsCodeHost(env: NodeJS.ProcessEnv): boolean {\n return env['CLAUDE_CODE_ENTRYPOINT'] === 'claude-vscode'\n && !env['CLAUDE_CODE_CHILD_SESSION']\n && !env['CLAUDECODE'];\n}\n\n/**\n * Whether this wrapper spawn may select the manifest's verified patched install.\n *\n * Every supported platform is eligible. Windows was held back until the `windows-launcher` CI job\n * could prove the selector's file-identity checks on NTFS and drive the substitution end to end\n * through the native launcher (`tests/wrapper-substitution.windows.test.ts`); what differs there\n * now lives in `wrapper-target.ts` (`requireWrapperExecutable`), not in this gate.\n */\nexport function wrapperSubstitutionEligible(\n env: NodeJS.ProcessEnv,\n state: ServerRuntimeState | null,\n): boolean {\n return wrapperIsTopLevelVsCodeHost(env)\n && state?.mode === 'proxy';\n}\n\n/** Main chat is the persistent SDK stream-json spawn; programmatic queries are not. */\nexport function wrapperInvocationIsChat(args: readonly string[]): boolean {\n const streamJson = args.some((arg, index) =>\n arg === 'stream-json'\n && (args[index - 1] === '--output-format' || args[index - 1] === '--input-format'));\n return streamJson && !args.includes('--no-session-persistence');\n}\n\nexport function computeWrapperEnv(\n baseEnv: NodeJS.ProcessEnv,\n state: ServerRuntimeState | null,\n): NodeJS.ProcessEnv {\n // No live server: launch claude completely untouched — a down server must\n // never break launching claude.\n if (!state) return { ...baseEnv };\n\n const baseline = networkEnvBaseline(baseEnv);\n const env: NodeJS.ProcessEnv = { ...baseline };\n\n if (state.mode === 'proxy') {\n // Selective MITM: claude keeps its own Anthropic credentials; the proxy\n // routes clodex:/alias models to OpenAI and passes everything else through.\n const proxyUrl = `http://127.0.0.1:${state.port}`;\n delete env['ANTHROPIC_BASE_URL'];\n for (const name of PROXY_ENV_VARS) env[name] = proxyUrl;\n if (state.caPath) env['NODE_EXTRA_CA_CERTS'] = state.caPath;\n removeAnthropicProxyBypass(env);\n recordNetworkEnvMutation(baseline, env);\n return env;\n }\n\n // Endpoint gateway: all traffic goes to the local Anthropic-format gateway.\n for (const name of PROXY_ENV_VARS) delete env[name];\n env['ANTHROPIC_BASE_URL'] = `http://127.0.0.1:${state.port}/anthropic`;\n env['ANTHROPIC_API_KEY'] = LOCAL_GATEWAY_API_KEY;\n recordNetworkEnvMutation(baseline, env);\n return env;\n}\n","// src/process-exit.ts — how both bins (clodex, clodex-claude) end the process.\n//\n// Not process.exit(). On Node 24 it can hang forever: it joins V8's background threads before\n// tearing down the isolate, so a background compile that is waiting for the main thread to collect\n// garbage is never woken (https://github.com/nodejs/node/issues/64274). Letting the event loop\n// drain with process.exitCode set tears the isolate down first, which releases that thread.\n//\n// A drain only happens if nothing still holds the loop, so work that nothing awaits registers a\n// canceller here (see cancelOnExit) and is cut off when the exit is requested, exactly as\n// process.exit() used to cut it off. If something still holds the loop after the grace period,\n// the process falls back to process.exit() — the old behaviour, deadlock risk included — so an\n// unforeseen leak costs a short delay rather than a process that never ends.\n\nimport { writeSync } from 'node:fs';\n\n/**\n * How long a requested exit may wait for the event loop to drain before falling back to\n * process.exit(). Most measured drains ended before a 1 ms poll could sample them, and none took\n * more than about 60 ms, so this only matters when something unexpected holds the loop. One second keeps that case\n * short for someone at a terminal, while leaving room for the waits a drain can legitimately\n * include: an uncancellable in-flight DNS lookup or connection attempt, a pipe reader catching up on\n * buffered output (which process.exit() can cut short), and closing sockets finishing their\n * shutdown. Waiting also gives V8's background compiles time to finish, which should make a\n * fallback that does fire less likely to meet the deadlock than an immediate process.exit().\n */\nexport const EXIT_DRAIN_GRACE_MS = 1_000;\n\nexport interface ExitAfterDrainOptions {\n /** Report what still held the loop when the fallback fires. CLODEX_TRACE=1 also turns it on. */\n trace?: boolean;\n /** Prefix for the trace line, e.g. `clodex` or `clodex-claude`. */\n label?: string;\n graceMs?: number;\n}\n\nconst exitCancellers = new Set<() => void>();\nlet exitRequested = false;\n\n/**\n * Register work that nothing awaits and that must not keep the process alive once an exit is\n * requested. Returns an unregister function; call it when the work settles. Work registered after\n * an exit was requested is cancelled immediately.\n */\nexport function cancelOnExit(cancel: () => void): () => void {\n if (exitRequested) {\n runCanceller(cancel);\n return () => {};\n }\n exitCancellers.add(cancel);\n return () => {\n exitCancellers.delete(cancel);\n };\n}\n\nfunction runCanceller(cancel: () => void): void {\n try {\n cancel();\n } catch {\n // A canceller that throws must not stop the exit or the other cancellers.\n }\n}\n\n/** Active resources by type, e.g. `TCPSocketWrap x2, Timeout`. */\nexport function describeActiveResources(resources: readonly string[] = process.getActiveResourcesInfo()): string {\n if (resources.length === 0) return 'nothing reported';\n const counts = new Map<string, number>();\n for (const resource of resources) counts.set(resource, (counts.get(resource) ?? 0) + 1);\n return [...counts].map(([name, count]) => (count > 1 ? `${name} x${count}` : name)).join(', ');\n}\n\n/**\n * End the process with `code` once the event loop drains, the way process.exit(code) would have,\n * without process.exit()'s Node 24 deadlock. Returns immediately: the caller must stop doing work\n * (return) rather than rely on this call not returning. The first request wins, as with\n * process.exit(); later requests are ignored.\n */\nexport function exitAfterDrain(code: number, options: ExitAfterDrainOptions = {}): void {\n if (exitRequested) return;\n exitRequested = true;\n process.exitCode = code;\n // process.exit() discarded a stdio failure that arrived after the exit: a reader that has gone\n // away reports EPIPE asynchronously, and unhandled it would turn this exit code into 1.\n // Errors from before the request are untouched.\n const ignoreStdioError = () => {};\n process.stdout.on('error', ignoreStdioError);\n process.stderr.on('error', ignoreStdioError);\n\n const cancellers = [...exitCancellers];\n exitCancellers.clear();\n for (const cancel of cancellers) runCanceller(cancel);\n\n const graceMs = options.graceMs ?? EXIT_DRAIN_GRACE_MS;\n // Unref'd, so it never holds the loop open itself: it fires only if something else does.\n const fallback = setTimeout(() => {\n if (options.trace || process.env.CLODEX_TRACE === '1') {\n const label = options.label ?? 'clodex';\n try {\n writeSync(\n 2,\n `${label}: event loop still busy ${graceMs}ms after exit was requested; `\n + `exiting with process.exit(${code}). Still active: ${describeActiveResources()}\\n`,\n );\n } catch {\n // stderr gone; the exit matters more than the diagnostic.\n }\n }\n process.exit(code);\n }, graceMs);\n fallback.unref();\n}\n","import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getAppHome } from './paths.js';\n\nexport interface PatchManifest {\n /** Resolved (real) path of the patched claude binary. */\n binaryPath: string;\n /** `claude --version` at patch time. */\n claudeVersion: string;\n /** sha256 of the transform-set version and desired patch model config. */\n configHash: string;\n /** Size in bytes of the binary after patching (cheap staleness probe). */\n patchedSize: number;\n /** sha256 of the binary after patching. */\n patchedSha256: string;\n /** Pristine backup used for restore. */\n backupPath: string;\n /**\n * sha256 of the pristine bytes in `backupPath`. Written since content-addressed\n * backups landed; absent in manifests from older installs, so legacy readers\n * must treat it as optional.\n */\n pristineSha256?: string;\n /**\n * `assumed` when this run tied `backupPath` to `binaryPath` by the claude version\n * in a file name alone, rather than by a record or an earlier manifest.\n * Absent means established; preserving the distinction prevents a later run\n * from promoting a guess into independent proof (issue #204).\n */\n pristineProvenance?: 'assumed';\n patchedAt: string;\n}\n\n/** The complete manifest shape required before the wrapper may execute another file. */\nexport type WrapperPatchManifest = PatchManifest & { pristineSha256: string };\n\nexport function getPatchManifestPath(env: NodeJS.ProcessEnv = process.env): string {\n return join(getAppHome(env), 'patch-state.json');\n}\n\n/**\n * Legacy-tolerant reader used by patch and restore. Its intentionally narrow\n * validation is unchanged: old and partial manifests remain available to their\n * existing recovery rules.\n */\nexport function readPatchManifest(path = getPatchManifestPath()): PatchManifest | null {\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as PatchManifest;\n if (parsed && typeof parsed.binaryPath === 'string' && typeof parsed.configHash === 'string') {\n return parsed;\n }\n } catch {\n // missing or invalid manifest → unpatched\n }\n return null;\n}\n\nexport type WrapperPatchManifestRead =\n | { status: 'missing'; path: string }\n | {\n status: 'invalid';\n path: string;\n /** A usable target hint lets the wrapper explain a legacy/incomplete state. */\n binaryPath?: string;\n claudeVersion?: string;\n }\n | { status: 'valid'; path: string; manifest: WrapperPatchManifest };\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/;\n\n/**\n * Strict, read-only manifest view for executable substitution. Unlike restore,\n * the wrapper has no compatibility reason to act on incomplete testimony.\n */\nexport function readWrapperPatchManifest(\n path = getPatchManifestPath(),\n): WrapperPatchManifestRead {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'ENOENT'\n ? { status: 'missing', path }\n : { status: 'invalid', path };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { status: 'invalid', path };\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return { status: 'invalid', path };\n }\n\n const value = parsed as Record<string, unknown>;\n const hint = {\n ...(typeof value.binaryPath === 'string' && value.binaryPath.trim()\n ? { binaryPath: value.binaryPath }\n : {}),\n ...(typeof value.claudeVersion === 'string' && value.claudeVersion.trim()\n ? { claudeVersion: value.claudeVersion }\n : {}),\n };\n const valid = typeof value.binaryPath === 'string'\n && value.binaryPath.trim().length > 0\n && typeof value.claudeVersion === 'string'\n && value.claudeVersion.trim().length > 0\n && typeof value.configHash === 'string'\n && value.configHash.trim().length > 0\n && typeof value.patchedSize === 'number'\n && Number.isSafeInteger(value.patchedSize)\n && value.patchedSize >= 0\n && typeof value.patchedSha256 === 'string'\n && SHA256_HEX.test(value.patchedSha256)\n && typeof value.backupPath === 'string'\n && value.backupPath.trim().length > 0\n && typeof value.pristineSha256 === 'string'\n && SHA256_HEX.test(value.pristineSha256)\n && typeof value.patchedAt === 'string'\n && value.patchedAt.trim().length > 0\n && (value.pristineProvenance === undefined || value.pristineProvenance === 'assumed');\n\n if (!valid) return { status: 'invalid', path, ...hint };\n return { status: 'valid', path, manifest: value as unknown as WrapperPatchManifest };\n}\n","// src/patch-backup.ts — pristine-backup identity for `clodex patch`.\n//\n// `clodex patch` patches PRISTINE bytes, never a patched binary (no patch on top\n// of a patch). Those bytes come from a backup: `applyPatch` seeds its candidate\n// from one and renames the result over the install, and `clodex patch --restore`\n// copies one straight over the binary. Either way the chosen backup lands on the\n// user's install, so picking the wrong one destroys it — a backup tagged with a\n// version it does not actually contain silently downgrades Claude Code. Three\n// rules make that unreachable:\n//\n// 1. Backups are CONTENT-ADDRESSED: `claude-<version>-<sha256 prefix>.orig`.\n// A file name can therefore never alias two different contents, and every\n// backup self-validates — rehash it and compare against its own name.\n// 2. A backup is only ever used when its bytes are established as the pristine\n// bytes of the exact version being patched: the version tag must match the\n// version probed from the binary under the patch, integrity must verify, and\n// a legacy (pre-content-addressing) backup — which carries no hash to check —\n// must additionally report the same version when executed.\n// 3. Bytes about to be patched must carry no clodex patch marker, whether they\n// came from the live binary or from a backup. A backup can be poisoned:\n// every clodex before content addressing snapshotted whatever was live when\n// no backup existed, and the version-resolution bug generated exactly that\n// state. Patching poisoned bytes would double-patch the install AND launder\n// the result into a content-addressed name that rule 1 then trusts on sight.\n// 4. A version tag is not install provenance. The npm platform package and the\n// native installer ship DIFFERENT files under the same Claude Code version,\n// and both are supported, so a user can hold two same-version installs whose\n// bytes differ. The manifest is the only record of which install a backup was\n// made for, and it holds ONE install, so it can confirm a backup but never\n// rule one in by elimination: when it records a different install, or names\n// pristine bytes for this one that are no longer on disk, restoring is\n// refused rather than guessed (issue #199). A manifest is not enough on its\n// own, though: it holds one install and a successful `--restore` DELETES it,\n// so a backup routinely outlives the only record of what it belonged to. Each\n// backup therefore also carries a PROVENANCE RECORD PER INSTALL beside it,\n// naming an install whose pristine content it is and whether that association\n// was established or merely assumed. Those records survive the manifest, and\n// they are what lets two same-version installs coexist instead of one of them\n// being handed the other's bytes (issue #204). A record that exists but cannot\n// be read refuses rather than falling back — something claimed those bytes — and\n// so does a manifest that disagrees with a record about the same install, unless\n// the manifest describes the live bytes and is therefore provably the current one. Backups written before the records existed carry none\n// at all; for those, and only those, selection still rests on the version tag,\n// and then the plan says so and the guess itself is recorded as a guess so no\n// later run can re-derive it as proof.\n//\n// Everything that decides anything here is deterministic given its inputs so the\n// decisions can be tested directly; the caller performs the file copies. The one\n// write this module owns is a provenance record, which belongs beside the naming\n// and scanning rules it is read by.\n\nimport { createHash } from 'node:crypto';\nimport {\n closeSync,\n existsSync,\n openSync,\n readFileSync,\n readSync,\n readdirSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** Hex characters of the sha256 embedded in a content-addressed backup name. */\nexport const BACKUP_SHA_PREFIX_LENGTH = 16;\n\n/** Backup directory, shared with tweakcc (`tweakcc --restore` reads it). */\nexport function backupDir(): string {\n return process.env['TWEAKCC_CONFIG_DIR']?.trim() || join(homedir(), '.tweakcc');\n}\n\nconst HASH_CHUNK_BYTES = 1024 * 1024;\n\n/** Full-file SHA-256 without retaining a Claude Code executable in memory. */\nexport function sha256File(path: string): string {\n const hash = createHash('sha256');\n const buffer = Buffer.allocUnsafe(HASH_CHUNK_BYTES);\n const fd = openSync(path, 'r');\n try {\n let bytesRead: number;\n do {\n bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n if (bytesRead > 0) hash.update(buffer.subarray(0, bytesRead));\n } while (bytesRead > 0);\n } finally {\n closeSync(fd);\n }\n return hash.digest('hex');\n}\n\n/** Filename-safe form of a claude version string. */\nexport function backupVersionTag(version: string): string {\n const tag = version.trim().replace(/[^\\w.-]+/g, '_');\n // Unreachable for a probed version (always `\\d+.\\d+.\\d+`), but an empty tag\n // would make every version share one backup name — the aliasing this module\n // exists to prevent — so refuse rather than guess.\n if (!tag) throw new Error('clodex patch: refusing to name a pristine backup for an empty claude version');\n return tag;\n}\n\n/** `~/.tweakcc/claude-<version>-<sha256 prefix>.orig` — the name IS the content. */\nexport function contentAddressedBackupPath(version: string, sha256: string, dir = backupDir()): string {\n return join(dir, `claude-${backupVersionTag(version)}-${sha256.slice(0, BACKUP_SHA_PREFIX_LENGTH)}.orig`);\n}\n\n/** Pre-content-addressing name written by earlier clodex versions. */\nexport function legacyBackupPath(version: string, dir = backupDir()): string {\n return join(dir, `claude-${backupVersionTag(version)}.orig`);\n}\n\n/** tweakcc's own restore location, mirrored from the pristine backup. */\nexport function tweakccMirrorBackupPath(dir = backupDir()): string {\n return join(dir, 'native-binary.backup');\n}\n\n/**\n * On-disk shape of one provenance record: which install the backup beside it was\n * made for, and whether that association was ESTABLISHED or merely assumed.\n */\ninterface InstallProvenance {\n install: string;\n /**\n * True when the only thing that tied this backup to that install was the claude\n * version in its file name (issue #204's remaining compatibility fallback). Such\n * a record never selects and never refuses anything — it exists to stop the guess\n * from being promoted to an established one later, which is how a warned fallback\n * used to turn into a permanent fact every run then trusted on sight.\n */\n assumed: boolean;\n}\n\n/**\n * ONE FILE PER INSTALL, beside the backup: `<backup>.for-<hash>.json`.\n *\n * A single shared list would have to be read, merged and rewritten, and the patch\n * lock lives under `CLODEX_HOME` while the backup directory is shared — two\n * concurrent patches under different `CLODEX_HOME`s would lose one install's entry.\n * A file per install is created once and never merged, so there is nothing to lose.\n * The install path is hashed to keep the name filesystem-safe and fixed-length; the\n * path itself is inside the file, which is what selection reads.\n *\n * The suffix keeps these out of the `claude-*.orig` pattern the scanner matches, and\n * `tweakcc` only ever names `native-binary.backup` explicitly, so neither mistakes a\n * record for a backup.\n */\nexport function installProvenancePath(backupPath: string, binaryPath: string): string {\n const tag = createHash('sha256').update(binaryPath).digest('hex').slice(0, BACKUP_SHA_PREFIX_LENGTH);\n return `${backupPath}.for-${tag}.json`;\n}\n\n/** Matches any install's provenance record for the backup file `name`. */\nfunction installProvenancePattern(name: string): RegExp {\n return new RegExp(`^${name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\.for-[0-9a-f]{${BACKUP_SHA_PREFIX_LENGTH}}\\\\.json$`);\n}\n\n/** What one provenance record says, or `damaged` when it cannot be read. */\nexport type ProvenanceRecord = InstallProvenance | 'damaged';\n\nexport function readInstallProvenance(path: string): ProvenanceRecord | null {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return null;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return 'damaged';\n }\n if (typeof parsed !== 'object' || parsed === null) return 'damaged';\n const { install, assumed } = parsed as { install?: unknown; assumed?: unknown };\n // Both fields are required, and `assumed` must be a real boolean: anything else\n // would read as `false` — the ESTABLISHED value — so a damaged former guess would\n // silently become the strongest evidence in the directory.\n if (typeof install !== 'string' || install === '') return 'damaged';\n if (typeof assumed !== 'boolean') return 'damaged';\n return { install, assumed };\n}\n\n/**\n * Record `binaryPath` as an install whose pristine content `backupPath` holds, and\n * report the confidence that now stands on disk.\n *\n * Idempotent. An ESTABLISHED record is never downgraded to a guess: callers ask for\n * `assumed` from what THIS run can prove, and a run that can prove less than an\n * earlier one must not erase what the earlier one knew. Promotion the other way is\n * allowed and necessary — a fresh snapshot inspects the install's own bytes, which is\n * evidence no guess produced — so callers must pass `assumed: true` whenever their\n * evidence is derived from an earlier guess rather than independent of it.\n *\n * The return value is what the caller must act on: `--restore` may only drop a patch\n * manifest once an ESTABLISHED record has replaced it, or the manifest's testimony is\n * destroyed and nothing takes its place.\n */\nexport function recordBackupProvenance(\n backupPath: string,\n binaryPath: string,\n opts: { assumed: boolean },\n): 'established' | 'assumed' {\n const target = installProvenancePath(backupPath, binaryPath);\n const existing = readInstallProvenance(target);\n if (existing && existing !== 'damaged' && existing.install === binaryPath) {\n // Nothing to write: already at this confidence, or already stronger.\n if (existing.assumed === opts.assumed) return existing.assumed ? 'assumed' : 'established';\n if (!existing.assumed) return 'established';\n }\n const record: InstallProvenance = { install: binaryPath, assumed: opts.assumed };\n // Temp + rename for the same reason the backups use it: a half-written record\n // reads as damaged, and a damaged record refuses rather than falling back.\n const temp = `${target}.tmp-${process.pid}-${Date.now().toString(36)}`;\n try {\n writeFileSync(temp, `${JSON.stringify(record, null, 2)}\\n`);\n renameSync(temp, target);\n return opts.assumed ? 'assumed' : 'established';\n } catch (err) {\n try {\n rmSync(temp, { force: true });\n } catch {\n // best effort — the temp name matches neither the backup pattern nor the\n // record one, so a leftover is inert.\n }\n throw err;\n }\n}\n\nexport interface BackupCandidate {\n path: string;\n /** `content-addressed` names carry a sha256 prefix; `legacy` ones do not. */\n kind: 'content-addressed' | 'legacy';\n /** sha256 of the file's CURRENT bytes. */\n sha256: string;\n /**\n * Installs whose pristine content these bytes are ESTABLISHED to be. Empty for a\n * backup written before records existed — the one remaining case where a version\n * tag is all that ties a backup to an install. Assumed records are deliberately\n * absent: they are not evidence, and only the writer consults them.\n */\n installs: string[];\n /**\n * Installs recorded beside this backup as a GUESS. Not evidence: these never\n * select and never promote. They do block the version-tag fallback for a\n * DIFFERENT install, because \"these bytes were once handed to that install\" is\n * still a reason not to hand them to this one.\n */\n assumedInstalls: string[];\n /**\n * Provenance records beside this backup that could not be read. Something was\n * recorded and we cannot tell what, so the version-tag fallback is refused rather\n * than treating damaged positive evidence as if it had never existed.\n */\n damagedProvenance: string[];\n}\n\nexport interface BackupScan {\n /** Backups for this version whose bytes passed every check the name allows. */\n valid: BackupCandidate[];\n /** Content-addressed backups whose bytes no longer match their own name. */\n corrupt: string[];\n}\n\n/**\n * Find every backup on disk that claims to hold the pristine bytes of `version`.\n * Only names carrying this exact version tag are considered — a backup tagged\n * with a different version is never a candidate for restoring this binary, which\n * is what makes a mislabeled legacy file harmless instead of destructive.\n */\nexport function scanPristineBackups(version: string, dir = backupDir()): BackupScan {\n const tag = backupVersionTag(version);\n const pattern = new RegExp(`^claude-${tag.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(?:-([0-9a-f]{${BACKUP_SHA_PREFIX_LENGTH}}))?\\\\.orig$`);\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return { valid: [], corrupt: [] };\n }\n // One readdir for the backups AND their provenance records: the records are named\n // after the backup they belong to, so they are already in this listing.\n const provenanceOf = (\n name: string,\n ): Pick<BackupCandidate, 'installs' | 'assumedInstalls' | 'damagedProvenance'> => {\n const recordPattern = installProvenancePattern(name);\n const installs: string[] = [];\n const assumedInstalls: string[] = [];\n const damagedProvenance: string[] = [];\n for (const entry of entries) {\n if (!recordPattern.test(entry)) continue;\n const path = join(dir, entry);\n const record = readInstallProvenance(path);\n // The name came out of this same listing, so `null` here is not \"no record\":\n // it is one that exists and cannot be opened — a directory, a dangling link,\n // mode 000, an I/O error. Reading that as absent is what let the version-tag\n // fallback run on a backup that had already been claimed.\n if (record === null || record === 'damaged' || installProvenancePath(join(dir, name), record.install) !== path) {\n damagedProvenance.push(path);\n continue;\n }\n (record.assumed ? assumedInstalls : installs).push(record.install);\n }\n return {\n installs: installs.sort(),\n assumedInstalls: assumedInstalls.sort(),\n damagedProvenance: damagedProvenance.sort(),\n };\n };\n const valid: BackupCandidate[] = [];\n const corrupt: string[] = [];\n for (const entry of entries.sort()) {\n const match = pattern.exec(entry);\n if (!match) continue;\n const path = join(dir, entry);\n let sha256: string;\n try {\n if (!statSync(path).isFile()) continue;\n sha256 = sha256File(path);\n } catch {\n corrupt.push(path);\n continue;\n }\n const embedded = match[1];\n if (embedded) {\n // Self-validating: the bytes must still hash to the name they are stored under.\n if (sha256.slice(0, BACKUP_SHA_PREFIX_LENGTH) !== embedded) {\n corrupt.push(path);\n continue;\n }\n valid.push({ path, kind: 'content-addressed', sha256, ...provenanceOf(entry) });\n } else {\n valid.push({ path, kind: 'legacy', sha256, ...provenanceOf(entry) });\n }\n }\n return { valid, corrupt };\n}\n\n// ── Patched-binary detection ────────────────────────────────────────────────\n\n/**\n * PROOF that the source carries a clodex patch: the `ccpatch` comments the patch\n * sites inject. The text is OURS, not Claude Code's, so a false positive would\n * take a Claude Code bundle literally shipping this prefix.\n *\n * It covers every binary current clodex publishes: PATCH 8a/8b/8c/9 emit the\n * `effort`, `xhigh-effort`, `max-effort` and `default-effort` variants, and those\n * sites are REQUIRED (applyPatch throws when any FAILs), so at least one is\n * always present. PATCH 7 adds the `ctx` variant when any model has a\n * non-default context window.\n *\n * Raw byte inspection of the native binary cannot see these (the bundle is\n * compressed inside it), so this runs on the JS `readContent` extracts.\n */\nconst CLODEX_PATCH_COMMENT_PREFIX = '/*ccpatch:';\n\n/**\n * WEAKER signals, used only to warn. These identify a patch applied by a clodex\n * old enough to predate the required effort sites: back then the `ctx` comment\n * was the only marker, and it appears only when some model carried a non-default\n * context window — so such a binary can carry no proof marker at all (verified\n * against the real 2.1.220 bundle).\n *\n * They do NOT block, because unlike the proof marker they can in principle\n * collide with Claude Code's own bytes, and a false positive there is\n * UNRECOVERABLE: refusing to bootstrap tells the user to reinstall Claude Code,\n * which yields the very same bytes and the very same refusal. A missed legacy\n * patch, by contrast, is recoverable — delete the bad backup, reinstall, and the\n * reinstalled binary carries proof markers the moment clodex patches it.\n * So: proof blocks, heuristic warns.\n */\nconst LEGACY_CLODEX_PATCH_MARKERS = [\n 'Additional custom models: ', // PATCH 4 — Agent tool model description\n 'function(_i){return _i.value===_o.value}', // PATCH 5 — picker dedupe guard\n '\"clodex:', // PATCH 1/3 — canonical ids as model identities\n];\n\n/**\n * True when the extracted Claude Code source provably carries a clodex patch.\n * This is the gate on treating bytes as pristine — see the marker docs above for\n * why only the unambiguous marker is allowed to block.\n */\nexport function isPatchedClaudeSource(source: string): boolean {\n return source.includes(CLODEX_PATCH_COMMENT_PREFIX);\n}\n\n/**\n * True when the source carries no proof marker but does look like a patch from a\n * pre-effort-sites clodex. Callers warn; they must not refuse on this alone.\n */\nexport function looksLikeLegacyClodexPatch(source: string): boolean {\n return !isPatchedClaudeSource(source)\n && LEGACY_CLODEX_PATCH_MARKERS.some(marker => source.includes(marker));\n}\n\n// ── Planning ────────────────────────────────────────────────────────────────\n\nexport interface PatchManifestFacts {\n binaryPath: string;\n /**\n * The claude version the manifest was written for. A manifest that recorded a\n * DIFFERENT version says nothing about the backups tagged with the version now\n * being restored — its own backup is not even among them — so its testimony is\n * scoped to its version rather than applied across an upgrade. Absent in\n * hand-edited or truncated manifests, which are treated as same-version.\n */\n claudeVersion?: string;\n backupPath?: string;\n patchedSha256?: string;\n pristineSha256?: string;\n /**\n * `assumed` when the run that wrote this manifest tied its backup to its install\n * by the claude version in a file name alone. Absent means established — which is\n * the right reading for every manifest written before this field existed, since a\n * guess back then was reported but not recorded anywhere else either.\n */\n pristineProvenance?: 'assumed';\n}\n\nexport interface PristineFacts {\n /** Version probed from the binary that is about to be patched. */\n version: string;\n binaryPath: string;\n /** sha256 of the live binary's current bytes. */\n liveSha256: string;\n manifest: PatchManifestFacts | null;\n backups: BackupCandidate[];\n corruptBackups?: string[];\n}\n\nexport type PristinePlan =\n /** The live binary IS the pristine bytes — patch it, no restore. */\n | { action: 'reuse'; backupPath: string; pristineSha256: string; notes: string[] }\n /** Restore these pristine bytes over the live binary, then patch. */\n | {\n action: 'restore';\n backupPath: string;\n pristineSha256: string;\n probeVersion: boolean;\n /**\n * True when nothing but the version tag tied these bytes to this install —\n * no manifest for it, and no provenance sidecar anywhere. The restore still\n * happens (refusing would strand every backup written before sidecars\n * existed), but the caller must not record the guess as provenance.\n */\n assumedForThisInstall: boolean;\n notes: string[];\n }\n /** Undecidable from bytes alone — extract the source and re-plan. */\n | { action: 'inspect' }\n /** Bootstrap: store the live binary as this version's pristine backup. */\n | { action: 'snapshot'; backupPath: string; pristineSha256: string; notes: string[] }\n /** Nothing safe to do. Never fall back to a destructive copy. */\n | { action: 'error'; message: string };\n\n/** A plan the caller can act on — `inspect` has already been resolved away. */\nexport type ResolvedPristinePlan = Exclude<PristinePlan, { action: 'inspect' }>;\n\n/** Restoring pristine bytes either works from an established backup, or fails. */\nexport type RestorePlan = Extract<PristinePlan, { action: 'restore' } | { action: 'error' }>;\n\nfunction noBackupMessage(facts: PristineFacts): string {\n const corrupt = facts.corruptBackups?.length\n ? ` (${facts.corruptBackups.length} backup file(s) for this version failed integrity checks and were ignored)`\n : '';\n return `clodex holds no pristine backup of claude ${facts.version} it can attribute to `\n + `${facts.binaryPath}: none was found in ${backupDir()}${corrupt}. If that binary is patched, `\n + 'reinstall Claude Code to get a pristine one and run `clodex patch` again. If it was never '\n + 'patched, there is nothing to restore.';\n}\n\n/**\n * True when `manifest` recorded WHICH backup holds its install's pristine bytes.\n * `pristineSha256` is absent from manifests written before content addressing,\n * so the recorded path counts too; a manifest carrying neither identifies\n * nothing and can neither confirm nor disqualify a backup.\n */\nfunction identifiesABackup(manifest: PatchManifestFacts): boolean {\n return manifest.pristineSha256 !== undefined || manifest.backupPath !== undefined;\n}\n\n/** The manifest named this install's pristine bytes, and they are no longer usable. */\nfunction recordedBackupGoneMessage(facts: PristineFacts, manifest: PatchManifestFacts): string {\n const named = manifest.backupPath ?? `the backup holding sha256 ${manifest.pristineSha256}`;\n const corrupt = facts.corruptBackups?.length\n ? ` (${facts.corruptBackups.length} backup file(s) for this version failed integrity checks and were ignored)`\n : '';\n const others = facts.backups.length\n ? `The ${facts.backups.length} other pristine backup(s) tagged claude ${facts.version} there were made `\n + 'for some other install — two installs of one Claude Code version are different files, so '\n + 'restoring one of them would overwrite this install with bytes that were never its own. '\n : '';\n return `The patch manifest records ${named} as the pristine content of ${facts.binaryPath}, and clodex `\n + `cannot use it: it is missing from ${backupDir()}, failed its integrity check, or holds a `\n + `different claude version${corrupt}. ${others}`\n + 'Reinstall Claude Code to get a pristine binary, then run `clodex patch`.';\n}\n\n/** A manifest for a DIFFERENT install cannot vouch for anything in the backup directory. */\nfunction otherInstallMessage(facts: PristineFacts, otherBinaryPath: string): string {\n return `The patch manifest records a different Claude Code install (${otherBinaryPath}), so nothing `\n + `establishes that a pristine backup tagged claude ${facts.version} in ${backupDir()} belongs to `\n + `${facts.binaryPath}. Two installs of one Claude Code version are different files, so restoring `\n + 'by version tag alone would overwrite this install with another one\\'s bytes. Set '\n + `TWEAKCC_CC_INSTALLATION_PATH=${otherBinaryPath} to restore that install instead, or reinstall `\n + 'Claude Code to make this one pristine.';\n}\n\n/** Every backup on disk is recorded as belonging to some OTHER install. */\nfunction otherInstallProvenanceMessage(facts: PristineFacts, recorded: BackupCandidate[]): string {\n const owners = [...new Set(recorded.flatMap(backup => backup.installs))];\n return `The pristine backup(s) of claude ${facts.version} in ${backupDir()} are recorded as the `\n + `pristine content of ${owners.join(', ')}, not of ${facts.binaryPath}. Two installs of one `\n + 'Claude Code version are different files, so restoring one of those would overwrite this '\n + `install with bytes that were never its own. Set TWEAKCC_CC_INSTALLATION_PATH=${owners[0]} to `\n + 'restore that install instead, or reinstall Claude Code to make this one pristine.';\n}\n\n/** A provenance record exists beside a backup but cannot be read. */\nfunction damagedProvenanceMessage(facts: PristineFacts, damaged: string[]): string {\n return `A pristine backup of claude ${facts.version} in ${backupDir()} carries a provenance record `\n + `that clodex cannot read (${damaged.join(', ')}). Something recorded which install those bytes `\n + 'belong to and that record is damaged, so clodex will not fall back to matching on the claude '\n + `version in the file name — on a machine with more than one install that would hand `\n + `${facts.binaryPath} bytes that were never its own. Delete the unreadable record to accept that `\n + 'fallback, or reinstall Claude Code to make this install pristine.';\n}\n\n/** Two backups holding different bytes both claim to be this install's pristine content. */\nfunction contradictoryProvenanceMessage(facts: PristineFacts, mine: BackupCandidate[]): string {\n return `More than one pristine backup of claude ${facts.version} is recorded as the pristine `\n + `content of ${facts.binaryPath} (${mine.map(backup => backup.path).join(', ')}), and they do `\n + 'not hold the same bytes, so clodex cannot tell which one that install holds now — being '\n + 'snapshotted twice, at two different sets of unpatched bytes, reaches this state without either '\n + 'record being wrong. Delete whichever is stale to decide it: '\n + `${mine.map(backup => installProvenancePath(backup.path, facts.binaryPath)).join(', ')}. `\n + `Or reinstall the Claude Code at ${facts.binaryPath} and delete both.`;\n}\n\n/** The manifest and an established record disagree about this install's pristine bytes. */\nfunction manifestContradictsRecordMessage(\n facts: PristineFacts,\n manifestChoice: string,\n recorded: BackupCandidate[],\n): string {\n return `The patch manifest names ${manifestChoice} as the pristine content of ${facts.binaryPath}, `\n + `but ${recorded.map(backup => backup.path).join(', ')} is recorded as that install's pristine `\n + 'content and holds different bytes. Nothing establishes which of them the install at that path '\n + 'is now — an executable replaced in place with a different build of the same claude version '\n + 'reaches exactly this state — so clodex will not pick one. Delete whichever of these records is '\n + `stale to decide it: ${recorded.map(backup => installProvenancePath(backup.path, facts.binaryPath)).join(', ')}. `\n + `Or reinstall the Claude Code at ${facts.binaryPath} and delete both: a pristine install needs `\n + 'no backup, and `clodex patch` will record its own.';\n}\n\n/** Bytes this machine has already handed to a DIFFERENT install, as a guess. */\nfunction assumedElsewhereMessage(facts: PristineFacts, backups: BackupCandidate[]): string {\n const others = [...new Set(backups.flatMap(backup => backup.assumedInstalls))];\n return `The pristine backup(s) of claude ${facts.version} in ${backupDir()} have already been `\n + `restored onto ${others.join(', ')} — not onto ${facts.binaryPath}. That was itself matched on `\n + 'the claude version in a file name, so it is not proof of ownership, but it is a reason not to '\n + `hand the same bytes to a second install: two installs of one version are different files. Set `\n + `TWEAKCC_CC_INSTALLATION_PATH=${others[0]} to restore that install, or reinstall Claude Code to `\n + 'make this one pristine.';\n}\n\n/**\n * Pick the pristine bytes to restore over an already-patched binary.\n * Every branch requires bytes whose provenance is established; when none are,\n * the result is an error rather than a guess.\n */\nfunction selectRestoreSource(facts: PristineFacts): RestorePlan {\n const notes: string[] = [];\n let assumedForThisInstall = false;\n const recorded = facts.manifest;\n // A manifest speaks only for the version it was written for. After an upgrade\n // it records a backup of the OLD version, which is not among this version's\n // candidates — treating that as \"the recorded backup is gone\" refused a restore\n // that was never in danger, and treating it as evidence about another install\n // refused one for a version it had never seen.\n const speaksForThisVersion = !recorded?.claudeVersion || recorded.claudeVersion === facts.version;\n const manifest = recorded && recorded.binaryPath === facts.binaryPath && speaksForThisVersion\n ? recorded\n : null;\n // A manifest recorded against a DIFFERENT install is not the same thing as no\n // manifest: it is positive evidence about the backup directory. The bytes it\n // names are that other install's pristine bytes, and two supported installs of\n // ONE Claude Code version are genuinely different files (the npm platform\n // package and the native installer ship different binaries under the same\n // version). Discarding the manifest and falling through to version-tag\n // selection published one install's bytes over the other and then deleted the\n // manifest, leaving no backup of what it clobbered — issue #199. Same version\n // is not the same install.\n const other = recorded && recorded.binaryPath !== facts.binaryPath && speaksForThisVersion\n ? recorded\n : null;\n\n // 1. The manifest's recorded pristine content, wherever it now lives. Matching\n // on content (not path) also survives the legacy → content-addressed rename.\n let chosen = manifest?.pristineSha256\n ? facts.backups.find(backup => backup.sha256 === manifest.pristineSha256)\n : undefined;\n\n // 2. The manifest's recorded backup path. Only candidates carrying THIS\n // version's tag are in `facts.backups`, so a manifest left by a version-\n // resolution bug (path tagged with some other version) simply does not\n // match here instead of being copied over a newer binary.\n if (!chosen && manifest?.backupPath) {\n chosen = facts.backups.find(backup => backup.path === manifest.backupPath);\n }\n\n // 2b. The manifest speaks FOR this install: it recorded which bytes are its\n // pristine content. When neither the recorded hash nor the recorded path is\n // on disk any more, every remaining same-version backup was made for some\n // other install, and the manifest's own testimony says so. Falling through\n // to version-tag selection here published another install's bytes.\n if (!chosen && manifest && identifiesABackup(manifest)) {\n return { action: 'error', message: recordedBackupGoneMessage(facts, manifest) };\n }\n\n // 2c. The manifest chose, but a record for this same install names bytes that are\n // not those. That state is reachable BOTH ways round: one install path can be\n // rewritten in place with a different build of the same claude version (the\n // manifest is then stale), and one install can legitimately be snapshotted\n // twice — tweakcc theming rewrites the binary, and the snapshot path keeps both\n // files on purpose — leaving two true records of what it held at two times.\n //\n // What separates them is whether the manifest describes the binary in front of\n // us. When its `patchedSha256` IS the live bytes, clodex provably wrote them\n // last, so it is the current record and its backup is the right source. Only\n // when it does not is there nothing to date either claim by, and then refusing\n // is the only safe answer: refusing in the other case made every later patch\n // AND restore of an ordinary single-install machine fail permanently, with a\n // message telling the user to reinstall, which does not clear a record.\n const manifestDescribesLiveBytes = !!manifest?.patchedSha256\n && manifest.patchedSha256 === facts.liveSha256;\n if (chosen && !manifestDescribesLiveBytes) {\n const manifestChoice = chosen;\n const contradicting = facts.backups.filter(\n backup => backup.installs.includes(facts.binaryPath) && backup.sha256 !== manifestChoice.sha256,\n );\n if (contradicting.length) {\n return {\n action: 'error',\n message: manifestContradictsRecordMessage(facts, manifestChoice.path, contradicting),\n };\n }\n }\n\n // 3. No manifest help: fall back to the version's backups, but only when they\n // agree on the content. Two different \"pristine\" snapshots of one version\n // mean at least one is wrong; guessing is exactly the destructive move.\n // 3. The provenance records: which backups were made FOR this install. This is\n // the record that outlives the manifest, so it is the evidence available on\n // exactly the paths #199's manifest fix could not reach — after a successful\n // `--restore` deleted the manifest, or with a wiped `~/.clodex`. It also\n // settles a two-install machine positively rather than by refusal: each\n // install's own backup names it, so both can be restored.\n if (!chosen) {\n const mine = facts.backups.filter(backup => backup.installs.includes(facts.binaryPath));\n if (mine.length) {\n if (new Set(mine.map(backup => backup.sha256)).size > 1) {\n return { action: 'error', message: contradictoryProvenanceMessage(facts, mine) };\n }\n // Prefer a self-validating name when both spellings hold the same bytes.\n chosen = mine.find(backup => backup.kind === 'content-addressed') ?? mine[0];\n }\n }\n\n // 4. Nothing establishes a backup for this install. Refuse on any positive\n // evidence that the bytes on disk belong elsewhere, and fall back to the\n // version tag only for backups that carry no record at all.\n if (!chosen) {\n // A manifest recorded against a DIFFERENT install vouches for nothing here.\n // Disqualifying only the backup IT names is not enough: the manifest holds one\n // install, so every earlier install's backup is an unrecorded orphan carrying\n // the same version tag, and picking \"the one it did not name\" hands a third\n // install's bytes to this one. There is no evidence to select on — refuse.\n if (other) {\n return facts.backups.length\n ? { action: 'error', message: otherInstallMessage(facts, other.binaryPath) }\n : { action: 'error', message: noBackupMessage(facts) };\n }\n // A sidecar naming some other install is the same kind of positive evidence,\n // and it is the one that survives the manifest being deleted. Without it,\n // restoring here published one install's pristine bytes over another and then\n // reported success — issue #204.\n const recordedElsewhere = facts.backups.filter(backup => backup.installs.length > 0);\n if (recordedElsewhere.length) {\n return { action: 'error', message: otherInstallProvenanceMessage(facts, recordedElsewhere) };\n }\n // A record that cannot be read is not the same as no record: something was\n // written, and reading it as \"unattributed\" is what would let the version-tag\n // fallback below run on a backup that had already been claimed.\n const damaged = facts.backups.flatMap(backup => backup.damagedProvenance);\n if (damaged.length) {\n return { action: 'error', message: damagedProvenanceMessage(facts, damaged) };\n }\n // These bytes have already been restored onto some OTHER install by this same\n // fallback. The guess proves nothing about ownership, so it neither selects nor\n // establishes — but running the fallback AGAIN for a second install is how one\n // install's bytes reach two, and the first guess is warning enough not to.\n // A guess recorded for THIS install is not a reason to refuse: it is the same\n // decision, being repeated.\n const assumedElsewhere = facts.backups.filter(\n backup => backup.assumedInstalls.some(install => install !== facts.binaryPath),\n );\n if (assumedElsewhere.length) {\n return { action: 'error', message: assumedElsewhereMessage(facts, assumedElsewhere) };\n }\n const distinct = [...new Set(facts.backups.map(backup => backup.sha256))];\n if (distinct.length > 1) {\n return {\n action: 'error',\n message: `Found conflicting pristine backups for claude ${facts.version}: `\n + `${facts.backups.map(backup => backup.path).join(', ')}. `\n + 'They do not hold the same bytes, so clodex cannot tell which one is pristine. '\n + 'If this machine has more than one Claude Code install, both are probably genuine — one '\n + `per install — and deleting either one is a guess. Reinstall the Claude Code at `\n + `${facts.binaryPath} instead: a pristine install needs no backup, and \\`clodex patch\\` `\n + 'will record its own.',\n };\n }\n // Prefer a self-validating name when both spellings hold the same bytes.\n chosen = facts.backups.find(backup => backup.kind === 'content-addressed') ?? facts.backups[0];\n if (chosen) {\n // Say so out loud: nothing records which install this backup was made for,\n // so the ONLY thing tying these bytes to this one is the version tag in the\n // file name. That is the last place a same-version backup from another\n // install can still be selected, and it is reachable only for a backup\n // written before clodex recorded provenance beside it.\n assumedForThisInstall = true;\n notes.push(\n `Neither a patch manifest nor a provenance record ties ${chosen.path} to ${facts.binaryPath}, `\n + `so it is being used as that install's pristine content on the strength of its claude `\n + `${facts.version} version tag alone — it predates the records clodex now writes. On a `\n + 'machine with more than one Claude Code install those bytes may belong to the other one, '\n + 'and clodex will not record this guess as provenance.',\n );\n }\n }\n\n if (!chosen) return { action: 'error', message: noBackupMessage(facts) };\n // A manifest written by a run that guessed is not independent evidence — it is the\n // guess, written down. Carrying its own confidence forward is what stops the next\n // run from reading it as proof and promoting the record beside the backup.\n if (manifest?.pristineProvenance === 'assumed') assumedForThisInstall = true;\n return {\n action: 'restore',\n backupPath: chosen.path,\n pristineSha256: chosen.sha256,\n // A legacy name carries no hash, so its bytes could be anything — including\n // another version's binary, stored under a mislabeled name by an older\n // clodex. Executing it is the only evidence available; require it.\n probeVersion: chosen.kind === 'legacy',\n assumedForThisInstall,\n notes,\n };\n}\n\n/**\n * First planning pass — decided from file hashes alone (no extraction).\n * Returns `inspect` when the live binary's bytes are unrecognized, meaning the\n * caller must extract the source and call `planInspectedPristineSource`.\n */\nexport function planPristineSource(facts: PristineFacts): PristinePlan {\n // The live binary matches a backup we already hold for this version → it is\n // provably pristine. Patch in place; no restore, no new snapshot.\n // Prefer a self-validating name when both spellings hold the same bytes; the\n // caller adopts a legacy-only match under its content address.\n const identical = facts.backups.find(backup => backup.sha256 === facts.liveSha256 && backup.kind === 'content-addressed')\n ?? facts.backups.find(backup => backup.sha256 === facts.liveSha256);\n if (identical) {\n return { action: 'reuse', backupPath: identical.path, pristineSha256: identical.sha256, notes: [] };\n }\n\n // The manifest says these exact bytes are the patch clodex applied to this\n // binary → it is patched; restore before patching again.\n const manifest = facts.manifest;\n if (manifest && manifest.binaryPath === facts.binaryPath && manifest.patchedSha256 === facts.liveSha256) {\n return selectRestoreSource(facts);\n }\n\n return { action: 'inspect' };\n}\n\n/**\n * Second planning pass, once the Claude Code source has been extracted and\n * checked for clodex patch markers.\n */\nexport function planInspectedPristineSource(\n facts: PristineFacts,\n inspection: { patched: boolean },\n): ResolvedPristinePlan {\n if (inspection.patched) return selectRestoreSource(facts);\n\n // Bootstrap. The only way a first backup ever exists is by snapshotting a\n // binary whose provenance clodex cannot prove, so this decision is explicit:\n // snapshot ONLY a binary that carries no clodex patch marker. That is what\n // keeps a patched binary from being stored as \"pristine\" and poisoning every\n // later restore. The name is derived from the bytes being stored, so it can\n // never overwrite a different backup's content.\n const notes: string[] = [];\n const conflicting = facts.backups.filter(backup => backup.sha256 !== facts.liveSha256);\n if (conflicting.length) {\n notes.push(\n `Existing backup(s) for claude ${facts.version} hold different bytes (${conflicting.map(b => b.path).join(', ')}); `\n + 'the binary being patched carries no clodex patch marker, so it is being stored under its own content address. '\n + 'Both files are kept.',\n );\n }\n return {\n action: 'snapshot',\n backupPath: contentAddressedBackupPath(facts.version, facts.liveSha256),\n pristineSha256: facts.liveSha256,\n notes,\n };\n}\n\n/**\n * Plan for `clodex patch --restore`: the live binary is assumed patched, so the\n * same \"establish the provenance or refuse\" rules apply.\n */\nexport function planRestoreOnly(facts: PristineFacts): RestorePlan {\n return selectRestoreSource(facts);\n}\n\n/** Gather the on-disk facts a plan needs (hashes every backup for the version). */\nexport function collectPristineFacts(args: {\n version: string;\n binaryPath: string;\n manifest: PatchManifestFacts | null;\n dir?: string;\n}): PristineFacts {\n const dir = args.dir ?? backupDir();\n const scan = scanPristineBackups(args.version, dir);\n return {\n version: args.version,\n binaryPath: args.binaryPath,\n liveSha256: existsSync(args.binaryPath) ? sha256File(args.binaryPath) : '',\n manifest: args.manifest,\n backups: scan.valid,\n corruptBackups: scan.corrupt,\n };\n}\n"],"mappings":"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","names":["randomUUID","closeSync","fsyncSync","mkdirSync","openSync","readFileSync","unlinkSync","dirname","join","join","mkdirSync","dirname","openSync","fsyncSync","closeSync","readFileSync","randomUUID","unlinkSync","randomUUID","readFileSync","renameSync","unlinkSync","readFileSync","randomUUID","renameSync","unlinkSync","execFileSync","existsSync","homedir","join","existsSync","isWindows","join","homedir","existsSync","execFileSync","writeSync","closeSync","mkdirSync","openSync","readFileSync","renameSync","unlinkSync","writeFileSync","dirname","join","join","isPidAlive","mkdirSync","dirname","openSync","writeFileSync","closeSync","unlinkSync","readFileSync","sleepSync","renameSync","writeSync","readFileSync","join","join","readFileSync","createHash","closeSync","existsSync","openSync","readFileSync","renameSync","rmSync","statSync","writeFileSync","homedir","join"]}
|
package/dist/claude-wrapper.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
OAUTH_ACCOUNT_ENV,
|
|
4
4
|
computeWrapperEnv,
|
|
5
5
|
emitParentNotice,
|
|
6
|
+
exitAfterDrain,
|
|
6
7
|
findClaudeBinary,
|
|
7
8
|
installParentNoticeSink,
|
|
8
9
|
orderWrapperServerCandidates,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
wrapperSpawnShell,
|
|
17
18
|
wrapperSubstitutionEligible,
|
|
18
19
|
writeParentNoticeLinesSync
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-Q2YFYQQT.js";
|
|
20
21
|
|
|
21
22
|
// src/claude-wrapper.ts
|
|
22
23
|
import { spawn } from "child_process";
|
|
@@ -313,7 +314,8 @@ async function main() {
|
|
|
313
314
|
}
|
|
314
315
|
if (!checkOnly && !claudePath) {
|
|
315
316
|
process.stderr.write("clodex-claude: could not find the claude binary (set CLODEX_CLAUDE_PATH)\n");
|
|
316
|
-
|
|
317
|
+
exitWrapper(127);
|
|
318
|
+
return;
|
|
317
319
|
}
|
|
318
320
|
const candidates = orderWrapperServerCandidates(readLiveServerRuntimeStates());
|
|
319
321
|
const state = await waitForTcpListenerCandidate(
|
|
@@ -322,10 +324,14 @@ async function main() {
|
|
|
322
324
|
WRAPPER_SERVER_READY_TIMEOUT_MS,
|
|
323
325
|
{ retryFailure: (result) => result === "timeout" }
|
|
324
326
|
);
|
|
325
|
-
if (checkOnly)
|
|
327
|
+
if (checkOnly) {
|
|
328
|
+
exitWrapper(state ? 0 : 1);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
326
331
|
if (!state && wrapperRequiresServer(process.env)) {
|
|
327
332
|
process.stderr.write("clodex-claude: no live clodex server is available\n");
|
|
328
|
-
|
|
333
|
+
exitWrapper(1);
|
|
334
|
+
return;
|
|
329
335
|
}
|
|
330
336
|
if (state && process.env[OAUTH_ACCOUNT_ENV]?.trim()) {
|
|
331
337
|
process.stderr.write(
|
|
@@ -360,15 +366,19 @@ async function main() {
|
|
|
360
366
|
child.on("error", (err) => {
|
|
361
367
|
process.stderr.write(`clodex-claude: failed to launch ${claudePath}: ${err.message}
|
|
362
368
|
`);
|
|
363
|
-
|
|
369
|
+
exitWrapper(127);
|
|
364
370
|
});
|
|
365
371
|
child.on("exit", (code, signal) => {
|
|
366
372
|
if (signal) {
|
|
367
373
|
const signum = osConstants.signals[signal];
|
|
368
|
-
|
|
374
|
+
exitWrapper(signum ? 128 + signum : 1);
|
|
375
|
+
return;
|
|
369
376
|
}
|
|
370
|
-
|
|
377
|
+
exitWrapper(code ?? 0);
|
|
371
378
|
});
|
|
372
379
|
}
|
|
380
|
+
function exitWrapper(code) {
|
|
381
|
+
exitAfterDrain(code, { label: "clodex-claude" });
|
|
382
|
+
}
|
|
373
383
|
void main();
|
|
374
384
|
//# sourceMappingURL=claude-wrapper.js.map
|