@tkpdx01/ccc 1.5.0 → 1.6.2
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 +65 -87
- package/cover.png +0 -0
- package/index.js +23 -16
- 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 +19 -11
- package/src/commands/index.js +1 -0
- 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 +222 -1
package/src/commands/edit.js
CHANGED
|
@@ -2,20 +2,25 @@ import fs from 'fs';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
getAllProfiles,
|
|
6
6
|
getDefaultProfile,
|
|
7
7
|
profileExists,
|
|
8
|
+
codexProfileExists,
|
|
9
|
+
anyProfileExists,
|
|
8
10
|
getProfilePath,
|
|
9
11
|
readProfile,
|
|
10
12
|
saveProfile,
|
|
11
13
|
setDefaultProfile,
|
|
12
14
|
deleteProfile,
|
|
13
|
-
|
|
15
|
+
resolveAnyProfile,
|
|
14
16
|
getProfileCredentials,
|
|
17
|
+
getCodexProfileCredentials,
|
|
15
18
|
getClaudeSettingsTemplate,
|
|
16
19
|
ensureRequiredClaudeEnvSettings,
|
|
17
20
|
ensureClaudeSettingsExtras,
|
|
18
|
-
applyClaudeSettingsExtras
|
|
21
|
+
applyClaudeSettingsExtras,
|
|
22
|
+
createCodexProfile,
|
|
23
|
+
deleteCodexProfile
|
|
19
24
|
} from '../profiles.js';
|
|
20
25
|
|
|
21
26
|
export function editCommand(program) {
|
|
@@ -23,108 +28,162 @@ export function editCommand(program) {
|
|
|
23
28
|
.command('edit [profile]')
|
|
24
29
|
.description('编辑 profile 配置')
|
|
25
30
|
.action(async (profile) => {
|
|
26
|
-
const
|
|
31
|
+
const allProfiles = getAllProfiles();
|
|
27
32
|
|
|
28
|
-
if (
|
|
33
|
+
if (allProfiles.length === 0) {
|
|
29
34
|
console.log(chalk.yellow('没有可用的 profiles'));
|
|
30
|
-
console.log(chalk.gray('使用 "ccc
|
|
35
|
+
console.log(chalk.gray('使用 "ccc new" 创建配置'));
|
|
31
36
|
process.exit(0);
|
|
32
37
|
}
|
|
33
38
|
|
|
39
|
+
let profileInfo;
|
|
40
|
+
|
|
34
41
|
// 如果没有指定 profile,交互选择
|
|
35
42
|
if (!profile) {
|
|
36
|
-
const
|
|
43
|
+
const choices = allProfiles.map(p => {
|
|
44
|
+
const typeTag = p.type === 'codex' ? chalk.blue('[Codex]') : chalk.magenta('[Claude]');
|
|
45
|
+
return { name: `${typeTag} ${p.name}`, value: p };
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const { selected } = await inquirer.prompt([
|
|
37
49
|
{
|
|
38
50
|
type: 'list',
|
|
39
|
-
name: '
|
|
51
|
+
name: 'selected',
|
|
40
52
|
message: '选择要编辑的配置:',
|
|
41
|
-
choices
|
|
53
|
+
choices
|
|
42
54
|
}
|
|
43
55
|
]);
|
|
44
|
-
|
|
56
|
+
profileInfo = selected;
|
|
45
57
|
} else {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!resolved) {
|
|
58
|
+
profileInfo = resolveAnyProfile(profile);
|
|
59
|
+
if (!profileInfo) {
|
|
49
60
|
console.log(chalk.red(`Profile "${profile}" 不存在`));
|
|
50
61
|
process.exit(1);
|
|
51
62
|
}
|
|
52
|
-
profile = resolved;
|
|
53
63
|
}
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
65
|
+
if (profileInfo.type === 'codex') {
|
|
66
|
+
// 编辑 Codex profile
|
|
67
|
+
const { apiKey: currentApiKey, baseUrl: currentBaseUrl, model: currentModel } = getCodexProfileCredentials(profileInfo.name);
|
|
68
|
+
|
|
69
|
+
console.log(chalk.cyan(`\n当前配置 (${profileInfo.name}) ${chalk.blue('[Codex]')}:`));
|
|
70
|
+
console.log(chalk.gray(` OPENAI_API_KEY: ${currentApiKey ? currentApiKey.substring(0, 10) + '...' : '未设置'}`));
|
|
71
|
+
console.log(chalk.gray(` Base URL: ${currentBaseUrl || '未设置'}`));
|
|
72
|
+
console.log(chalk.gray(` Model: ${currentModel || '(默认)'}`));
|
|
73
|
+
console.log();
|
|
74
|
+
|
|
75
|
+
const { apiKey, baseUrl, model, newName } = await inquirer.prompt([
|
|
76
|
+
{
|
|
77
|
+
type: 'input',
|
|
78
|
+
name: 'apiKey',
|
|
79
|
+
message: 'OPENAI_API_KEY:',
|
|
80
|
+
default: currentApiKey || ''
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'input',
|
|
84
|
+
name: 'baseUrl',
|
|
85
|
+
message: 'Base URL:',
|
|
86
|
+
default: currentBaseUrl || 'https://api.openai.com/v1'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: 'input',
|
|
90
|
+
name: 'model',
|
|
91
|
+
message: 'Model (留空使用默认):',
|
|
92
|
+
default: currentModel || ''
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: 'input',
|
|
96
|
+
name: 'newName',
|
|
97
|
+
message: 'Profile 名称:',
|
|
98
|
+
default: profileInfo.name
|
|
99
|
+
}
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
if (newName && newName !== profileInfo.name) {
|
|
103
|
+
const check = anyProfileExists(newName);
|
|
104
|
+
if (check.exists) {
|
|
105
|
+
console.log(chalk.red(`Profile "${newName}" 已存在`));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
createCodexProfile(newName, apiKey, baseUrl, model);
|
|
109
|
+
deleteCodexProfile(profileInfo.name);
|
|
110
|
+
|
|
111
|
+
if (getDefaultProfile() === profileInfo.name) {
|
|
112
|
+
setDefaultProfile(newName);
|
|
113
|
+
}
|
|
114
|
+
console.log(chalk.green(`\n✓ Codex Profile 已重命名为 "${newName}" 并保存`));
|
|
115
|
+
} else {
|
|
116
|
+
createCodexProfile(profileInfo.name, apiKey, baseUrl, model);
|
|
117
|
+
console.log(chalk.green(`\n✓ Codex Profile "${profileInfo.name}" 已更新`));
|
|
81
118
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
let currentProfile = readProfile(profile);
|
|
86
|
-
if (!currentProfile || !currentProfile.env) {
|
|
87
|
-
// 如果是旧格式或空配置,基于主配置模板创建
|
|
88
|
-
const template = getClaudeSettingsTemplate() || {};
|
|
89
|
-
currentProfile = { ...template };
|
|
90
|
-
}
|
|
119
|
+
} else {
|
|
120
|
+
// 编辑 Claude profile(保持原逻辑)
|
|
121
|
+
const { apiKey: currentApiKey, apiUrl: currentApiUrl } = getProfileCredentials(profileInfo.name);
|
|
91
122
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
console.log(chalk.cyan(`\n当前配置 (${profileInfo.name}) ${chalk.magenta('[Claude]')}:`));
|
|
124
|
+
console.log(chalk.gray(` ANTHROPIC_BASE_URL: ${currentApiUrl || '未设置'}`));
|
|
125
|
+
console.log(chalk.gray(` ANTHROPIC_AUTH_TOKEN: ${currentApiKey ? currentApiKey.substring(0, 10) + '...' : '未设置'}`));
|
|
126
|
+
console.log();
|
|
96
127
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
const { apiUrl, apiKey, newName } = await inquirer.prompt([
|
|
129
|
+
{
|
|
130
|
+
type: 'input',
|
|
131
|
+
name: 'apiUrl',
|
|
132
|
+
message: 'ANTHROPIC_BASE_URL:',
|
|
133
|
+
default: currentApiUrl || ''
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
type: 'input',
|
|
137
|
+
name: 'apiKey',
|
|
138
|
+
message: 'ANTHROPIC_AUTH_TOKEN:',
|
|
139
|
+
default: currentApiKey || ''
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
type: 'input',
|
|
143
|
+
name: 'newName',
|
|
144
|
+
message: 'Profile 名称:',
|
|
145
|
+
default: profileInfo.name
|
|
146
|
+
}
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
let currentProfile = readProfile(profileInfo.name);
|
|
150
|
+
if (!currentProfile || !currentProfile.env) {
|
|
151
|
+
const template = getClaudeSettingsTemplate() || {};
|
|
152
|
+
currentProfile = { ...template };
|
|
115
153
|
}
|
|
116
|
-
saveProfile(newName, currentProfile);
|
|
117
|
-
deleteProfile(profile);
|
|
118
154
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
setDefaultProfile(newName);
|
|
155
|
+
if (!currentProfile.env) {
|
|
156
|
+
currentProfile.env = {};
|
|
122
157
|
}
|
|
123
158
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
159
|
+
currentProfile.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
160
|
+
currentProfile.env.ANTHROPIC_BASE_URL = apiUrl;
|
|
161
|
+
|
|
162
|
+
ensureRequiredClaudeEnvSettings();
|
|
163
|
+
ensureClaudeSettingsExtras();
|
|
164
|
+
currentProfile.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1';
|
|
165
|
+
currentProfile.env.CLAUDE_CODE_ATTRIBUTION_HEADER = '0';
|
|
166
|
+
currentProfile.env.DISABLE_INSTALLATION_CHECKS = '1';
|
|
167
|
+
applyClaudeSettingsExtras(currentProfile);
|
|
168
|
+
|
|
169
|
+
if (newName && newName !== profileInfo.name) {
|
|
170
|
+
const newPath = getProfilePath(newName);
|
|
171
|
+
if (fs.existsSync(newPath)) {
|
|
172
|
+
console.log(chalk.red(`Profile "${newName}" 已存在`));
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
saveProfile(newName, currentProfile);
|
|
176
|
+
deleteProfile(profileInfo.name);
|
|
177
|
+
|
|
178
|
+
if (getDefaultProfile() === profileInfo.name) {
|
|
179
|
+
setDefaultProfile(newName);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.log(chalk.green(`\n✓ Claude Profile 已重命名为 "${newName}" 并保存`));
|
|
183
|
+
} else {
|
|
184
|
+
saveProfile(profileInfo.name, currentProfile);
|
|
185
|
+
console.log(chalk.green(`\n✓ Claude Profile "${profileInfo.name}" 已更新`));
|
|
186
|
+
}
|
|
128
187
|
}
|
|
129
188
|
});
|
|
130
189
|
}
|
package/src/commands/help.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
|
|
3
3
|
export function showHelp() {
|
|
4
|
-
console.log(chalk.cyan.bold('\n CCC - Claude Code Settings Launcher\n'));
|
|
5
|
-
console.log(chalk.white(' 管理多个 Claude Code 配置文件,快速切换不同的 API 设置\n'));
|
|
4
|
+
console.log(chalk.cyan.bold('\n CCC - Claude Code / Codex Settings Launcher\n'));
|
|
5
|
+
console.log(chalk.white(' 管理多个 Claude Code 和 OpenAI Codex 配置文件,快速切换不同的 API 设置\n'));
|
|
6
6
|
|
|
7
7
|
console.log(chalk.yellow(' 启动命令:'));
|
|
8
8
|
console.log(chalk.gray(' ccc ') + '使用默认配置启动,无默认则交互选择');
|
|
9
|
-
console.log(chalk.gray(' ccc <profile> ') + '
|
|
9
|
+
console.log(chalk.gray(' ccc <profile> ') + '使用指定配置启动(支持名称或序号,自动识别类型)');
|
|
10
10
|
console.log(chalk.gray(' ccc <序号> ') + '使用序号启动(如 ccc 1)');
|
|
11
|
-
console.log(chalk.gray(' ccc -d, --ddd ') + '
|
|
11
|
+
console.log(chalk.gray(' ccc -d, --ddd ') + 'Claude: --dangerously-skip-permissions / Codex: --full-auto');
|
|
12
12
|
console.log();
|
|
13
13
|
|
|
14
14
|
console.log(chalk.yellow(' 管理命令:'));
|
|
15
|
-
console.log(chalk.gray(' ccc list, ls ') + '
|
|
15
|
+
console.log(chalk.gray(' ccc list, ls ') + '列出所有配置(Claude + Codex 混合,带序号)');
|
|
16
16
|
console.log(chalk.gray(' ccc show [profile] ') + '显示完整配置');
|
|
17
17
|
console.log(chalk.gray(' ccc use <profile> ') + '设置默认配置');
|
|
18
|
-
console.log(chalk.gray(' ccc new [name] ') + '
|
|
18
|
+
console.log(chalk.gray(' ccc new [name] ') + '创建新配置(可选 Claude 或 Codex)');
|
|
19
19
|
console.log(chalk.gray(' ccc sync [profile] ') + '从模板同步配置(保留 API 凭证)');
|
|
20
20
|
console.log(chalk.gray(' ccc sync --all ') + '同步所有配置');
|
|
21
|
+
console.log(chalk.gray(' ccc apply [profile] ') + '将配置应用到默认目录(~/.claude 或 ~/.codex)');
|
|
21
22
|
console.log(chalk.gray(' ccc edit [profile] ') + '编辑配置');
|
|
22
23
|
console.log(chalk.gray(' ccc delete, rm [name] ') + '删除配置');
|
|
23
24
|
console.log(chalk.gray(' ccc help ') + '显示此帮助信息');
|
|
@@ -31,16 +32,24 @@ export function showHelp() {
|
|
|
31
32
|
console.log();
|
|
32
33
|
|
|
33
34
|
console.log(chalk.yellow(' 配置存储:'));
|
|
34
|
-
console.log(chalk.gray(' ~/.ccc/profiles/ ') + '影子配置文件目录');
|
|
35
|
+
console.log(chalk.gray(' ~/.ccc/profiles/ ') + 'Claude 影子配置文件目录');
|
|
36
|
+
console.log(chalk.gray(' ~/.ccc/codex-profiles/ ') + 'Codex 配置目录(auth.json + config.toml)');
|
|
35
37
|
console.log(chalk.gray(' ~/.ccc/webdav.json ') + 'WebDAV 连接配置');
|
|
36
38
|
console.log(chalk.gray(' ~/.ccc/.sync_key ') + '本地密码缓存(机器指纹加密)');
|
|
37
39
|
console.log();
|
|
38
40
|
|
|
41
|
+
console.log(chalk.yellow(' 创建配置:'));
|
|
42
|
+
console.log(chalk.gray(' ccc new ') + '交互式创建,选择 Claude 或 Codex 类型');
|
|
43
|
+
console.log(chalk.gray(' ccc new myprofile ') + '指定名称创建,随后选择类型并填写凭证');
|
|
44
|
+
console.log(chalk.gray(' ') + chalk.dim('Claude 需要: ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN'));
|
|
45
|
+
console.log(chalk.gray(' ') + chalk.dim('Codex 需要: OPENAI_API_KEY + Base URL + Model(可选)'));
|
|
46
|
+
console.log();
|
|
47
|
+
|
|
39
48
|
console.log(chalk.yellow(' 示例:'));
|
|
40
49
|
console.log(chalk.gray(' ccc ls ') + '查看配置列表和序号');
|
|
41
|
-
console.log(chalk.gray(' ccc 3 ') + '启动第 3
|
|
42
|
-
console.log(chalk.gray(' ccc 3 -d ') + '启动第 3 个配置 +
|
|
43
|
-
console.log(chalk.gray(' ccc
|
|
50
|
+
console.log(chalk.gray(' ccc 3 ') + '启动第 3 个配置(自动识别 Claude/Codex)');
|
|
51
|
+
console.log(chalk.gray(' ccc 3 -d ') + '启动第 3 个配置 + 跳过权限/全自动');
|
|
52
|
+
console.log(chalk.gray(' ccc apply myprofile ') + '将 myprofile 的配置应用到默认目录');
|
|
44
53
|
console.log(chalk.gray(' ccc webdav push ') + '推送配置到云端');
|
|
45
54
|
console.log();
|
|
46
55
|
}
|
|
@@ -51,4 +60,3 @@ export function helpCommand(program) {
|
|
|
51
60
|
.description('显示帮助信息')
|
|
52
61
|
.action(showHelp);
|
|
53
62
|
}
|
|
54
|
-
|
package/src/commands/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { newCommand } from './new.js';
|
|
|
5
5
|
export { editCommand } from './edit.js';
|
|
6
6
|
export { deleteCommand } from './delete.js';
|
|
7
7
|
export { syncCommand } from './sync.js';
|
|
8
|
+
export { applyCommand } from './apply.js';
|
|
8
9
|
export { webdavCommand } from './webdav.js';
|
|
9
10
|
export { helpCommand, showHelp } from './help.js';
|
|
10
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
|
-
|