"use strict";
/**
 * Persist Command Handler
 *
 * Writes a profile's Claude setup to ~/.claude/settings.json
 * for native Claude Code usage across the CLI and IDE extension.
 *
 * Supports API, CLIProxy, Copilot, account, and default flows
 * through the shared Claude extension setup resolver.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handlePersistCommand = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const lockfile = __importStar(require("proper-lockfile"));
const ui_1 = require("../utils/ui");
const prompt_1 = require("../utils/prompt");
const profile_detector_1 = __importDefault(require("../auth/profile-detector"));
const claude_config_path_1 = require("../utils/claude-config-path");
const arg_extractor_1 = require("./arg-extractor");
const claude_extension_setup_1 = require("../shared/claude-extension-setup");
const stale_codex_translator_settings_1 = require("../shared/stale-codex-translator-settings");
const PERSIST_KNOWN_FLAGS = [
    '--yes',
    '-y',
    '--list-backups',
    '--restore',
    '--permission-mode',
    '--dangerously-skip-permissions',
    '--auto-approve',
    '--help',
    '-h',
];
const VALID_PERMISSION_MODES = ['default', 'plan', 'acceptEdits', 'bypassPermissions'];
const PERSIST_LOCK_STALE_MS = 10000;
const PERSIST_LOCK_RETRIES = 5;
const PERSIST_LOCK_RETRY_MIN_MS = 100;
const PERSIST_LOCK_RETRY_MAX_MS = 500;
const NATIVE_CODEX_TARGETS = ['ccsxp', 'ccs codex --target codex'];
function isPermissionMode(value) {
    return VALID_PERMISSION_MODES.includes(value);
}
function isKnownPersistFlagToken(token) {
    return PERSIST_KNOWN_FLAGS.some((flag) => token === flag || token.startsWith(`${flag}=`));
}
function resolvePermissionMode(parsedArgs) {
    if (!parsedArgs.dangerouslySkipPermissions) {
        return parsedArgs.permissionMode;
    }
    if (parsedArgs.permissionMode && parsedArgs.permissionMode !== 'bypassPermissions') {
        throw new Error('--dangerously-skip-permissions conflicts with --permission-mode. Use bypassPermissions or remove one flag.');
    }
    return 'bypassPermissions';
}
/** Parse command line arguments */
function parseArgs(args) {
    const result = {
        yes: (0, arg_extractor_1.hasAnyFlag)(args, ['--yes', '-y']),
        listBackups: (0, arg_extractor_1.hasAnyFlag)(args, ['--list-backups']),
    };
    const restoreOption = (0, arg_extractor_1.extractOption)(args, ['--restore']);
    if (restoreOption.found) {
        result.restore = restoreOption.missingValue ? true : restoreOption.value || true;
    }
    const permissionModeOption = (0, arg_extractor_1.extractOption)(restoreOption.remainingArgs, ['--permission-mode'], {
        knownFlags: PERSIST_KNOWN_FLAGS,
    });
    if (permissionModeOption.found) {
        if (permissionModeOption.missingValue) {
            result.parseError = 'Missing value for --permission-mode';
        }
        else if (permissionModeOption.value) {
            if (!isPermissionMode(permissionModeOption.value)) {
                result.parseError = `Invalid --permission-mode "${permissionModeOption.value}". Valid modes: ${VALID_PERMISSION_MODES.join(', ')}`;
            }
            else {
                result.permissionMode = permissionModeOption.value;
            }
        }
    }
    result.dangerouslySkipPermissions = (0, arg_extractor_1.hasAnyFlag)(permissionModeOption.remainingArgs, [
        '--dangerously-skip-permissions',
        '--auto-approve',
    ]);
    const unknownFlags = permissionModeOption.remainingArgs.filter((arg) => arg.startsWith('-') && !isKnownPersistFlagToken(arg));
    if (!result.parseError && unknownFlags.length > 0) {
        const unknownList = unknownFlags.map((flag) => `"${flag}"`).join(', ');
        result.parseError = `Unknown option(s): ${unknownList}. Run 'ccs persist --help' for usage.`;
    }
    if (!result.parseError && result.listBackups && result.restore) {
        result.parseError = '--list-backups cannot be used with --restore';
    }
    if (!result.parseError &&
        (result.listBackups || result.restore) &&
        (result.permissionMode || result.dangerouslySkipPermissions)) {
        result.parseError =
            'Permission flags are not valid with backup operations. Use them only with ccs persist <profile>.';
    }
    for (const arg of permissionModeOption.remainingArgs) {
        if (!arg.startsWith('-')) {
            result.profile = arg;
            break;
        }
    }
    return result;
}
function formatDisplayPath(filePath) {
    const defaultClaudeDir = path.join(os.homedir(), '.claude');
    const claudeDir = (0, claude_config_path_1.getClaudeConfigDir)();
    // Keep real path when user overrides Claude directory.
    if (path.resolve(claudeDir) !== path.resolve(defaultClaudeDir)) {
        return filePath;
    }
    if (filePath === claudeDir) {
        return '~/.claude';
    }
    const claudePrefix = `${claudeDir}${path.sep}`;
    if (filePath.startsWith(claudePrefix)) {
        return filePath.replace(claudePrefix, '~/.claude/');
    }
    return filePath;
}
function getClaudeSettingsDisplayPath() {
    return formatDisplayPath((0, claude_config_path_1.getClaudeSettingsPath)());
}
async function pathExists(filePath) {
    try {
        await fs.promises.access(filePath, fs.constants.F_OK);
        return true;
    }
    catch {
        return false;
    }
}
async function isSymlinkAsync(filePath) {
    try {
        const stats = await fs.promises.lstat(filePath);
        return stats.isSymbolicLink();
    }
    catch {
        return false;
    }
}
function getNoFollowFlag() {
    const candidate = fs.constants['O_NOFOLLOW'];
    if (process.platform !== 'win32' && typeof candidate === 'number') {
        return candidate;
    }
    return 0;
}
function createSymlinkReadError(filePath) {
    const error = new Error(`Refusing to read symlinked file for security: ${formatDisplayPath(filePath)}`);
    error.code = 'ELOOP';
    return error;
}
async function readFileUtf8NoFollow(filePath) {
    if (await isSymlinkAsync(filePath)) {
        throw createSymlinkReadError(filePath);
    }
    const noFollowFlag = getNoFollowFlag();
    const flags = fs.constants.O_RDONLY | noFollowFlag;
    const handle = await fs.promises.open(filePath, flags);
    try {
        // Best-effort fallback for platforms without O_NOFOLLOW (notably Windows).
        // Re-check symlink status after open to reduce check-then-use windows.
        if (noFollowFlag === 0 && (await isSymlinkAsync(filePath))) {
            throw createSymlinkReadError(filePath);
        }
        const stats = await handle.stat();
        if (!stats.isFile()) {
            throw new Error('Path is not a regular file');
        }
        if (noFollowFlag === 0) {
            const latestStats = await fs.promises.stat(filePath);
            if (latestStats.dev !== stats.dev || latestStats.ino !== stats.ino) {
                throw new Error('Path changed during secure read');
            }
        }
        return await handle.readFile({ encoding: 'utf8' });
    }
    finally {
        await handle.close();
    }
}
function parseSettingsObject(content, sourceLabel) {
    if (!content.trim()) {
        return {};
    }
    const parsed = JSON.parse(content);
    if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
        throw new Error(`${sourceLabel} must contain a JSON object, not an array or primitive`);
    }
    return parsed;
}
async function withPersistSettingsLock(operation) {
    const settingsPath = (0, claude_config_path_1.getClaudeSettingsPath)();
    const settingsDir = path.dirname(settingsPath);
    await fs.promises.mkdir(settingsDir, { recursive: true });
    let release;
    try {
        release = await lockfile.lock(settingsDir, {
            stale: PERSIST_LOCK_STALE_MS,
            retries: {
                retries: PERSIST_LOCK_RETRIES,
                minTimeout: PERSIST_LOCK_RETRY_MIN_MS,
                maxTimeout: PERSIST_LOCK_RETRY_MAX_MS,
            },
            realpath: false,
        });
    }
    catch (error) {
        throw new Error(`Failed to lock Claude settings directory (${formatDisplayPath(settingsDir)}): ${error.message}`);
    }
    try {
        return await operation();
    }
    finally {
        if (release) {
            try {
                await release();
            }
            catch {
                // Best-effort release.
            }
        }
    }
}
/** Read existing Claude settings.json with validation */
async function readClaudeSettings() {
    const settingsPath = (0, claude_config_path_1.getClaudeSettingsPath)();
    try {
        const content = await readFileUtf8NoFollow(settingsPath);
        return parseSettingsObject(content, 'settings.json');
    }
    catch (error) {
        const nodeError = error;
        if (nodeError.code === 'ENOENT') {
            return {};
        }
        if (nodeError.code === 'ELOOP') {
            throw new Error('settings.json is a symlink - refusing to read for security');
        }
        throw new Error(`Failed to parse settings.json: ${error.message}`);
    }
}
/** Write settings back to settings.json with atomic replace semantics. */
async function writeClaudeSettings(settings) {
    const settingsPath = (0, claude_config_path_1.getClaudeSettingsPath)();
    if (await isSymlinkAsync(settingsPath)) {
        throw new Error('settings.json is a symlink - refusing to write for security');
    }
    const settingsDir = path.dirname(settingsPath);
    await fs.promises.mkdir(settingsDir, { recursive: true });
    const nonce = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
    const tmpPath = path.join(settingsDir, `settings.json.tmp-${nonce}`);
    const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | getNoFollowFlag();
    let handle;
    try {
        handle = await fs.promises.open(tmpPath, flags, 0o600);
        await handle.writeFile(JSON.stringify(settings, null, 2) + '\n', { encoding: 'utf8' });
        await handle.sync();
    }
    finally {
        if (handle) {
            await handle.close();
        }
    }
    try {
        await fs.promises.rename(tmpPath, settingsPath);
    }
    catch (error) {
        try {
            await fs.promises.unlink(tmpPath);
        }
        catch {
            // Best-effort cleanup.
        }
        throw error;
    }
    try {
        await fs.promises.chmod(settingsPath, 0o600);
    }
    catch {
        // Best-effort permission hardening.
    }
}
/** Maximum number of backups to keep (oldest are deleted) */
const MAX_BACKUPS = 10;
/** Create backup of settings.json with proper permissions and rotation */
async function createBackup() {
    const settingsPath = (0, claude_config_path_1.getClaudeSettingsPath)();
    if (!(await pathExists(settingsPath))) {
        throw new Error('No settings.json to backup');
    }
    const settingsContent = await readFileUtf8NoFollow(settingsPath);
    const now = new Date();
    const timestamp = now.getFullYear().toString() +
        (now.getMonth() + 1).toString().padStart(2, '0') +
        now.getDate().toString().padStart(2, '0') +
        '_' +
        now.getHours().toString().padStart(2, '0') +
        now.getMinutes().toString().padStart(2, '0') +
        now.getSeconds().toString().padStart(2, '0');
    const backupPath = `${settingsPath}.backup.${timestamp}`;
    const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | getNoFollowFlag();
    let handle;
    try {
        handle = await fs.promises.open(backupPath, flags, 0o600);
        await handle.writeFile(settingsContent, { encoding: 'utf8' });
        await handle.sync();
    }
    finally {
        if (handle) {
            await handle.close();
        }
    }
    try {
        await fs.promises.chmod(backupPath, 0o600);
    }
    catch {
        // Best-effort permission hardening.
    }
    // Cleanup: Rotate old backups (keep only MAX_BACKUPS)
    cleanupOldBackups();
    return backupPath;
}
/** Remove old backups keeping only MAX_BACKUPS most recent */
function cleanupOldBackups() {
    const backups = getBackupFiles();
    if (backups.length > MAX_BACKUPS) {
        const toDelete = backups.slice(MAX_BACKUPS);
        for (const backup of toDelete) {
            try {
                fs.unlinkSync(backup.path);
            }
            catch (error) {
                console.log((0, ui_1.warn)(`Failed to delete old backup ${formatDisplayPath(backup.path)}: ${error.message}`));
            }
        }
    }
}
function parseBackupTimestamp(timestamp) {
    const year = parseInt(timestamp.slice(0, 4), 10);
    const month = parseInt(timestamp.slice(4, 6), 10);
    const day = parseInt(timestamp.slice(6, 8), 10);
    const hour = parseInt(timestamp.slice(9, 11), 10);
    const minute = parseInt(timestamp.slice(11, 13), 10);
    const second = parseInt(timestamp.slice(13, 15), 10);
    const date = new Date(year, month - 1, day, hour, minute, second);
    if (date.getFullYear() !== year)
        return null;
    if (date.getMonth() !== month - 1)
        return null;
    if (date.getDate() !== day)
        return null;
    if (date.getHours() !== hour)
        return null;
    if (date.getMinutes() !== minute)
        return null;
    if (date.getSeconds() !== second)
        return null;
    return date;
}
/** Get all backup files sorted by date (newest first) */
function getBackupFiles() {
    const settingsPath = (0, claude_config_path_1.getClaudeSettingsPath)();
    const dir = path.dirname(settingsPath);
    if (!fs.existsSync(dir)) {
        return [];
    }
    const backupPattern = /^settings\.json\.backup\.(\d{8}_\d{6})$/;
    const files = fs
        .readdirSync(dir)
        .filter((f) => backupPattern.test(f))
        .map((f) => {
        const match = f.match(backupPattern);
        if (!match)
            return null;
        const timestamp = match[1];
        const date = parseBackupTimestamp(timestamp);
        if (!date)
            return null;
        return {
            path: path.join(dir, f),
            timestamp,
            date,
        };
    })
        .filter((f) => f !== null)
        .sort((a, b) => b.date.getTime() - a.date.getTime()); // newest first
    return files;
}
/** Mask API key for display (show first 4 and last 4 chars) */
function maskApiKey(key) {
    if (key.length <= 12) {
        return '****';
    }
    return `${key.slice(0, 4)}...${key.slice(-4)}`;
}
const SENSITIVE_ENV_PARTS = new Set([
    'TOKEN',
    'KEY',
    'SECRET',
    'PASSWORD',
    'PASS',
    'AUTH',
    'CREDENTIAL',
    'PRIVATE',
    'ACCESS',
    'REFRESH',
    'APIKEY',
]);
function splitSensitiveKeyParts(key) {
    const withCamelCaseBoundaries = key.replace(/([a-z0-9])([A-Z])/g, '$1_$2');
    return withCamelCaseBoundaries
        .toUpperCase()
        .split(/[^A-Z0-9]+/)
        .filter(Boolean);
}
function isSensitiveEnvKey(key) {
    const parts = splitSensitiveKeyParts(key);
    if (parts.some((part) => SENSITIVE_ENV_PARTS.has(part))) {
        return true;
    }
    const compact = parts.join('');
    return (compact.includes('TOKEN') ||
        compact.includes('APIKEY') ||
        compact.includes('ACCESSKEY') ||
        compact.includes('AUTHKEY') ||
        compact.includes('SECRET') ||
        compact.includes('PASSWORD') ||
        compact.includes('CREDENTIAL'));
}
function buildPersistReceipt(existingEnv, existingSettings, mergedSettings, resolved, resolvedPermissionMode) {
    const mergedEnv = typeof mergedSettings.env === 'object' &&
        mergedSettings.env !== null &&
        !Array.isArray(mergedSettings.env)
        ? mergedSettings.env
        : {};
    const clearedKeys = resolved.clearEnvKeys.filter((key) => Object.prototype.hasOwnProperty.call(existingEnv, key) && mergedEnv[key] === undefined);
    const clearedCodexTranslatorUrlKeys = clearedKeys.filter((key) => (0, stale_codex_translator_settings_1.findCodexTranslatorUrlPaths)(existingEnv[key]).length > 0);
    const writtenKeys = Object.entries(resolved.env)
        .filter(([key, value]) => existingEnv[key] !== value)
        .map(([key]) => key)
        .sort((left, right) => left.localeCompare(right));
    const unchangedWrittenKeys = Object.entries(resolved.env)
        .filter(([key, value]) => existingEnv[key] === value)
        .map(([key]) => key)
        .sort((left, right) => left.localeCompare(right));
    const existingPermissions = typeof existingSettings.permissions === 'object' &&
        existingSettings.permissions !== null &&
        !Array.isArray(existingSettings.permissions)
        ? existingSettings.permissions
        : {};
    const writtenSettings = resolvedPermissionMode && existingPermissions.defaultMode !== resolvedPermissionMode
        ? ['permissions.defaultMode']
        : [];
    const unchangedSettings = resolvedPermissionMode && existingPermissions.defaultMode === resolvedPermissionMode
        ? ['permissions.defaultMode']
        : [];
    return {
        clearedKeys,
        clearedCodexTranslatorUrlKeys,
        writtenKeys,
        unchangedWrittenKeys,
        writtenSettings,
        unchangedSettings,
        codexTranslatorUrlPaths: (0, stale_codex_translator_settings_1.findCodexTranslatorUrlPaths)(mergedSettings),
    };
}
function formatKeyList(keys) {
    return keys.length > 0 ? keys.join(', ') : 'none';
}
function printPersistReceipt(receipt) {
    console.log((0, ui_1.subheader)('Config Receipt'));
    console.log(`  Settings: ${getClaudeSettingsDisplayPath()}`);
    console.log(`  Cleared managed keys: ${formatKeyList(receipt.clearedKeys)}`);
    console.log(`  Written/rewritten managed keys: ${formatKeyList(receipt.writtenKeys)}`);
    if (receipt.unchangedWrittenKeys.length > 0) {
        console.log(`  Already current keys: ${formatKeyList(receipt.unchangedWrittenKeys)}`);
    }
    if (receipt.writtenSettings.length > 0 || receipt.unchangedSettings.length > 0) {
        console.log(`  Written/rewritten managed settings: ${formatKeyList(receipt.writtenSettings)}`);
        if (receipt.unchangedSettings.length > 0) {
            console.log(`  Already current settings: ${formatKeyList(receipt.unchangedSettings)}`);
        }
    }
    const hadCodexTranslatorCleanup = receipt.clearedCodexTranslatorUrlKeys.length > 0;
    if (receipt.codexTranslatorUrlPaths.length > 0) {
        console.log((0, ui_1.warn)(`  Codex translator URL: still found at ${(0, stale_codex_translator_settings_1.formatSettingsPathList)(receipt.codexTranslatorUrlPaths)} (${stale_codex_translator_settings_1.CODEX_TRANSLATOR_URL_MARKER})`));
    }
    else {
        console.log((0, ui_1.ok)('  Codex translator URL: not found'));
    }
    if (hadCodexTranslatorCleanup || receipt.codexTranslatorUrlPaths.length > 0) {
        console.log(`  Native Codex target: ${NATIVE_CODEX_TARGETS.join(' or ')}`);
    }
}
/** Resolve shared Claude settings payload for a profile */
async function resolveProfileEnvVars(profileName) {
    const setup = await (0, claude_extension_setup_1.resolveClaudeExtensionSetup)(profileName);
    const typeLabel = {
        settings: 'API',
        cliproxy: 'CLIProxy',
        copilot: 'Copilot',
        account: 'Account',
        default: 'Default',
    };
    return {
        env: setup.extensionEnv,
        clearEnvKeys: setup.removeEnvKeys,
        profileType: typeLabel[setup.profileType] ?? setup.profileType,
        warnings: setup.warnings,
        notes: setup.notes,
    };
}
/** Handle --list-backups flag */
async function handleListBackups() {
    await (0, ui_1.initUI)();
    const backups = getBackupFiles();
    if (backups.length === 0) {
        console.log((0, ui_1.info)('No backups found'));
        return;
    }
    console.log((0, ui_1.header)('Available Backups'));
    console.log('');
    backups.forEach((b, i) => {
        const dateStr = b.date.toLocaleString();
        const marker = i === 0 ? (0, ui_1.color)(' (latest)', 'success') : '';
        console.log(`  ${(0, ui_1.color)(b.timestamp, 'command')}  ${(0, ui_1.dim)(dateStr)}${marker}`);
    });
    console.log('');
    console.log((0, ui_1.dim)('To restore: ccs persist --restore [timestamp]'));
}
/** Handle --restore [timestamp] flag */
async function handleRestore(timestamp, yes) {
    await (0, ui_1.initUI)();
    const backups = getBackupFiles();
    if (backups.length === 0) {
        console.log((0, ui_1.fail)('No backups found'));
        process.exit(1);
    }
    // Find backup to restore
    let backup;
    if (timestamp === true) {
        // Use latest
        backup = backups[0];
    }
    else {
        const found = backups.find((b) => b.timestamp === timestamp);
        if (!found) {
            console.log((0, ui_1.fail)(`Backup not found: ${timestamp}`));
            console.log('');
            console.log('Available backups:');
            backups.slice(0, 5).forEach((b) => console.log(`  ${b.timestamp}`));
            process.exit(1);
        }
        backup = found;
    }
    console.log((0, ui_1.header)('Restore Backup'));
    console.log('');
    console.log(`Backup: ${(0, ui_1.color)(backup.timestamp, 'command')}`);
    console.log(`Date:   ${backup.date.toLocaleString()}`);
    console.log('');
    console.log((0, ui_1.warn)(`This will replace ${getClaudeSettingsDisplayPath()}`));
    console.log('');
    if (!yes) {
        const proceed = await prompt_1.InteractivePrompt.confirm('Proceed with restore?', { default: false });
        if (!proceed) {
            console.log((0, ui_1.info)('Cancelled'));
            process.exit(0);
        }
    }
    let parsedBackupSettings;
    try {
        const backupContent = await readFileUtf8NoFollow(backup.path);
        parsedBackupSettings = parseSettingsObject(backupContent, 'Backup file');
    }
    catch (error) {
        const nodeError = error;
        if (nodeError.code === 'ENOENT') {
            console.log((0, ui_1.fail)('Backup was deleted during restore'));
            process.exit(1);
        }
        if (nodeError.code === 'ELOOP') {
            console.log((0, ui_1.fail)('Backup file is a symlink - refusing to restore for security'));
            process.exit(1);
        }
        console.log((0, ui_1.fail)(`Backup file is corrupted: ${error.message}`));
        process.exit(1);
    }
    try {
        await withPersistSettingsLock(async () => {
            const settingsPath = (0, claude_config_path_1.getClaudeSettingsPath)();
            if (await isSymlinkAsync(settingsPath)) {
                throw new Error('settings.json is a symlink - refusing to restore for security');
            }
            let rollbackBackupPath = null;
            if (await pathExists(settingsPath)) {
                rollbackBackupPath = await createBackup();
            }
            try {
                await writeClaudeSettings(parsedBackupSettings);
            }
            catch (error) {
                const writeError = error;
                if (rollbackBackupPath) {
                    try {
                        const rollbackContent = await readFileUtf8NoFollow(rollbackBackupPath);
                        const rollbackSettings = parseSettingsObject(rollbackContent, 'Rollback backup');
                        await writeClaudeSettings(rollbackSettings);
                    }
                    catch (rollbackError) {
                        throw new Error(`Restore failed: ${writeError.message}. Rollback also failed: ${rollbackError.message}. Manual recovery backup: ${formatDisplayPath(rollbackBackupPath)}`);
                    }
                }
                throw new Error(`Restore failed: ${writeError.message}`);
            }
        });
    }
    catch (error) {
        console.log((0, ui_1.fail)(error.message));
        process.exit(1);
    }
    console.log((0, ui_1.ok)(`Restored from backup: ${backup.timestamp}`));
}
/** Show help for persist command */
async function showHelp() {
    await (0, ui_1.initUI)();
    console.log((0, ui_1.header)('CCS Persist Command'));
    console.log('');
    console.log((0, ui_1.subheader)('Usage'));
    console.log(`  ${(0, ui_1.color)('ccs persist', 'command')} <profile> [options]`);
    console.log(`  ${(0, ui_1.color)('ccs persist', 'command')} --list-backups`);
    console.log(`  ${(0, ui_1.color)('ccs persist', 'command')} --restore [timestamp]`);
    console.log('');
    console.log((0, ui_1.subheader)('Description'));
    console.log("  Writes a profile's Claude setup directly to");
    console.log(`  ${getClaudeSettingsDisplayPath()} for native Claude Code usage.`);
    console.log('');
    console.log('  This is the preferred shared-settings path for Claude Code');
    console.log('  and the Claude IDE extension when you want one profile everywhere.');
    console.log('');
    console.log((0, ui_1.subheader)('Options'));
    console.log(`  ${(0, ui_1.color)('--yes, -y', 'command')}         Skip confirmation prompts (auto-backup)`);
    console.log(`  ${(0, ui_1.color)('--permission-mode <mode>', 'command')}  Set default permission mode in settings.json`);
    console.log(`  ${(0, ui_1.color)('--dangerously-skip-permissions', 'command')}  Persist auto-approve (bypassPermissions)`);
    console.log(`  ${(0, ui_1.color)('--auto-approve', 'command')}  Alias for --dangerously-skip-permissions`);
    console.log(`  ${(0, ui_1.color)('--help, -h', 'command')}        Show this help message`);
    console.log('');
    console.log((0, ui_1.subheader)('Backup Management'));
    console.log(`  ${(0, ui_1.color)('--list-backups', 'command')}    List available backup files`);
    console.log(`  ${(0, ui_1.color)('--restore', 'command')}         Restore from the most recent backup`);
    console.log(`  ${(0, ui_1.color)('--restore <ts>', 'command')}    Restore from specific backup (e.g., 20260110_205324)`);
    console.log('');
    console.log((0, ui_1.subheader)('Supported Profile Types'));
    console.log(`  ${(0, ui_1.color)('API profiles', 'command')}      glm, km, custom API profiles`);
    console.log(`  ${(0, ui_1.color)('CLIProxy', 'command')}          gemini, agy, qwen, kiro, ghcp`);
    console.log(`  ${(0, ui_1.color)('Copilot', 'command')}           copilot (requires copilot-api daemon)`);
    console.log(`  ${(0, ui_1.color)('Account profiles', 'command')}  work, personal, client (persists CLAUDE_CONFIG_DIR)`);
    console.log(`  ${(0, ui_1.color)('default', 'command')}           Clears CCS-managed overrides or inherits mapped continuity`);
    console.log('');
    console.log((0, ui_1.subheader)('Examples'));
    console.log(`  ${(0, ui_1.dim)('# Persist GLM profile')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist glm', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# Persist with auto-confirmation')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist gemini --yes', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# Persist with default permission mode')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist glm --permission-mode acceptEdits', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# Persist with auto-approve enabled')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist glm --dangerously-skip-permissions', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# Persist an account profile for IDE/native Claude use')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist work --yes', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# Reset to native Claude defaults (clear CCS-managed overrides)')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist default --yes', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# List all backups')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist --list-backups', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# Restore latest backup')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist --restore', 'command')}`);
    console.log('');
    console.log(`  ${(0, ui_1.dim)('# Restore specific backup')}`);
    console.log(`  ${(0, ui_1.color)('ccs persist --restore 20260110_205324', 'command')}`);
    console.log('');
    console.log((0, ui_1.subheader)('Notes'));
    console.log('  [i] CLIProxy profiles require the proxy to be running.');
    console.log('  [i] Codex CLIProxy profiles are native Codex-only: use ccsxp or ccs codex --target codex.');
    console.log('  [i] Copilot profiles require copilot-api daemon.');
    console.log('  [i] Account/default flows remove stale ANTHROPIC_* overrides before applying new setup.');
    console.log('  [i] For IDE-local settings.json snippets, use: ccs env <profile> --format claude-extension');
    console.log(`  [i] Backups are saved as ${getClaudeSettingsDisplayPath()}.backup.YYYYMMDD_HHMMSS`);
    console.log('');
}
/** Main persist command handler */
async function handlePersistCommand(args) {
    // Check for help first
    if (args.includes('--help') || args.includes('-h') || args.length === 0) {
        await showHelp();
        return;
    }
    const parsedArgs = parseArgs(args);
    if (parsedArgs.parseError) {
        throw new Error(parsedArgs.parseError);
    }
    // Handle --list-backups
    if (parsedArgs.listBackups) {
        await handleListBackups();
        return;
    }
    // Handle --restore
    if (parsedArgs.restore) {
        await handleRestore(parsedArgs.restore, parsedArgs.yes ?? false);
        return;
    }
    await (0, ui_1.initUI)();
    const resolvedPermissionMode = resolvePermissionMode(parsedArgs);
    if (!parsedArgs.profile) {
        console.log((0, ui_1.fail)('Profile name is required'));
        console.log('');
        console.log('Usage:');
        console.log(`  ${(0, ui_1.color)('ccs persist <profile>', 'command')}`);
        console.log('');
        console.log('Run for help:');
        console.log(`  ${(0, ui_1.color)('ccs persist --help', 'command')}`);
        process.exit(1);
    }
    // Detect profile
    const detector = new profile_detector_1.default();
    try {
        detector.detectProfileType(parsedArgs.profile);
    }
    catch (error) {
        const err = error;
        console.log((0, ui_1.fail)(`Profile not found: ${parsedArgs.profile}`));
        console.log('');
        if (err.availableProfiles) {
            console.log(err.availableProfiles);
        }
        process.exit(1);
    }
    // Resolve env vars
    let resolved;
    try {
        resolved = await resolveProfileEnvVars(parsedArgs.profile);
    }
    catch (error) {
        console.log((0, ui_1.fail)(error.message));
        process.exit(1);
    }
    // Display what will be written
    console.log((0, ui_1.header)(`Persist Profile: ${parsedArgs.profile}`));
    console.log('');
    console.log(`Profile type: ${(0, ui_1.color)(resolved.profileType, 'command')}`);
    console.log('');
    const envKeys = Object.keys(resolved.env);
    if (envKeys.length > 0) {
        console.log(`The following env vars will be written to ${getClaudeSettingsDisplayPath()}:`);
        console.log('');
        const maxKeyLen = Math.max(...envKeys.map((k) => k.length));
        for (const [key, value] of Object.entries(resolved.env)) {
            const paddedKey = key.padEnd(maxKeyLen + 2);
            const displayValue = isSensitiveEnvKey(key) ? maskApiKey(value) : value;
            console.log(`  ${(0, ui_1.color)(paddedKey, 'command')} = ${displayValue}`);
        }
        console.log('');
    }
    else {
        console.log((0, ui_1.info)('No new env vars will be added.'));
        console.log((0, ui_1.dim)('    CCS-managed transport overrides will be removed if present.'));
        console.log('');
    }
    if (resolved.clearEnvKeys.length > 0) {
        console.log('Managed env keys replaced/cleared on write:');
        console.log(`  ${(0, ui_1.dim)(resolved.clearEnvKeys.join(', '))}`);
        console.log('');
    }
    if (resolvedPermissionMode) {
        console.log(`Default permission mode: ${(0, ui_1.color)(resolvedPermissionMode, 'command')}`);
        if (resolvedPermissionMode === 'bypassPermissions') {
            console.log((0, ui_1.warn)('Auto-approve enabled: Claude will skip permission prompts by default.'));
        }
        console.log('');
    }
    if (resolved.warnings?.length) {
        for (const message of resolved.warnings) {
            console.log((0, ui_1.warn)(message));
        }
        console.log('');
    }
    if (resolved.notes?.length) {
        for (const note of resolved.notes) {
            console.log((0, ui_1.info)(note));
        }
        console.log('');
    }
    // Warning about modification
    console.log((0, ui_1.warn)(`This will modify ${getClaudeSettingsDisplayPath()}`));
    console.log((0, ui_1.dim)('    Existing hooks and other settings will be preserved.'));
    console.log((0, ui_1.dim)('    Existing managed profile env keys will be replaced to avoid stale routing.'));
    console.log('');
    // Check if settings.json exists for backup
    const settingsPath = (0, claude_config_path_1.getClaudeSettingsPath)();
    const settingsExist = fs.existsSync(settingsPath);
    let createBackupFlag = false;
    // Track backup path for error recovery guidance
    let createdBackupPath = null;
    // Backup prompt (unless --yes)
    if (settingsExist) {
        createBackupFlag = parsedArgs.yes === true; // Auto-backup with --yes
        if (!parsedArgs.yes) {
            createBackupFlag = await prompt_1.InteractivePrompt.confirm('Create backup before modifying?', {
                default: true,
            });
        }
    }
    // Proceed confirmation (unless --yes)
    if (!parsedArgs.yes) {
        const proceed = await prompt_1.InteractivePrompt.confirm('Proceed with persist?', { default: true });
        if (!proceed) {
            console.log((0, ui_1.info)('Cancelled'));
            process.exit(0);
        }
    }
    try {
        await withPersistSettingsLock(async () => {
            if (createBackupFlag && (await pathExists(settingsPath))) {
                try {
                    createdBackupPath = await createBackup();
                    console.log((0, ui_1.ok)(`Backup created: ${formatDisplayPath(createdBackupPath)}`));
                    console.log('');
                }
                catch (error) {
                    throw new Error(`Failed to create backup: ${error.message}`);
                }
            }
            // Read existing settings and merge
            const existingSettings = await readClaudeSettings();
            // Validate existing env is an object (not array/primitive)
            const rawEnv = existingSettings.env;
            let existingEnv = {};
            if (rawEnv !== undefined) {
                if (rawEnv === null) {
                    console.log((0, ui_1.warn)('Existing env in settings.json is null - it will be replaced'));
                }
                else if (typeof rawEnv !== 'object' || Array.isArray(rawEnv)) {
                    console.log((0, ui_1.warn)('Existing env in settings.json is not an object - it will be replaced'));
                }
                else {
                    existingEnv = rawEnv;
                }
            }
            const preservedEnv = { ...existingEnv };
            for (const key of resolved.clearEnvKeys) {
                delete preservedEnv[key];
            }
            const mergedSettings = {
                ...existingSettings,
                env: {
                    ...preservedEnv,
                    ...resolved.env,
                },
            };
            if (resolvedPermissionMode) {
                const rawPermissions = existingSettings.permissions;
                let existingPermissions = {};
                if (rawPermissions !== undefined) {
                    if (rawPermissions === null) {
                        console.log((0, ui_1.warn)('Existing permissions in settings.json is null - it will be replaced'));
                    }
                    else if (typeof rawPermissions !== 'object' || Array.isArray(rawPermissions)) {
                        console.log((0, ui_1.warn)('Existing permissions in settings.json is not an object - it will be replaced'));
                    }
                    else {
                        existingPermissions = rawPermissions;
                    }
                }
                mergedSettings.permissions = {
                    ...existingPermissions,
                    defaultMode: resolvedPermissionMode,
                };
            }
            await writeClaudeSettings(mergedSettings);
            const persistedSettings = await readClaudeSettings();
            const receipt = buildPersistReceipt(existingEnv, existingSettings, persistedSettings, resolved, resolvedPermissionMode);
            console.log('');
            console.log((0, ui_1.ok)(`Profile '${parsedArgs.profile}' written to ${getClaudeSettingsDisplayPath()}`));
            console.log('');
            printPersistReceipt(receipt);
            console.log('');
        });
    }
    catch (error) {
        const message = error.message;
        if (message.startsWith('Failed to create backup:')) {
            console.log((0, ui_1.fail)(message));
        }
        else {
            console.log((0, ui_1.fail)(`Failed to write settings: ${message}`));
        }
        if (createdBackupPath) {
            console.log('');
            console.log((0, ui_1.info)(`A backup was created before this error:`));
            console.log(`    ${formatDisplayPath(createdBackupPath)}`);
            console.log((0, ui_1.dim)('    To restore: ccs persist --restore'));
        }
        process.exit(1);
    }
    console.log((0, ui_1.info)('Claude Code will now use this profile by default.'));
    console.log((0, ui_1.dim)('    To revert, restore the backup or edit settings.json manually.'));
    console.log('');
}
exports.handlePersistCommand = handlePersistCommand;
//# sourceMappingURL=persist-command.js.map