diff --git a/scripts/teardown-ai-workspace.sh b/scripts/teardown-ai-workspace.sh new file mode 100755 index 0000000..624a6c1 --- /dev/null +++ b/scripts/teardown-ai-workspace.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$DIR")" + +echo "Starting AI-Relay-Kit Cleanup..." +cd "$PROJECT_ROOT" + +# Ensure dependencies are installed +if [ ! -d "node_modules" ]; then + echo "Dependencies not found, installing just in case..." + npm install +fi + +# Run the cleaner +npx tsx src/injector/clean.ts + +echo "Done." diff --git a/src/injector/clean.ts b/src/injector/clean.ts new file mode 100644 index 0000000..5481047 --- /dev/null +++ b/src/injector/clean.ts @@ -0,0 +1,18 @@ +import { cleanCodexConfig, cleanEnvConfigs } from './cleaner'; + +function runCleaner() { + console.log('--- Starting AI-Relay-Kit Config Cleanup ---'); + + // 1. Clean Codex + cleanCodexConfig(); + + // 2. Clean Environment Configs (Claude & Antigravity) + cleanEnvConfigs(); + + console.log('--- Cleanup Complete ---'); + console.log('\n💡 Your AI Workspace has been restored to its pure, pristine state.'); + console.log(' Please restart your terminal to apply the removed environment variables,'); + console.log(' or manually run `unset ANTHROPIC_BASE_URL OPENAI_BASE_URL` in current sessions.'); +} + +runCleaner(); diff --git a/src/injector/cleaner.ts b/src/injector/cleaner.ts new file mode 100644 index 0000000..f3b5712 --- /dev/null +++ b/src/injector/cleaner.ts @@ -0,0 +1,87 @@ +import fs from 'fs'; +import path from 'path'; +import os from 'os'; + +export function cleanCodexConfig() { + const codexConfigPath = path.join(os.homedir(), '.codex', 'config.toml'); + const codexCatalogPath = path.join(os.homedir(), '.codex', 'custom_model_catalog.json'); + + if (fs.existsSync(codexCatalogPath)) { + fs.unlinkSync(codexCatalogPath); + console.log(`[Codex Cleaner] Removed custom model catalog: ${codexCatalogPath}`); + } + + if (fs.existsSync(codexConfigPath)) { + let tomlContent = fs.readFileSync(codexConfigPath, 'utf8'); + let originalLength = tomlContent.length; + + // 1. Remove top-level injected configurations + tomlContent = tomlContent.replace(/^model_provider\s*=\s*".*?"\r?\n?/gm, ''); + tomlContent = tomlContent.replace(/^model_catalog_json\s*=\s*".*?"\r?\n?/gm, ''); + + // 2. Remove AI-Relay-Kit injected block + const markerStart = '\n# --- UNIFIED RELAY CONFIG START ---'; + const markerEnd = '# --- UNIFIED RELAY CONFIG END ---\n'; + + // Some older iterations might not have the marker, so let's also specifically target the deepseek-relay block if it exists + const fallbackRegex1 = /\[model_providers\.unified-relay\][\s\S]*?(?=\n\[|$)/g; + const fallbackRegex2 = /\[model_providers\.deepseek-relay\][\s\S]*?(?=\n\[|$)/g; + + if (tomlContent.includes(markerStart)) { + const regex = new RegExp(`${markerStart}[\\s\\S]*?${markerEnd}`, 'g'); + tomlContent = tomlContent.replace(regex, ''); + } else { + tomlContent = tomlContent.replace(fallbackRegex1, ''); + tomlContent = tomlContent.replace(fallbackRegex2, ''); + } + + // Optional: Remove specific XWorkmate block if desired (as per user context) + const xworkmateStart = '# BEGIN XWORKMATE MANAGED MCP BLOCK'; + const xworkmateEnd = '# END XWORKMATE MANAGED MCP BLOCK'; + if (tomlContent.includes(xworkmateStart)) { + const xworkRegex = new RegExp(`${xworkmateStart}[\\s\\S]*?${xworkmateEnd}\r?\n?`, 'g'); + tomlContent = tomlContent.replace(xworkRegex, ''); + console.log(`[Codex Cleaner] Removed XWorkmate managed MCP block`); + } + + if (tomlContent.length !== originalLength) { + fs.writeFileSync(codexConfigPath, tomlContent.trim() + '\n', 'utf8'); + console.log(`[Codex Cleaner] Restored ~/.codex/config.toml to pristine state.`); + } else { + console.log(`[Codex Cleaner] ~/.codex/config.toml is already clean.`); + } + } +} + +export function cleanEnvConfigs() { + const envFilePath = path.join(os.homedir(), '.ai-relay-kit.env'); + const iacDir = path.join(os.homedir(), '.ai-relay-kit'); + + // 1. Delete the generated env file + if (fs.existsSync(envFilePath)) { + fs.unlinkSync(envFilePath); + console.log(`[Env Cleaner] Deleted ${envFilePath}`); + } + + // 2. Delete the IaC directory + if (fs.existsSync(iacDir)) { + fs.rmSync(iacDir, { recursive: true, force: true }); + console.log(`[Env Cleaner] Deleted IaC output directory ${iacDir}`); + } + + // 3. Remove source lines from .bashrc and .zshrc + const rcFiles = ['.zshrc', '.bashrc'].map(f => path.join(os.homedir(), f)); + for (const rcPath of rcFiles) { + if (fs.existsSync(rcPath)) { + let content = fs.readFileSync(rcPath, 'utf8'); + if (content.includes('.ai-relay-kit.env')) { + const regex = /^.*source\s+.*\.ai-relay-kit\.env.*$/gm; + content = content.replace(regex, ''); + fs.writeFileSync(rcPath, content, 'utf8'); + console.log(`[Env Cleaner] Removed source line from ${rcPath}`); + } + } + } + + console.log(`[Env Cleaner] Claude Code and Antigravity third-party environment variables cleaned.`); +}