@tkpdx01/ccc 1.3.7 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -12
- package/index.js +23 -18
- package/package.json +4 -2
- package/src/commands/apply.js +79 -0
- package/src/commands/delete.js +35 -23
- package/src/commands/edit.js +139 -80
- package/src/commands/help.js +12 -18
- package/src/commands/index.js +1 -1
- package/src/commands/list.js +20 -13
- package/src/commands/new.js +146 -55
- package/src/commands/show.js +66 -36
- package/src/commands/sync.js +69 -29
- package/src/commands/use.js +6 -5
- package/src/config.js +2 -0
- package/src/launch.js +68 -15
- package/src/profiles.js +232 -2
- package/src/utils.js +0 -53
- package/src/commands/import.js +0 -357
- package/src/parsers.js +0 -154
package/src/commands/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export { listCommand } from './list.js';
|
|
2
2
|
export { useCommand } from './use.js';
|
|
3
3
|
export { showCommand } from './show.js';
|
|
4
|
-
export { importCommand } from './import.js';
|
|
5
4
|
export { newCommand } from './new.js';
|
|
6
5
|
export { editCommand } from './edit.js';
|
|
7
6
|
export { deleteCommand } from './delete.js';
|
|
8
7
|
export { syncCommand } from './sync.js';
|
|
8
|
+
export { applyCommand } from './apply.js';
|
|
9
9
|
export { webdavCommand } from './webdav.js';
|
|
10
10
|
export { helpCommand, showHelp } from './help.js';
|
|
11
11
|
|
package/src/commands/list.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import Table from 'cli-table3';
|
|
3
|
-
import {
|
|
3
|
+
import { getAllProfiles, getDefaultProfile, getProfileCredentials, getCodexProfileCredentials } from '../profiles.js';
|
|
4
4
|
|
|
5
5
|
export function listCommand(program) {
|
|
6
6
|
program
|
|
@@ -8,17 +8,17 @@ export function listCommand(program) {
|
|
|
8
8
|
.alias('ls')
|
|
9
9
|
.description('列出所有 profiles')
|
|
10
10
|
.action(() => {
|
|
11
|
-
const
|
|
11
|
+
const allProfiles = getAllProfiles();
|
|
12
12
|
const defaultProfile = getDefaultProfile();
|
|
13
13
|
|
|
14
|
-
if (
|
|
14
|
+
if (allProfiles.length === 0) {
|
|
15
15
|
console.log(chalk.yellow('没有可用的 profiles'));
|
|
16
|
-
console.log(chalk.gray('使用 "ccc
|
|
16
|
+
console.log(chalk.gray('使用 "ccc new" 创建配置'));
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const table = new Table({
|
|
21
|
-
head: [chalk.cyan('#'), chalk.cyan('Profile'), chalk.cyan('
|
|
21
|
+
head: [chalk.cyan('#'), chalk.cyan('类型'), chalk.cyan('Profile'), chalk.cyan('API URL')],
|
|
22
22
|
style: { head: [], border: [] },
|
|
23
23
|
chars: {
|
|
24
24
|
'top': '─', 'top-mid': '┬', 'top-left': '┌', 'top-right': '┐',
|
|
@@ -28,19 +28,26 @@ export function listCommand(program) {
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
const isDefault = p === defaultProfile;
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
allProfiles.forEach((p, index) => {
|
|
32
|
+
const isDefault = p.name === defaultProfile;
|
|
33
|
+
|
|
34
|
+
let apiUrl;
|
|
35
|
+
if (p.type === 'codex') {
|
|
36
|
+
const creds = getCodexProfileCredentials(p.name);
|
|
37
|
+
apiUrl = creds.baseUrl || chalk.gray('(未设置)');
|
|
38
|
+
} else {
|
|
39
|
+
const creds = getProfileCredentials(p.name);
|
|
40
|
+
apiUrl = creds.apiUrl || chalk.gray('(未设置)');
|
|
41
|
+
}
|
|
35
42
|
|
|
36
43
|
const num = isDefault ? chalk.green(`${index + 1}`) : chalk.gray(`${index + 1}`);
|
|
37
|
-
const
|
|
38
|
-
|
|
44
|
+
const typeTag = p.type === 'codex' ? chalk.blue('Codex') : chalk.magenta('Claude');
|
|
45
|
+
const name = isDefault ? chalk.green(`${p.name} *`) : p.name;
|
|
46
|
+
table.push([num, typeTag, name, apiUrl]);
|
|
39
47
|
});
|
|
40
48
|
|
|
41
49
|
console.log();
|
|
42
50
|
console.log(table.toString());
|
|
43
|
-
console.log(chalk.gray(`\n 共 ${
|
|
51
|
+
console.log(chalk.gray(`\n 共 ${allProfiles.length} 个配置,* 表示默认,可用序号或名称启动\n`));
|
|
44
52
|
});
|
|
45
53
|
}
|
|
46
|
-
|
package/src/commands/new.js
CHANGED
|
@@ -3,12 +3,16 @@ import inquirer from 'inquirer';
|
|
|
3
3
|
import {
|
|
4
4
|
ensureDirs,
|
|
5
5
|
getProfiles,
|
|
6
|
+
getAllProfiles,
|
|
6
7
|
profileExists,
|
|
8
|
+
codexProfileExists,
|
|
9
|
+
anyProfileExists,
|
|
7
10
|
createProfileFromTemplate,
|
|
11
|
+
createCodexProfile,
|
|
8
12
|
setDefaultProfile,
|
|
9
13
|
ensureClaudeSettingsExtras
|
|
10
14
|
} from '../profiles.js';
|
|
11
|
-
import { launchClaude } from '../launch.js';
|
|
15
|
+
import { launchClaude, launchCodex } from '../launch.js';
|
|
12
16
|
|
|
13
17
|
const RESERVED_PROFILE_NAMES = [
|
|
14
18
|
'list',
|
|
@@ -22,6 +26,7 @@ const RESERVED_PROFILE_NAMES = [
|
|
|
22
26
|
'delete',
|
|
23
27
|
'rm',
|
|
24
28
|
'sync',
|
|
29
|
+
'apply',
|
|
25
30
|
'webdav',
|
|
26
31
|
'help'
|
|
27
32
|
];
|
|
@@ -44,8 +49,21 @@ function validateProfileName(input) {
|
|
|
44
49
|
export function newCommand(program) {
|
|
45
50
|
program
|
|
46
51
|
.command('new [name]')
|
|
47
|
-
.description('
|
|
52
|
+
.description('创建新的配置(Claude 或 Codex)')
|
|
48
53
|
.action(async (name) => {
|
|
54
|
+
// 选择 profile 类型
|
|
55
|
+
const { profileType } = await inquirer.prompt([
|
|
56
|
+
{
|
|
57
|
+
type: 'list',
|
|
58
|
+
name: 'profileType',
|
|
59
|
+
message: '配置类型:',
|
|
60
|
+
choices: [
|
|
61
|
+
{ name: `${chalk.magenta('[Claude]')} Claude Code`, value: 'claude' },
|
|
62
|
+
{ name: `${chalk.blue('[Codex]')} OpenAI Codex`, value: 'codex' }
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
]);
|
|
66
|
+
|
|
49
67
|
// 如果没有提供名称,询问
|
|
50
68
|
if (!name) {
|
|
51
69
|
const { profileName } = await inquirer.prompt([
|
|
@@ -65,13 +83,15 @@ export function newCommand(program) {
|
|
|
65
83
|
}
|
|
66
84
|
}
|
|
67
85
|
|
|
68
|
-
//
|
|
69
|
-
|
|
86
|
+
// 检查是否已存在(跨类型检查)
|
|
87
|
+
const existing = anyProfileExists(name);
|
|
88
|
+
if (existing.exists) {
|
|
89
|
+
const existingType = existing.type === 'codex' ? 'Codex' : 'Claude';
|
|
70
90
|
const { overwrite } = await inquirer.prompt([
|
|
71
91
|
{
|
|
72
92
|
type: 'confirm',
|
|
73
93
|
name: 'overwrite',
|
|
74
|
-
message: `配置 "${name}"
|
|
94
|
+
message: `配置 "${name}" 已存在(${existingType} 类型),是否覆盖?`,
|
|
75
95
|
default: false
|
|
76
96
|
}
|
|
77
97
|
]);
|
|
@@ -81,69 +101,140 @@ export function newCommand(program) {
|
|
|
81
101
|
}
|
|
82
102
|
}
|
|
83
103
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
if (profileType === 'codex') {
|
|
105
|
+
// Codex profile 创建
|
|
106
|
+
const answers = await inquirer.prompt([
|
|
107
|
+
{
|
|
108
|
+
type: 'input',
|
|
109
|
+
name: 'apiKey',
|
|
110
|
+
message: 'OPENAI_API_KEY:',
|
|
111
|
+
default: ''
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: 'input',
|
|
115
|
+
name: 'baseUrl',
|
|
116
|
+
message: 'Base URL:',
|
|
117
|
+
default: 'https://api.openai.com/v1'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'input',
|
|
121
|
+
name: 'model',
|
|
122
|
+
message: 'Model (留空使用默认):',
|
|
123
|
+
default: ''
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
type: 'input',
|
|
127
|
+
name: 'finalName',
|
|
128
|
+
message: 'Profile 名称:',
|
|
129
|
+
default: name,
|
|
130
|
+
validate: validateProfileName
|
|
131
|
+
}
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
const finalName = answers.finalName;
|
|
135
|
+
if (finalName !== name) {
|
|
136
|
+
const check = anyProfileExists(finalName);
|
|
137
|
+
if (check.exists) {
|
|
138
|
+
const { overwriteNew } = await inquirer.prompt([
|
|
139
|
+
{
|
|
140
|
+
type: 'confirm',
|
|
141
|
+
name: 'overwriteNew',
|
|
142
|
+
message: `配置 "${finalName}" 已存在,是否覆盖?`,
|
|
143
|
+
default: false
|
|
144
|
+
}
|
|
145
|
+
]);
|
|
146
|
+
if (!overwriteNew) {
|
|
147
|
+
console.log(chalk.yellow('已取消'));
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
103
151
|
}
|
|
104
|
-
]);
|
|
105
152
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
153
|
+
ensureDirs();
|
|
154
|
+
createCodexProfile(finalName, answers.apiKey, answers.baseUrl, answers.model);
|
|
155
|
+
console.log(chalk.green(`\n✓ Codex 配置 "${finalName}" 已创建`));
|
|
156
|
+
|
|
157
|
+
const allProfiles = getAllProfiles();
|
|
158
|
+
if (allProfiles.length === 1) {
|
|
159
|
+
setDefaultProfile(finalName);
|
|
160
|
+
console.log(chalk.green(`✓ 已设为默认配置`));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const { useNow } = await inquirer.prompt([
|
|
109
164
|
{
|
|
110
165
|
type: 'confirm',
|
|
111
|
-
name: '
|
|
112
|
-
message:
|
|
166
|
+
name: 'useNow',
|
|
167
|
+
message: '是否立即启动 Codex?',
|
|
113
168
|
default: false
|
|
114
169
|
}
|
|
115
170
|
]);
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
process.exit(0);
|
|
171
|
+
if (useNow) {
|
|
172
|
+
launchCodex(finalName);
|
|
119
173
|
}
|
|
120
|
-
}
|
|
174
|
+
} else {
|
|
175
|
+
// Claude profile 创建(保持原逻辑)
|
|
176
|
+
const { apiUrl, apiKey, finalName } = await inquirer.prompt([
|
|
177
|
+
{
|
|
178
|
+
type: 'input',
|
|
179
|
+
name: 'apiUrl',
|
|
180
|
+
message: 'ANTHROPIC_BASE_URL:',
|
|
181
|
+
default: 'https://api.anthropic.com'
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'input',
|
|
185
|
+
name: 'apiKey',
|
|
186
|
+
message: 'ANTHROPIC_AUTH_TOKEN:',
|
|
187
|
+
default: ''
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: 'input',
|
|
191
|
+
name: 'finalName',
|
|
192
|
+
message: 'Profile 名称:',
|
|
193
|
+
default: name,
|
|
194
|
+
validate: validateProfileName
|
|
195
|
+
}
|
|
196
|
+
]);
|
|
121
197
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
198
|
+
if (finalName !== name) {
|
|
199
|
+
const check = anyProfileExists(finalName);
|
|
200
|
+
if (check.exists) {
|
|
201
|
+
const { overwriteNew } = await inquirer.prompt([
|
|
202
|
+
{
|
|
203
|
+
type: 'confirm',
|
|
204
|
+
name: 'overwriteNew',
|
|
205
|
+
message: `配置 "${finalName}" 已存在,是否覆盖?`,
|
|
206
|
+
default: false
|
|
207
|
+
}
|
|
208
|
+
]);
|
|
209
|
+
if (!overwriteNew) {
|
|
210
|
+
console.log(chalk.yellow('已取消'));
|
|
211
|
+
process.exit(0);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
126
215
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
console.log(chalk.green(`✓ 已设为默认配置`));
|
|
132
|
-
}
|
|
216
|
+
ensureDirs();
|
|
217
|
+
ensureClaudeSettingsExtras();
|
|
218
|
+
createProfileFromTemplate(finalName, apiUrl, apiKey);
|
|
219
|
+
console.log(chalk.green(`\n✓ Claude 配置 "${finalName}" 已创建(基于 ~/.claude/settings.json)`));
|
|
133
220
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
name: 'useNow',
|
|
139
|
-
message: '是否立即启动 Claude?',
|
|
140
|
-
default: false
|
|
221
|
+
const allProfiles = getAllProfiles();
|
|
222
|
+
if (allProfiles.length === 1) {
|
|
223
|
+
setDefaultProfile(finalName);
|
|
224
|
+
console.log(chalk.green(`✓ 已设为默认配置`));
|
|
141
225
|
}
|
|
142
|
-
]);
|
|
143
226
|
|
|
144
|
-
|
|
145
|
-
|
|
227
|
+
const { useNow } = await inquirer.prompt([
|
|
228
|
+
{
|
|
229
|
+
type: 'confirm',
|
|
230
|
+
name: 'useNow',
|
|
231
|
+
message: '是否立即启动 Claude?',
|
|
232
|
+
default: false
|
|
233
|
+
}
|
|
234
|
+
]);
|
|
235
|
+
if (useNow) {
|
|
236
|
+
launchClaude(finalName);
|
|
237
|
+
}
|
|
146
238
|
}
|
|
147
239
|
});
|
|
148
240
|
}
|
|
149
|
-
|
package/src/commands/show.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
getDefaultProfile,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import {
|
|
4
|
+
getAllProfiles,
|
|
5
|
+
getDefaultProfile,
|
|
6
|
+
anyProfileExists,
|
|
7
|
+
resolveAnyProfile,
|
|
8
|
+
getProfilePath,
|
|
9
|
+
getCodexProfileDir,
|
|
10
|
+
readProfile,
|
|
11
|
+
readCodexProfile,
|
|
12
|
+
getCodexProfileCredentials
|
|
9
13
|
} from '../profiles.js';
|
|
10
14
|
import { formatValue } from '../utils.js';
|
|
11
15
|
|
|
@@ -14,55 +18,81 @@ export function showCommand(program) {
|
|
|
14
18
|
.command('show [profile]')
|
|
15
19
|
.description('显示 profile 的完整配置')
|
|
16
20
|
.action(async (profile) => {
|
|
17
|
-
const
|
|
21
|
+
const allProfiles = getAllProfiles();
|
|
18
22
|
|
|
19
|
-
if (
|
|
23
|
+
if (allProfiles.length === 0) {
|
|
20
24
|
console.log(chalk.yellow('没有可用的 profiles'));
|
|
21
25
|
process.exit(0);
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
let profileInfo;
|
|
29
|
+
|
|
25
30
|
if (!profile) {
|
|
26
31
|
const defaultProfile = getDefaultProfile();
|
|
27
|
-
const
|
|
32
|
+
const choices = allProfiles.map(p => {
|
|
33
|
+
const typeTag = p.type === 'codex' ? chalk.blue('[Codex]') : chalk.magenta('[Claude]');
|
|
34
|
+
return { name: `${typeTag} ${p.name}`, value: p };
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const { selected } = await inquirer.prompt([
|
|
28
38
|
{
|
|
29
39
|
type: 'list',
|
|
30
|
-
name: '
|
|
40
|
+
name: 'selected',
|
|
31
41
|
message: '选择要查看的配置:',
|
|
32
|
-
choices
|
|
33
|
-
default: defaultProfile
|
|
42
|
+
choices,
|
|
43
|
+
default: allProfiles.findIndex(p => p.name === defaultProfile)
|
|
34
44
|
}
|
|
35
45
|
]);
|
|
36
|
-
|
|
46
|
+
profileInfo = selected;
|
|
47
|
+
} else {
|
|
48
|
+
profileInfo = resolveAnyProfile(profile);
|
|
49
|
+
if (!profileInfo) {
|
|
50
|
+
console.log(chalk.red(`Profile "${profile}" 不存在`));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
37
53
|
}
|
|
38
54
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
const isDefault = getDefaultProfile() === profileInfo.name;
|
|
56
|
+
|
|
57
|
+
if (profileInfo.type === 'codex') {
|
|
58
|
+
const dir = getCodexProfileDir(profileInfo.name);
|
|
59
|
+
const codexProfile = readCodexProfile(profileInfo.name);
|
|
60
|
+
const { apiKey, baseUrl, model } = getCodexProfileCredentials(profileInfo.name);
|
|
43
61
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const isDefault = getDefaultProfile() === profile;
|
|
62
|
+
console.log(chalk.cyan.bold(`\n Profile: ${profileInfo.name}`) + ` ${chalk.blue('[Codex]')}` + (isDefault ? chalk.green(' (默认)') : ''));
|
|
63
|
+
console.log(chalk.gray(` 路径: ${dir}\n`));
|
|
47
64
|
|
|
48
|
-
|
|
49
|
-
|
|
65
|
+
console.log(` ${chalk.cyan('OPENAI_API_KEY')}: ${chalk.yellow(apiKey ? apiKey.substring(0, 15) + '...' : '未设置')}`);
|
|
66
|
+
console.log(` ${chalk.cyan('Base URL')}: ${chalk.white(baseUrl)}`);
|
|
67
|
+
console.log(` ${chalk.cyan('Model')}: ${chalk.white(model || '(默认)')}`);
|
|
50
68
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} else if (typeof value === 'boolean') {
|
|
57
|
-
console.log(` ${chalk.cyan(key)}: ${value ? chalk.green(formattedValue) : chalk.red(formattedValue)}`);
|
|
58
|
-
} else if (typeof value === 'object') {
|
|
59
|
-
console.log(` ${chalk.cyan(key)}: ${chalk.gray(formattedValue)}`);
|
|
60
|
-
} else {
|
|
61
|
-
console.log(` ${chalk.cyan(key)}: ${chalk.white(formattedValue)}`);
|
|
69
|
+
if (codexProfile?.configToml) {
|
|
70
|
+
console.log(`\n ${chalk.cyan('config.toml')}:`);
|
|
71
|
+
codexProfile.configToml.split('\n').forEach(line => {
|
|
72
|
+
console.log(` ${chalk.gray(line)}`);
|
|
73
|
+
});
|
|
62
74
|
}
|
|
63
|
-
}
|
|
75
|
+
} else {
|
|
76
|
+
const profilePath = getProfilePath(profileInfo.name);
|
|
77
|
+
const settings = readProfile(profileInfo.name);
|
|
78
|
+
|
|
79
|
+
console.log(chalk.cyan.bold(`\n Profile: ${profileInfo.name}`) + ` ${chalk.magenta('[Claude]')}` + (isDefault ? chalk.green(' (默认)') : ''));
|
|
80
|
+
console.log(chalk.gray(` 路径: ${profilePath}\n`));
|
|
81
|
+
|
|
82
|
+
Object.entries(settings).forEach(([key, value]) => {
|
|
83
|
+
const formattedValue = formatValue(key, value);
|
|
84
|
+
if ((key === 'apiKey' || key === 'ANTHROPIC_AUTH_TOKEN') && value) {
|
|
85
|
+
console.log(` ${chalk.cyan(key)}: ${chalk.yellow(formattedValue)}`);
|
|
86
|
+
} else if (typeof value === 'boolean') {
|
|
87
|
+
console.log(` ${chalk.cyan(key)}: ${value ? chalk.green(formattedValue) : chalk.red(formattedValue)}`);
|
|
88
|
+
} else if (typeof value === 'object') {
|
|
89
|
+
console.log(` ${chalk.cyan(key)}: ${chalk.gray(formattedValue)}`);
|
|
90
|
+
} else {
|
|
91
|
+
console.log(` ${chalk.cyan(key)}: ${chalk.white(formattedValue)}`);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
64
95
|
|
|
65
96
|
console.log();
|
|
66
97
|
});
|
|
67
98
|
}
|
|
68
|
-
|
package/src/commands/sync.js
CHANGED
|
@@ -1,41 +1,39 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import {
|
|
4
|
+
getAllProfiles,
|
|
4
5
|
getProfiles,
|
|
6
|
+
getCodexProfiles,
|
|
5
7
|
syncProfileWithTemplate,
|
|
8
|
+
syncCodexProfileWithTemplate,
|
|
6
9
|
getClaudeSettingsTemplate,
|
|
7
|
-
|
|
10
|
+
resolveAnyProfile
|
|
8
11
|
} from '../profiles.js';
|
|
12
|
+
import { CODEX_HOME_PATH } from '../config.js';
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import path from 'path';
|
|
9
15
|
|
|
10
16
|
export function syncCommand(program) {
|
|
11
17
|
program
|
|
12
18
|
.command('sync [profile]')
|
|
13
|
-
.description('
|
|
19
|
+
.description('同步模板配置到 profile(保留 API 凭证)')
|
|
14
20
|
.option('-a, --all', '同步所有 profiles')
|
|
15
21
|
.action(async (profile, options) => {
|
|
16
|
-
|
|
17
|
-
const template = getClaudeSettingsTemplate();
|
|
18
|
-
if (!template) {
|
|
19
|
-
console.log(chalk.red('未找到 ~/.claude/settings.json'));
|
|
20
|
-
console.log(chalk.gray('请确保 Claude Code 已正确安装'));
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
22
|
+
const allProfiles = getAllProfiles();
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (profiles.length === 0) {
|
|
24
|
+
if (allProfiles.length === 0) {
|
|
27
25
|
console.log(chalk.yellow('没有可用的 profiles'));
|
|
28
26
|
console.log(chalk.gray('使用 "ccc new" 创建配置'));
|
|
29
27
|
process.exit(0);
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
// 同步所有
|
|
30
|
+
// 同步所有
|
|
33
31
|
if (options.all) {
|
|
34
32
|
const { confirm } = await inquirer.prompt([
|
|
35
33
|
{
|
|
36
34
|
type: 'confirm',
|
|
37
35
|
name: 'confirm',
|
|
38
|
-
message: `确定要同步所有 ${
|
|
36
|
+
message: `确定要同步所有 ${allProfiles.length} 个 profiles 吗?`,
|
|
39
37
|
default: false
|
|
40
38
|
}
|
|
41
39
|
]);
|
|
@@ -48,43 +46,85 @@ export function syncCommand(program) {
|
|
|
48
46
|
console.log(chalk.cyan('\n开始同步所有 profiles...\n'));
|
|
49
47
|
|
|
50
48
|
let successCount = 0;
|
|
51
|
-
for (const p of
|
|
52
|
-
|
|
49
|
+
for (const p of allProfiles) {
|
|
50
|
+
let result;
|
|
51
|
+
if (p.type === 'codex') {
|
|
52
|
+
const codexConfigPath = path.join(CODEX_HOME_PATH, 'config.toml');
|
|
53
|
+
if (!fs.existsSync(codexConfigPath)) {
|
|
54
|
+
console.log(chalk.yellow(` ⚠ ${chalk.blue('[Codex]')} ${p.name} (无 ~/.codex/config.toml 模板,跳过)`));
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
result = syncCodexProfileWithTemplate(p.name);
|
|
58
|
+
} else {
|
|
59
|
+
const template = getClaudeSettingsTemplate();
|
|
60
|
+
if (!template) {
|
|
61
|
+
console.log(chalk.yellow(` ⚠ ${chalk.magenta('[Claude]')} ${p.name} (无 ~/.claude/settings.json 模板,跳过)`));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
result = syncProfileWithTemplate(p.name);
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
if (result) {
|
|
54
|
-
|
|
68
|
+
const typeTag = p.type === 'codex' ? chalk.blue('[Codex]') : chalk.magenta('[Claude]');
|
|
69
|
+
console.log(chalk.green(` ✓ ${typeTag} ${p.name}`));
|
|
55
70
|
successCount++;
|
|
56
71
|
} else {
|
|
57
|
-
console.log(chalk.red(` ✗ ${p} (同步失败)`));
|
|
72
|
+
console.log(chalk.red(` ✗ ${p.name} (同步失败)`));
|
|
58
73
|
}
|
|
59
74
|
}
|
|
60
75
|
|
|
61
|
-
console.log(chalk.green(`\n✓ 已同步 ${successCount}/${
|
|
76
|
+
console.log(chalk.green(`\n✓ 已同步 ${successCount}/${allProfiles.length} 个 profiles`));
|
|
62
77
|
return;
|
|
63
78
|
}
|
|
64
79
|
|
|
65
|
-
// 同步单个
|
|
80
|
+
// 同步单个
|
|
81
|
+
let profileInfo;
|
|
82
|
+
|
|
66
83
|
if (!profile) {
|
|
67
|
-
const
|
|
84
|
+
const choices = allProfiles.map(p => {
|
|
85
|
+
const typeTag = p.type === 'codex' ? chalk.blue('[Codex]') : chalk.magenta('[Claude]');
|
|
86
|
+
return { name: `${typeTag} ${p.name}`, value: p };
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const { selected } = await inquirer.prompt([
|
|
68
90
|
{
|
|
69
91
|
type: 'list',
|
|
70
|
-
name: '
|
|
92
|
+
name: 'selected',
|
|
71
93
|
message: '选择要同步的配置:',
|
|
72
|
-
choices
|
|
94
|
+
choices
|
|
73
95
|
}
|
|
74
96
|
]);
|
|
75
|
-
|
|
97
|
+
profileInfo = selected;
|
|
76
98
|
} else {
|
|
77
|
-
|
|
78
|
-
if (!
|
|
99
|
+
profileInfo = resolveAnyProfile(profile);
|
|
100
|
+
if (!profileInfo) {
|
|
79
101
|
console.log(chalk.red(`Profile "${profile}" 不存在`));
|
|
80
102
|
process.exit(1);
|
|
81
103
|
}
|
|
82
|
-
profile = resolved;
|
|
83
104
|
}
|
|
84
105
|
|
|
85
|
-
|
|
106
|
+
let result;
|
|
107
|
+
if (profileInfo.type === 'codex') {
|
|
108
|
+
const codexConfigPath = path.join(CODEX_HOME_PATH, 'config.toml');
|
|
109
|
+
if (!fs.existsSync(codexConfigPath)) {
|
|
110
|
+
console.log(chalk.red('未找到 ~/.codex/config.toml'));
|
|
111
|
+
console.log(chalk.gray('请确保 Codex CLI 已正确配置'));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
result = syncCodexProfileWithTemplate(profileInfo.name);
|
|
115
|
+
} else {
|
|
116
|
+
const template = getClaudeSettingsTemplate();
|
|
117
|
+
if (!template) {
|
|
118
|
+
console.log(chalk.red('未找到 ~/.claude/settings.json'));
|
|
119
|
+
console.log(chalk.gray('请确保 Claude Code 已正确安装'));
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
result = syncProfileWithTemplate(profileInfo.name);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const typeLabel = profileInfo.type === 'codex' ? 'Codex' : 'Claude';
|
|
86
126
|
if (result) {
|
|
87
|
-
console.log(chalk.green(`\n✓ Profile "${
|
|
127
|
+
console.log(chalk.green(`\n✓ ${typeLabel} Profile "${profileInfo.name}" 已同步(保留了 API 凭证)`));
|
|
88
128
|
} else {
|
|
89
129
|
console.log(chalk.red(`\n✗ 同步失败`));
|
|
90
130
|
process.exit(1);
|
package/src/commands/use.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import {
|
|
2
|
+
import { anyProfileExists, resolveAnyProfile, setDefaultProfile } from '../profiles.js';
|
|
3
3
|
|
|
4
4
|
export function useCommand(program) {
|
|
5
5
|
program
|
|
6
6
|
.command('use <profile>')
|
|
7
7
|
.description('设置默认 profile')
|
|
8
8
|
.action((profile) => {
|
|
9
|
-
|
|
9
|
+
const resolved = resolveAnyProfile(profile);
|
|
10
|
+
if (!resolved) {
|
|
10
11
|
console.log(chalk.red(`Profile "${profile}" 不存在`));
|
|
11
12
|
console.log(chalk.yellow(`使用 "ccc list" 查看可用的 profiles`));
|
|
12
13
|
process.exit(1);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
const typeTag = resolved.type === 'codex' ? chalk.blue('[Codex]') : chalk.magenta('[Claude]');
|
|
17
|
+
setDefaultProfile(resolved.name);
|
|
18
|
+
console.log(chalk.green(`✓ 默认 profile 已设置为 ${typeTag} "${resolved.name}"`));
|
|
17
19
|
});
|
|
18
20
|
}
|
|
19
|
-
|
package/src/config.js
CHANGED
|
@@ -4,6 +4,8 @@ import os from 'os';
|
|
|
4
4
|
// 配置文件存储目录
|
|
5
5
|
export const CONFIG_DIR = path.join(os.homedir(), '.ccc');
|
|
6
6
|
export const PROFILES_DIR = path.join(CONFIG_DIR, 'profiles');
|
|
7
|
+
export const CODEX_PROFILES_DIR = path.join(CONFIG_DIR, 'codex-profiles');
|
|
7
8
|
export const DEFAULT_FILE = path.join(CONFIG_DIR, 'default');
|
|
8
9
|
export const CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');
|
|
10
|
+
export const CODEX_HOME_PATH = path.join(os.homedir(), '.codex');
|
|
9
11
|
|