mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-20 05:11:48 +00:00
refactor(config): migrate to config.yaml as primary format
- Update postinstall.js to create config.yaml instead of config.json - Update recovery-manager to create config.yaml as primary config - Fix isFirstTimeInstall() to check for meaningful config content (profiles, accounts, variants, oauth_accounts, remote proxy) - Update validation to accept config.yaml OR config.json - Preserve backward compatibility: legacy config.json is migrated to config.yaml on first run via autoMigrate() - Update postinstall tests to verify config.yaml creation Fixes #142 - remote CLIProxyAPI configuration
This commit is contained in:
1 parent
cec616d530
commit
b34469d75f
4 files changed
+147
-106
No files matched your search
@@ -93,10 +93,31 @@ async function selectOption(
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a first-time install (no config exists)
|
||||
* Check if this is a first-time install (config exists but is empty/unconfigured)
|
||||
* Returns true if user should be prompted to run setup wizard
|
||||
*/
|
||||
export function isFirstTimeInstall(): boolean {
|
||||
return !hasUnifiedConfig();
|
||||
// No config at all → definitely first time
|
||||
if (!hasUnifiedConfig()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Config exists - check if it's meaningfully configured
|
||||
const config = loadOrCreateUnifiedConfig();
|
||||
|
||||
// Check for any meaningful configuration
|
||||
const hasProfiles = Object.keys(config.profiles || {}).length > 0;
|
||||
const hasAccounts = Object.keys(config.accounts || {}).length > 0;
|
||||
const hasVariants = Object.keys(config.cliproxy?.variants || {}).length > 0;
|
||||
const hasOAuthAccounts = Object.keys(config.cliproxy?.oauth_accounts || {}).length > 0;
|
||||
const hasRemoteProxy =
|
||||
config.cliproxy_server?.remote?.enabled && config.cliproxy_server?.remote?.host;
|
||||
|
||||
// If any of these exist, user has configured something
|
||||
const isConfigured =
|
||||
hasProfiles || hasAccounts || hasVariants || hasOAuthAccounts || hasRemoteProxy;
|
||||
|
||||
return !isConfigured;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,8 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { info } from '../utils/ui';
|
||||
import { createEmptyUnifiedConfig, UNIFIED_CONFIG_VERSION } from '../config/unified-config-types';
|
||||
import { saveUnifiedConfig, hasUnifiedConfig } from '../config/unified-config-loader';
|
||||
|
||||
/**
|
||||
* Get CCS home directory (respects CCS_HOME env for test isolation)
|
||||
@@ -49,37 +51,39 @@ class RecoveryManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure ~/.ccs/config.json exists with defaults
|
||||
* Ensure ~/.ccs/config.yaml exists with defaults
|
||||
* This is the primary config format (YAML unified config)
|
||||
*/
|
||||
ensureConfigJson(): boolean {
|
||||
const configPath = path.join(this.ccsDir, 'config.json');
|
||||
|
||||
// Check if exists and valid
|
||||
if (fs.existsSync(configPath)) {
|
||||
try {
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
JSON.parse(content); // Validate JSON
|
||||
return false; // No recovery needed
|
||||
} catch (_e) {
|
||||
// Corrupted - backup and recreate
|
||||
const backupPath = `${configPath}.backup.${Date.now()}`;
|
||||
fs.renameSync(configPath, backupPath);
|
||||
this.recovered.push(`Backed up corrupted config.json to ${path.basename(backupPath)}`);
|
||||
}
|
||||
ensureConfigYaml(): boolean {
|
||||
// Skip if config.yaml already exists
|
||||
if (hasUnifiedConfig()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create default config (matches postinstall.js)
|
||||
// NOTE: Empty profiles - users create profiles via `ccs api create` or UI
|
||||
const defaultConfig = {
|
||||
profiles: {},
|
||||
};
|
||||
// Check for legacy config.json - if exists, let autoMigrate handle it
|
||||
const legacyConfigPath = path.join(this.ccsDir, 'config.json');
|
||||
if (fs.existsSync(legacyConfigPath)) {
|
||||
// Legacy config exists - autoMigrate() in ccs.ts will handle migration
|
||||
return false;
|
||||
}
|
||||
|
||||
const tmpPath = `${configPath}.tmp`;
|
||||
fs.writeFileSync(tmpPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf8');
|
||||
fs.renameSync(tmpPath, configPath);
|
||||
// Create fresh config.yaml with defaults
|
||||
const config = createEmptyUnifiedConfig();
|
||||
config.version = UNIFIED_CONFIG_VERSION;
|
||||
|
||||
this.recovered.push('Created ~/.ccs/config.json');
|
||||
return true;
|
||||
try {
|
||||
saveUnifiedConfig(config);
|
||||
this.recovered.push('Created ~/.ccs/config.yaml');
|
||||
return true;
|
||||
} catch (_e) {
|
||||
// Fallback: create minimal config.json for backward compat
|
||||
const fallbackConfig = { profiles: {} };
|
||||
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)');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,8 +285,8 @@ class RecoveryManager {
|
||||
this.ensureSharedDirectories();
|
||||
this.ensureClaudeSettings();
|
||||
|
||||
// Config files (core only - no GLM/GLMT/Kimi auto-creation)
|
||||
this.ensureConfigJson();
|
||||
// Config files - use YAML as primary format
|
||||
this.ensureConfigYaml();
|
||||
|
||||
// Shell completions
|
||||
this.ensureShellCompletions();
|
||||
|
||||
Reference in new issue
Block a user