mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 08:19:59 +00:00
Merge pull request #58 from kaitranntt/dev
feat(release): promote dev to main - v5.6.0
This commit is contained in:
@@ -8,9 +8,13 @@ on:
|
||||
- 'scripts/wrangler.toml'
|
||||
- 'installers/**'
|
||||
|
||||
# Run on all PRs to satisfy branch protection (no path filter)
|
||||
# Only run on PRs that touch installer-related files
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'scripts/worker.js'
|
||||
- 'scripts/wrangler.toml'
|
||||
- 'installers/**'
|
||||
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/iflow",
|
||||
"ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed",
|
||||
"ANTHROPIC_MODEL": "deepseek-v3.2",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-k2-thinking",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v3.2",
|
||||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "minimax-m2"
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/qwen",
|
||||
"ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed",
|
||||
"ANTHROPIC_MODEL": "qwen3-coder",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "qwen3-coder",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen3-coder",
|
||||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "qwen3-coder"
|
||||
"ANTHROPIC_MODEL": "qwen3-coder-plus",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "qwen3-coder-plus",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen3-coder-plus",
|
||||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "qwen3-vl-plus"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kaitranntt/ccs",
|
||||
"version": "5.6.0",
|
||||
"version": "5.6.0-dev.3",
|
||||
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
+2
-1
@@ -228,7 +228,8 @@ async function main(): Promise<void> {
|
||||
|
||||
// Special case: doctor command
|
||||
if (firstArg === 'doctor' || firstArg === '--doctor') {
|
||||
await handleDoctorCommand();
|
||||
const shouldFix = args.includes('--fix') || args.includes('-f');
|
||||
await handleDoctorCommand(shouldFix);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
* Each provider has its own directory to avoid conflicts.
|
||||
*/
|
||||
|
||||
import { execSync, spawn } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { spawn, execSync } from 'child_process';
|
||||
import { ProgressIndicator } from '../utils/progress-indicator';
|
||||
import { getProviderAuthDir, generateConfig } from './config-generator';
|
||||
import { ensureCLIProxyBinary } from './binary-manager';
|
||||
import { generateConfig, getProviderAuthDir } from './config-generator';
|
||||
import { CLIProxyProvider } from './types';
|
||||
|
||||
/**
|
||||
@@ -169,6 +169,13 @@ const OAUTH_CONFIGS: Record<CLIProxyProvider, ProviderOAuthConfig> = {
|
||||
scopes: ['openid', 'profile', 'email', 'model.completion'],
|
||||
authFlag: '--qwen-login',
|
||||
},
|
||||
iflow: {
|
||||
provider: 'iflow',
|
||||
displayName: 'iFlow',
|
||||
authUrl: 'https://iflow.cn/oauth',
|
||||
scopes: ['phone', 'profile', 'email'],
|
||||
authFlag: '--iflow-login',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -199,6 +206,7 @@ const PROVIDER_AUTH_PREFIXES: Record<CLIProxyProvider, string[]> = {
|
||||
codex: ['codex-', 'openai-'],
|
||||
agy: ['antigravity-', 'agy-'],
|
||||
qwen: ['qwen-'],
|
||||
iflow: ['iflow-'],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -210,6 +218,7 @@ const PROVIDER_TYPE_VALUES: Record<CLIProxyProvider, string[]> = {
|
||||
codex: ['codex'],
|
||||
agy: ['antigravity'],
|
||||
qwen: ['qwen'],
|
||||
iflow: ['iflow'],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -327,7 +336,7 @@ export function getAuthStatus(provider: CLIProxyProvider): AuthStatus {
|
||||
* Get auth status for all providers
|
||||
*/
|
||||
export function getAllAuthStatus(): AuthStatus[] {
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen'];
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow'];
|
||||
return providers.map(getAuthStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { getCcsDir } from '../utils/config-manager';
|
||||
import { CLIProxyProvider, ProviderConfig, ProviderModelMapping } from './types';
|
||||
import { getModelMappingFromConfig } from './base-config-loader';
|
||||
import { getModelMappingFromConfig, getEnvVarsFromConfig } from './base-config-loader';
|
||||
|
||||
/** Settings file structure for user overrides */
|
||||
interface ProviderSettings {
|
||||
@@ -31,6 +31,7 @@ const PROVIDER_DISPLAY_NAMES: Record<CLIProxyProvider, string> = {
|
||||
codex: 'Codex',
|
||||
agy: 'Antigravity',
|
||||
qwen: 'Qwen Code',
|
||||
iflow: 'iFlow',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -197,7 +198,11 @@ export function getClaudeEnvVars(
|
||||
): NodeJS.ProcessEnv {
|
||||
const models = getModelMapping(provider);
|
||||
|
||||
return {
|
||||
// Base env vars from config file (includes ANTHROPIC_MAX_TOKENS, etc.)
|
||||
const baseEnvVars = getEnvVarsFromConfig(provider);
|
||||
|
||||
// Core env vars that we always set dynamically
|
||||
const coreEnvVars = {
|
||||
// Provider-specific endpoint - routes to correct provider via URL path
|
||||
ANTHROPIC_BASE_URL: `http://127.0.0.1:${port}/api/provider/${provider}`,
|
||||
ANTHROPIC_AUTH_TOKEN: CCS_INTERNAL_API_KEY,
|
||||
@@ -206,6 +211,23 @@ export function getClaudeEnvVars(
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: models.sonnetModel || models.claudeModel,
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: models.haikuModel || models.claudeModel,
|
||||
};
|
||||
|
||||
// Filter out core env vars from base config to avoid conflicts
|
||||
const {
|
||||
ANTHROPIC_BASE_URL: _baseUrl,
|
||||
ANTHROPIC_AUTH_TOKEN: _authToken,
|
||||
ANTHROPIC_MODEL: _model,
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: _opusModel,
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: _sonnetModel,
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: _haikuModel,
|
||||
...additionalEnvVars
|
||||
} = baseEnvVars;
|
||||
|
||||
// Merge core env vars with additional env vars from base config
|
||||
return {
|
||||
...coreEnvVars,
|
||||
...additionalEnvVars, // Includes ANTHROPIC_MAX_TOKENS, etc.
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,7 @@ export interface BinaryManagerConfig {
|
||||
/** Enable verbose logging */
|
||||
verbose: boolean;
|
||||
/** Force specific version (skip auto-upgrade to latest) */
|
||||
forceVersion?: boolean;
|
||||
forceVersion: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,8 +113,9 @@ export interface DownloadResult {
|
||||
* - codex: OpenAI Codex via OAuth
|
||||
* - agy: Antigravity via OAuth (short name for easy usage)
|
||||
* - qwen: Qwen Code via OAuth (qwen3-coder)
|
||||
* - iflow: iFlow via OAuth
|
||||
*/
|
||||
export type CLIProxyProvider = 'gemini' | 'codex' | 'agy' | 'qwen';
|
||||
export type CLIProxyProvider = 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow';
|
||||
|
||||
/**
|
||||
* CLIProxy config.yaml structure (minimal)
|
||||
|
||||
@@ -6,14 +6,20 @@
|
||||
|
||||
/**
|
||||
* Handle doctor command
|
||||
* @param shouldFix - If true, attempt to fix detected issues
|
||||
*/
|
||||
export async function handleDoctorCommand(): Promise<void> {
|
||||
export async function handleDoctorCommand(shouldFix = false): Promise<void> {
|
||||
const DoctorModule = await import('../management/doctor');
|
||||
const Doctor = DoctorModule.default;
|
||||
const doctor = new Doctor();
|
||||
|
||||
await doctor.runAllChecks();
|
||||
|
||||
// Attempt to fix issues if --fix flag provided
|
||||
if (shouldFix) {
|
||||
await doctor.fixIssues();
|
||||
}
|
||||
|
||||
// Exit with error code if unhealthy
|
||||
process.exit(doctor.isHealthy() ? 0 : 1);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,15 @@ export async function handleSyncCommand(): Promise<void> {
|
||||
const manager = new ClaudeSymlinkManager();
|
||||
manager.install(false);
|
||||
|
||||
console.log('');
|
||||
|
||||
// Repair shared symlinks (~/.ccs/shared/ → ~/.claude/)
|
||||
// This fixes symlinks broken by Claude CLI's atomic writes (e.g., toggle thinking)
|
||||
const SharedManager = (await import('../management/shared-manager')).default;
|
||||
const sharedManager = new SharedManager();
|
||||
sharedManager.ensureSharedDirectories();
|
||||
console.log(colored('[OK]', 'green') + ' Shared symlinks verified');
|
||||
|
||||
console.log('');
|
||||
console.log(colored('[OK] Sync complete!', 'green'));
|
||||
console.log('');
|
||||
|
||||
@@ -955,6 +955,49 @@ class Doctor {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix detected issues
|
||||
* Currently fixes: shared symlinks broken by Claude CLI's atomic writes
|
||||
*/
|
||||
async fixIssues(): Promise<void> {
|
||||
console.log('');
|
||||
console.log(header('ATTEMPTING FIXES'));
|
||||
console.log('');
|
||||
|
||||
let fixed = 0;
|
||||
|
||||
// Fix shared symlinks (settings.json broken by Claude CLI toggle thinking, etc.)
|
||||
const sharedSettings = path.join(this.homedir, '.ccs', 'shared', 'settings.json');
|
||||
if (fs.existsSync(sharedSettings)) {
|
||||
try {
|
||||
const stats = fs.lstatSync(sharedSettings);
|
||||
if (!stats.isSymbolicLink()) {
|
||||
const spinner = ora('Fixing shared settings.json symlink').start();
|
||||
try {
|
||||
// Import and use SharedManager to fix symlinks
|
||||
const SharedManagerModule = await import('./shared-manager');
|
||||
const SharedManager = SharedManagerModule.default;
|
||||
const sharedManager = new SharedManager();
|
||||
sharedManager.ensureSharedDirectories();
|
||||
spinner.succeed(ok('Fixed') + ' Shared settings.json symlink restored');
|
||||
fixed++;
|
||||
} catch (err) {
|
||||
spinner.fail(fail('Failed') + ` Could not fix: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
} catch (_err) {
|
||||
// Ignore stat errors
|
||||
}
|
||||
}
|
||||
|
||||
if (fixed > 0) {
|
||||
console.log('');
|
||||
console.log(ok(`[OK] Fixed ${fixed} issue(s)`));
|
||||
} else {
|
||||
console.log(info('[i] No fixable issues detected'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the health check results are healthy
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user