mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 22:16:41 +00:00
refactor(config): DRY precedence logic, dynamic recovery messages, CCS_HOME cloud warning
- Extract shared _resolveCcsDir() to prevent getCcsDir/getCcsDirSource divergence - Replace all hardcoded ~/.ccs/ and ~/.claude/ in recovery-manager.ts with dynamic paths from instance properties (ccsDir, claudeDir, sharedDir, etc.) - Add cloud sync detection for CCS_HOME env var (parity with CCS_DIR/--config-dir) - Cache getSessionSecretPath() in local var in auth-middleware.ts - Cache getCacheDir() in local var in disk-cache.ts ensureCacheDir()
This commit is contained in:
@@ -318,6 +318,14 @@ async function main(): Promise<void> {
|
||||
console.error(warn(`CCS directory is under ${cloudService}.`));
|
||||
console.error(' OAuth tokens in cliproxy/auth/ will be synced to cloud.');
|
||||
}
|
||||
} else if (process.env.CCS_HOME) {
|
||||
// Also warn for CCS_HOME env var pointing to cloud sync
|
||||
const { detectCloudSyncPath } = await import('./utils/config-manager');
|
||||
const cloudService = detectCloudSyncPath(process.env.CCS_HOME);
|
||||
if (cloudService) {
|
||||
console.error(warn(`CCS directory is under ${cloudService}.`));
|
||||
console.error(' OAuth tokens in cliproxy/auth/ will be synced to cloud.');
|
||||
}
|
||||
}
|
||||
|
||||
const firstArg = args[0];
|
||||
|
||||
@@ -41,7 +41,7 @@ class RecoveryManager {
|
||||
ensureCcsDirectory(): boolean {
|
||||
if (!fs.existsSync(this.ccsDir)) {
|
||||
fs.mkdirSync(this.ccsDir, { recursive: true, mode: 0o755 });
|
||||
this.recovered.push('Created ~/.ccs/ directory');
|
||||
this.recovered.push(`Created ${this.ccsDir} directory`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -60,7 +60,7 @@ class RecoveryManager {
|
||||
return false; // Config exists and is valid
|
||||
}
|
||||
// Config exists but is corrupted - will be recreated below
|
||||
this.recovered.push('Detected corrupted ~/.ccs/config.yaml');
|
||||
this.recovered.push(`Detected corrupted ${this.ccsDir}/config.yaml`);
|
||||
}
|
||||
|
||||
// Check for legacy config.json - if exists, let autoMigrate handle it
|
||||
@@ -76,7 +76,7 @@ class RecoveryManager {
|
||||
|
||||
try {
|
||||
saveUnifiedConfig(config);
|
||||
this.recovered.push('Created ~/.ccs/config.yaml');
|
||||
this.recovered.push(`Created ${this.ccsDir}/config.yaml`);
|
||||
return true;
|
||||
} catch (_saveErr) {
|
||||
// Fallback: create minimal config.json for backward compat
|
||||
@@ -85,7 +85,7 @@ class RecoveryManager {
|
||||
const tmpPath = `${legacyConfigPath}.tmp`;
|
||||
fs.writeFileSync(tmpPath, JSON.stringify(fallbackConfig, null, 2) + '\n', 'utf8');
|
||||
fs.renameSync(tmpPath, legacyConfigPath);
|
||||
this.recovered.push('Created ~/.ccs/config.json (fallback)');
|
||||
this.recovered.push(`Created ${this.ccsDir}/config.json (fallback)`);
|
||||
return true;
|
||||
} catch (_fallbackErr) {
|
||||
// Both writes failed - log but don't crash
|
||||
@@ -104,7 +104,7 @@ class RecoveryManager {
|
||||
// Create ~/.claude/ if missing
|
||||
if (!fs.existsSync(this.claudeDir)) {
|
||||
fs.mkdirSync(this.claudeDir, { recursive: true, mode: 0o755 });
|
||||
this.recovered.push('Created ~/.claude/ directory');
|
||||
this.recovered.push(`Created ${this.claudeDir} directory`);
|
||||
}
|
||||
|
||||
// Create settings.json if missing
|
||||
@@ -113,7 +113,7 @@ class RecoveryManager {
|
||||
fs.writeFileSync(tmpPath, '{}\n', 'utf8');
|
||||
fs.renameSync(tmpPath, claudeSettingsPath);
|
||||
|
||||
this.recovered.push('Created ~/.claude/settings.json');
|
||||
this.recovered.push(`Created ${claudeSettingsPath}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ class RecoveryManager {
|
||||
// Create shared directory
|
||||
if (!fs.existsSync(this.sharedDir)) {
|
||||
fs.mkdirSync(this.sharedDir, { recursive: true, mode: 0o755 });
|
||||
this.recovered.push('Created ~/.ccs/shared/');
|
||||
this.recovered.push(`Created ${this.sharedDir}`);
|
||||
created = true;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ class RecoveryManager {
|
||||
}
|
||||
|
||||
if (installed) {
|
||||
this.recovered.push('Installed shell completions to ~/.ccs/completions/');
|
||||
this.recovered.push(`Installed shell completions to ${this.completionsDir}`);
|
||||
}
|
||||
|
||||
return installed;
|
||||
@@ -238,7 +238,7 @@ class RecoveryManager {
|
||||
this.recovered.forEach((msg) => console.log(` - ${msg}`));
|
||||
|
||||
// Show login hint if created Claude settings
|
||||
if (this.recovered.some((msg) => msg.includes('~/.claude/settings.json'))) {
|
||||
if (this.recovered.some((msg) => msg.includes('settings.json') && msg.includes('.claude'))) {
|
||||
console.log('');
|
||||
console.log(info('Next step: Login to Claude CLI'));
|
||||
console.log(' Run: claude /login');
|
||||
|
||||
@@ -30,16 +30,25 @@ export function getCcsHome(): string {
|
||||
return process.env.CCS_HOME || os.homedir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the CCS directory with source information.
|
||||
* Single source of truth for precedence logic.
|
||||
*/
|
||||
function _resolveCcsDir(): { source: string; dir: string } {
|
||||
if (_globalConfigDir) return { source: '--config-dir', dir: _globalConfigDir };
|
||||
if (process.env.CCS_DIR) return { source: 'CCS_DIR', dir: path.resolve(process.env.CCS_DIR) };
|
||||
if (process.env.CCS_HOME)
|
||||
return { source: 'CCS_HOME', dir: path.join(path.resolve(process.env.CCS_HOME), '.ccs') };
|
||||
return { source: 'default', dir: path.join(os.homedir(), '.ccs') };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CCS directory path.
|
||||
* Precedence: --config-dir flag > CCS_DIR env > CCS_HOME env (legacy, appends .ccs) > ~/.ccs default
|
||||
* @returns Path to CCS config directory
|
||||
*/
|
||||
export function getCcsDir(): string {
|
||||
if (_globalConfigDir) return _globalConfigDir;
|
||||
if (process.env.CCS_DIR) return path.resolve(process.env.CCS_DIR);
|
||||
if (process.env.CCS_HOME) return path.join(path.resolve(process.env.CCS_HOME), '.ccs');
|
||||
return path.join(os.homedir(), '.ccs');
|
||||
return _resolveCcsDir().dir;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,11 +56,8 @@ export function getCcsDir(): string {
|
||||
* @returns [source_label, resolved_path] tuple
|
||||
*/
|
||||
export function getCcsDirSource(): [string, string] {
|
||||
if (_globalConfigDir) return ['--config-dir', _globalConfigDir];
|
||||
if (process.env.CCS_DIR) return ['CCS_DIR', path.resolve(process.env.CCS_DIR)];
|
||||
if (process.env.CCS_HOME)
|
||||
return ['CCS_HOME', path.join(path.resolve(process.env.CCS_HOME), '.ccs')];
|
||||
return ['default', path.join(os.homedir(), '.ccs')];
|
||||
const r = _resolveCcsDir();
|
||||
return [r.source, r.dir];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,10 +38,12 @@ function getSessionSecret(): string {
|
||||
return process.env.CCS_SESSION_SECRET;
|
||||
}
|
||||
|
||||
const secretPath = getSessionSecretPath();
|
||||
|
||||
// 2. Try to read persisted secret
|
||||
try {
|
||||
if (fs.existsSync(getSessionSecretPath())) {
|
||||
const secret = fs.readFileSync(getSessionSecretPath(), 'utf-8').trim();
|
||||
if (fs.existsSync(secretPath)) {
|
||||
const secret = fs.readFileSync(secretPath, 'utf-8').trim();
|
||||
if (secret.length >= 32) {
|
||||
return secret;
|
||||
}
|
||||
@@ -53,11 +55,11 @@ function getSessionSecret(): string {
|
||||
// 3. Generate and persist new random secret
|
||||
const newSecret = crypto.randomBytes(32).toString('hex');
|
||||
try {
|
||||
const dir = path.dirname(getSessionSecretPath());
|
||||
const dir = path.dirname(secretPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(getSessionSecretPath(), newSecret, { mode: 0o600 });
|
||||
fs.writeFileSync(secretPath, newSecret, { mode: 0o600 });
|
||||
} catch (err) {
|
||||
// Log warning - sessions won't persist across restarts
|
||||
console.warn('[!] Failed to persist session secret:', (err as Error).message);
|
||||
|
||||
@@ -42,8 +42,9 @@ const CACHE_VERSION = 3;
|
||||
* Ensure ~/.ccs/cache directory exists
|
||||
*/
|
||||
function ensureCacheDir(): void {
|
||||
if (!fs.existsSync(getCacheDir())) {
|
||||
fs.mkdirSync(getCacheDir(), { recursive: true });
|
||||
const dir = getCacheDir();
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user