import { stat } from 'fs/promises' import { homedir } from 'os' import { join } from 'path' import { getGlobalConfig, saveGlobalConfig } from './config.js' import { execFileNoThrow } from './execFileNoThrow.js' import { logError } from './log.js' export function markTerminalSetupInProgress(backupPath: string): void { saveGlobalConfig(current => ({ ...current, appleTerminalSetupInProgress: true, appleTerminalBackupPath: backupPath, })) } export function markTerminalSetupComplete(): void { saveGlobalConfig(current => ({ ...current, appleTerminalSetupInProgress: false, })) } function getTerminalRecoveryInfo(): { inProgress: boolean backupPath: string | null } { const config = getGlobalConfig() return { inProgress: config.appleTerminalSetupInProgress ?? false, backupPath: config.appleTerminalBackupPath || null, } } export function getTerminalPlistPath(): string { return join(homedir(), 'Library', 'Preferences', 'com.apple.Terminal.plist') } export async function backupTerminalPreferences(): Promise { const terminalPlistPath = getTerminalPlistPath() const backupPath = `${terminalPlistPath}.bak` try { const { code } = await execFileNoThrow('defaults', [ 'export', 'com.apple.Terminal', terminalPlistPath, ]) if (code !== 0) { return null } try { await stat(terminalPlistPath) } catch { return null } await execFileNoThrow('defaults', [ 'export', 'com.apple.Terminal', backupPath, ]) markTerminalSetupInProgress(backupPath) return backupPath } catch (error) { logError(error) return null } } type RestoreResult = | { status: 'restored' | 'no_backup' } | { status: 'failed' backupPath: string } export async function checkAndRestoreTerminalBackup(): Promise { const { inProgress, backupPath } = getTerminalRecoveryInfo() if (!inProgress) { return { status: 'no_backup' } } if (!backupPath) { markTerminalSetupComplete() return { status: 'no_backup' } } try { await stat(backupPath) } catch { markTerminalSetupComplete() return { status: 'no_backup' } } try { const { code } = await execFileNoThrow('defaults', [ 'import', 'com.apple.Terminal', backupPath, ]) if (code !== 0) { return { status: 'failed', backupPath } } await execFileNoThrow('killall', ['cfprefsd']) markTerminalSetupComplete() return { status: 'restored' } } catch (restoreError) { logError( new Error( `Failed to restore Terminal.app settings with: ${restoreError}`, ), ) markTerminalSetupComplete() return { status: 'failed', backupPath } } }