ccjk 13.3.18 → 13.3.19
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/dist/chunks/api-cli.mjs +1 -1
- package/dist/chunks/api.mjs +1 -1
- package/dist/chunks/auto-fix.mjs +4 -4
- package/dist/chunks/boost.mjs +1 -1
- package/dist/chunks/ccjk-mcp.mjs +2 -2
- package/dist/chunks/ccr.mjs +24 -20
- package/dist/chunks/check-updates.mjs +2 -1
- package/dist/chunks/claude-code-config-manager.mjs +4 -4
- package/dist/chunks/claude-code-incremental-manager.mjs +5 -5
- package/dist/chunks/claude-config.mjs +2 -2
- package/dist/chunks/claude-wrapper.mjs +1 -1
- package/dist/chunks/codex-config-switch.mjs +4 -3
- package/dist/chunks/codex-provider-manager.mjs +2 -1
- package/dist/chunks/codex.mjs +4 -332
- package/dist/chunks/config-switch.mjs +2 -1
- package/dist/chunks/config.mjs +424 -358
- package/dist/chunks/config2.mjs +358 -424
- package/dist/chunks/config3.mjs +6 -1
- package/dist/chunks/doctor.mjs +2 -2
- package/dist/chunks/features.mjs +812 -0
- package/dist/chunks/index.mjs +1 -1
- package/dist/chunks/init.mjs +43 -89
- package/dist/chunks/installer.mjs +1 -1
- package/dist/chunks/mcp-cli.mjs +18 -17
- package/dist/chunks/mcp.mjs +5 -4
- package/dist/chunks/menu-hierarchical.mjs +27 -23
- package/dist/chunks/menu.mjs +17 -791
- package/dist/chunks/onboarding-wizard.mjs +2 -2
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/platform.mjs +1 -1
- package/dist/chunks/quick-setup.mjs +14 -11
- package/dist/chunks/slash-commands.mjs +1 -1
- package/dist/chunks/uninstall.mjs +1 -1
- package/dist/chunks/update.mjs +17 -15
- package/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/shared/ccjk.0aJQmVxS.mjs +336 -0
- package/dist/shared/ccjk.C3o4BXvM.mjs +444 -0
- package/dist/shared/{ccjk.DfZKjHvG.mjs → ccjk.Dgq22o6V.mjs} +2 -438
- package/dist/shared/{ccjk.KpFl2RDA.mjs → ccjk.LTONy3IS.mjs} +4 -3
- package/dist/shared/ccjk.Si-T_ccK.mjs +75 -0
- package/dist/shared/{ccjk.I6IuYdc_.mjs → ccjk.Xla_pE3y.mjs} +1 -1
- package/dist/shared/{ccjk.DZ4ehAHg.mjs → ccjk.byom1b8z.mjs} +1 -1
- package/package.json +1 -1
package/dist/chunks/config.mjs
CHANGED
|
@@ -1,412 +1,478 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import { existsSync, copyFileSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import process__default from 'node:process';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
2
6
|
import a from './index2.mjs';
|
|
3
7
|
import { d as dayjs } from '../shared/ccjk.RyizuzOI.mjs';
|
|
4
8
|
import { i as inquirer } from './index3.mjs';
|
|
5
|
-
import {
|
|
9
|
+
import { SETTINGS_FILE } from './constants.mjs';
|
|
6
10
|
import { ensureI18nInitialized, i18n } from './index5.mjs';
|
|
7
|
-
import { s as setPrimaryApiKey,
|
|
8
|
-
import {
|
|
11
|
+
import { s as setPrimaryApiKey, c as addCompletedOnboarding } from './claude-config.mjs';
|
|
12
|
+
import { b as backupExistingConfig } from './config2.mjs';
|
|
9
13
|
import { readJsonConfig, writeJsonConfig } from './json-config.mjs';
|
|
10
|
-
import {
|
|
11
|
-
import { j as join
|
|
14
|
+
import { p as promptBoolean } from '../shared/ccjk.BWFpnOr3.mjs';
|
|
15
|
+
import { j as join } from '../shared/ccjk.bQ7Dh1g4.mjs';
|
|
16
|
+
import '../shared/ccjk.BAGoDD49.mjs';
|
|
17
|
+
import 'node:readline';
|
|
18
|
+
import 'stream';
|
|
19
|
+
import 'node:tty';
|
|
20
|
+
import 'node:async_hooks';
|
|
21
|
+
import '../shared/ccjk.Cjgrln_h.mjs';
|
|
22
|
+
import 'tty';
|
|
23
|
+
import 'fs';
|
|
24
|
+
import 'child_process';
|
|
25
|
+
import 'node:path';
|
|
26
|
+
import 'node:crypto';
|
|
27
|
+
import 'buffer';
|
|
28
|
+
import 'string_decoder';
|
|
29
|
+
import 'node:url';
|
|
30
|
+
import './platform.mjs';
|
|
31
|
+
import './main.mjs';
|
|
32
|
+
import 'module';
|
|
33
|
+
import 'node:stream';
|
|
34
|
+
import './fs-operations.mjs';
|
|
35
|
+
import 'node:fs/promises';
|
|
36
|
+
import '../shared/ccjk.DScm_NnL.mjs';
|
|
37
|
+
import '../shared/ccjk.DeWpAShp.mjs';
|
|
12
38
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
env[key] = "";
|
|
25
|
-
} else {
|
|
26
|
-
delete env[key];
|
|
39
|
+
const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
|
|
40
|
+
async function fetchProviderPresets() {
|
|
41
|
+
try {
|
|
42
|
+
const controller = new AbortController();
|
|
43
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
44
|
+
const response = await fetch(PROVIDER_PRESETS_URL, {
|
|
45
|
+
signal: controller.signal
|
|
46
|
+
});
|
|
47
|
+
clearTimeout(timeoutId);
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
27
50
|
}
|
|
51
|
+
const data = await response.json();
|
|
52
|
+
const presets = [];
|
|
53
|
+
if (Array.isArray(data)) {
|
|
54
|
+
for (const provider of data) {
|
|
55
|
+
if (provider && typeof provider === "object") {
|
|
56
|
+
presets.push({
|
|
57
|
+
name: provider.name || "",
|
|
58
|
+
provider: provider.name || "",
|
|
59
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
60
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
61
|
+
models: provider.models || [],
|
|
62
|
+
description: provider.description || provider.name || "",
|
|
63
|
+
transformer: provider.transformer
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else if (data && typeof data === "object") {
|
|
68
|
+
for (const [key, value] of Object.entries(data)) {
|
|
69
|
+
if (typeof value === "object" && value !== null) {
|
|
70
|
+
const provider = value;
|
|
71
|
+
presets.push({
|
|
72
|
+
name: provider.name || key,
|
|
73
|
+
provider: key,
|
|
74
|
+
baseURL: provider.api_base_url || provider.baseURL || provider.url,
|
|
75
|
+
requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
|
|
76
|
+
models: provider.models || [],
|
|
77
|
+
description: provider.description || "",
|
|
78
|
+
transformer: provider.transformer
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return presets;
|
|
84
|
+
} catch {
|
|
85
|
+
return getFallbackPresets();
|
|
28
86
|
}
|
|
29
87
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
88
|
+
function getFallbackPresets() {
|
|
89
|
+
return [
|
|
90
|
+
{
|
|
91
|
+
name: "dashscope",
|
|
92
|
+
provider: "dashscope",
|
|
93
|
+
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
94
|
+
requiresApiKey: true,
|
|
95
|
+
models: ["qwen3-coder-plus"],
|
|
96
|
+
description: "Alibaba DashScope",
|
|
97
|
+
transformer: {
|
|
98
|
+
"use": [["maxtoken", { max_tokens: 65536 }]],
|
|
99
|
+
"qwen3-coder-plus": {
|
|
100
|
+
use: ["enhancetool"]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "deepseek",
|
|
106
|
+
provider: "deepseek",
|
|
107
|
+
baseURL: "https://api.deepseek.com/chat/completions",
|
|
108
|
+
requiresApiKey: true,
|
|
109
|
+
models: ["deepseek-chat", "deepseek-reasoner"],
|
|
110
|
+
description: "DeepSeek AI models",
|
|
111
|
+
transformer: {
|
|
112
|
+
"use": ["deepseek"],
|
|
113
|
+
"deepseek-chat": {
|
|
114
|
+
use: ["tooluse"]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "gemini",
|
|
120
|
+
provider: "gemini",
|
|
121
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
122
|
+
requiresApiKey: true,
|
|
123
|
+
models: ["gemini-2.5-flash", "gemini-2.5-pro"],
|
|
124
|
+
description: "Google Gemini models",
|
|
125
|
+
transformer: {
|
|
126
|
+
use: ["gemini"]
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "modelscope",
|
|
131
|
+
provider: "modelscope",
|
|
132
|
+
baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
133
|
+
requiresApiKey: true,
|
|
134
|
+
models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
|
|
135
|
+
description: "ModelScope AI models",
|
|
136
|
+
transformer: {
|
|
137
|
+
"use": [["maxtoken", { max_tokens: 65536 }]],
|
|
138
|
+
"Qwen/Qwen3-Coder-480B-A35B-Instruct": {
|
|
139
|
+
use: ["enhancetool"]
|
|
140
|
+
},
|
|
141
|
+
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
142
|
+
use: ["reasoning"]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "openrouter",
|
|
148
|
+
provider: "openrouter",
|
|
149
|
+
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
|
150
|
+
requiresApiKey: true,
|
|
151
|
+
models: [
|
|
152
|
+
"google/gemini-2.5-pro-preview",
|
|
153
|
+
"anthropic/claude-sonnet-4",
|
|
154
|
+
"anthropic/claude-3.5-sonnet",
|
|
155
|
+
"anthropic/claude-3.7-sonnet:thinking"
|
|
156
|
+
],
|
|
157
|
+
description: "OpenRouter API",
|
|
158
|
+
transformer: {
|
|
159
|
+
use: ["openrouter"]
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "siliconflow",
|
|
164
|
+
provider: "siliconflow",
|
|
165
|
+
baseURL: "https://api.siliconflow.cn/v1/chat/completions",
|
|
166
|
+
requiresApiKey: true,
|
|
167
|
+
models: ["moonshotai/Kimi-K2-Instruct"],
|
|
168
|
+
description: "SiliconFlow AI",
|
|
169
|
+
transformer: {
|
|
170
|
+
use: [["maxtoken", { max_tokens: 16384 }]]
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "volcengine",
|
|
175
|
+
provider: "volcengine",
|
|
176
|
+
baseURL: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
|
177
|
+
requiresApiKey: true,
|
|
178
|
+
models: ["deepseek-v3-250324", "deepseek-r1-250528"],
|
|
179
|
+
description: "Volcengine AI",
|
|
180
|
+
transformer: {
|
|
181
|
+
use: ["deepseek"]
|
|
182
|
+
}
|
|
58
183
|
}
|
|
184
|
+
];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const execAsync = promisify(exec);
|
|
188
|
+
const CCR_CONFIG_DIR = join(homedir(), ".claude-code-router");
|
|
189
|
+
const CCR_CONFIG_FILE = join(CCR_CONFIG_DIR, "config.json");
|
|
190
|
+
const CCR_BACKUP_DIR = CCR_CONFIG_DIR;
|
|
191
|
+
function ensureCcrConfigDir() {
|
|
192
|
+
if (!existsSync(CCR_CONFIG_DIR)) {
|
|
193
|
+
mkdirSync(CCR_CONFIG_DIR, { recursive: true });
|
|
59
194
|
}
|
|
60
195
|
}
|
|
61
|
-
function
|
|
196
|
+
async function backupCcrConfig() {
|
|
197
|
+
ensureI18nInitialized();
|
|
62
198
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
199
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const timestamp = `${dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS")}Z`;
|
|
203
|
+
const backupFileName = `config.json.${timestamp}.bak`;
|
|
204
|
+
const backupPath = join(CCR_BACKUP_DIR, backupFileName);
|
|
205
|
+
console.log(a.green(`${i18n.t("ccr:backupCcrConfig")}`));
|
|
206
|
+
copyFileSync(CCR_CONFIG_FILE, backupPath);
|
|
207
|
+
console.log(a.green(`\u2714 ${i18n.t("ccr:ccrBackupSuccess").replace("{path}", backupPath)}`));
|
|
208
|
+
return backupPath;
|
|
68
209
|
} catch (error) {
|
|
69
|
-
console.error("
|
|
70
|
-
return
|
|
210
|
+
console.error(a.red(`${i18n.t("ccr:ccrBackupFailed")}:`), error.message);
|
|
211
|
+
return null;
|
|
71
212
|
}
|
|
72
213
|
}
|
|
73
|
-
function
|
|
74
|
-
if (!
|
|
214
|
+
function readCcrConfig() {
|
|
215
|
+
if (!existsSync(CCR_CONFIG_FILE)) {
|
|
75
216
|
return null;
|
|
76
|
-
const existingSettings = readJsonConfig(SETTINGS_FILE);
|
|
77
|
-
let settings;
|
|
78
|
-
if (existingSettings) {
|
|
79
|
-
settings = existingSettings;
|
|
80
|
-
} else {
|
|
81
|
-
settings = getDefaultSettings();
|
|
82
217
|
}
|
|
218
|
+
return readJsonConfig(CCR_CONFIG_FILE);
|
|
219
|
+
}
|
|
220
|
+
function writeCcrConfig(config) {
|
|
221
|
+
ensureCcrConfigDir();
|
|
222
|
+
writeJsonConfig(CCR_CONFIG_FILE, config);
|
|
223
|
+
}
|
|
224
|
+
async function configureCcrProxy(ccrConfig) {
|
|
225
|
+
const settings = readJsonConfig(SETTINGS_FILE) || {};
|
|
226
|
+
const host = ccrConfig.HOST || "127.0.0.1";
|
|
227
|
+
const port = ccrConfig.PORT || 3456;
|
|
228
|
+
const apiKey = ccrConfig.APIKEY || "sk-ccjk-x-ccr";
|
|
83
229
|
if (!settings.env) {
|
|
84
230
|
settings.env = {};
|
|
85
231
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
} else if (apiConfig.authType === "auth_token") {
|
|
90
|
-
settings.env.ANTHROPIC_AUTH_TOKEN = apiConfig.key;
|
|
91
|
-
delete settings.env.ANTHROPIC_API_KEY;
|
|
92
|
-
}
|
|
93
|
-
if (apiConfig.url) {
|
|
94
|
-
settings.env.ANTHROPIC_BASE_URL = apiConfig.url;
|
|
95
|
-
}
|
|
232
|
+
delete settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
233
|
+
settings.env.ANTHROPIC_BASE_URL = `http://${host}:${port}`;
|
|
234
|
+
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
96
235
|
writeJsonConfig(SETTINGS_FILE, settings);
|
|
97
|
-
if (apiConfig.authType) {
|
|
98
|
-
try {
|
|
99
|
-
setPrimaryApiKey();
|
|
100
|
-
} catch (error) {
|
|
101
|
-
ensureI18nInitialized();
|
|
102
|
-
console.error(i18n.t("mcp:primaryApiKeySetFailed"), error);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
236
|
try {
|
|
106
|
-
|
|
237
|
+
setPrimaryApiKey();
|
|
107
238
|
} catch (error) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const verification = readJsonConfig(SETTINGS_FILE);
|
|
111
|
-
if (verification?.env) {
|
|
112
|
-
const envKey = apiConfig.authType === "api_key" ? "ANTHROPIC_API_KEY" : "ANTHROPIC_AUTH_TOKEN";
|
|
113
|
-
if (verification.env[envKey] !== apiConfig.key) {
|
|
114
|
-
console.error(a.red("\u26A0 API config write verification failed \u2014 retrying..."));
|
|
115
|
-
const freshSettings = readJsonConfig(SETTINGS_FILE) || settings;
|
|
116
|
-
if (!freshSettings.env)
|
|
117
|
-
freshSettings.env = {};
|
|
118
|
-
if (apiConfig.authType === "api_key") {
|
|
119
|
-
freshSettings.env.ANTHROPIC_API_KEY = apiConfig.key;
|
|
120
|
-
delete freshSettings.env.ANTHROPIC_AUTH_TOKEN;
|
|
121
|
-
} else if (apiConfig.authType === "auth_token") {
|
|
122
|
-
freshSettings.env.ANTHROPIC_AUTH_TOKEN = apiConfig.key;
|
|
123
|
-
delete freshSettings.env.ANTHROPIC_API_KEY;
|
|
124
|
-
}
|
|
125
|
-
if (apiConfig.url) {
|
|
126
|
-
freshSettings.env.ANTHROPIC_BASE_URL = apiConfig.url;
|
|
127
|
-
}
|
|
128
|
-
writeJsonConfig(SETTINGS_FILE, freshSettings);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return apiConfig;
|
|
132
|
-
}
|
|
133
|
-
function configureHooks(hooks) {
|
|
134
|
-
let settings = getDefaultSettings();
|
|
135
|
-
const existingSettings = readJsonConfig(SETTINGS_FILE);
|
|
136
|
-
if (existingSettings) {
|
|
137
|
-
settings = existingSettings;
|
|
138
|
-
}
|
|
139
|
-
settings.hooks = {
|
|
140
|
-
...settings.hooks || {},
|
|
141
|
-
...hooks
|
|
142
|
-
};
|
|
143
|
-
writeJsonConfig(SETTINGS_FILE, settings);
|
|
144
|
-
}
|
|
145
|
-
function mergeConfigs(sourceFile, targetFile) {
|
|
146
|
-
if (!exists(sourceFile))
|
|
147
|
-
return;
|
|
148
|
-
const target = readJsonConfig(targetFile) || {};
|
|
149
|
-
const source = readJsonConfig(sourceFile) || {};
|
|
150
|
-
const merged = deepMerge(target, source);
|
|
151
|
-
writeJsonConfig(targetFile, merged);
|
|
152
|
-
}
|
|
153
|
-
function updateCustomModel(primaryModel, haikuModel, sonnetModel, opusModel) {
|
|
154
|
-
if (!primaryModel?.trim() && !haikuModel?.trim() && !sonnetModel?.trim() && !opusModel?.trim()) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
let settings = getDefaultSettings();
|
|
158
|
-
const existingSettings = readJsonConfig(SETTINGS_FILE);
|
|
159
|
-
if (existingSettings) {
|
|
160
|
-
settings = existingSettings;
|
|
161
|
-
}
|
|
162
|
-
delete settings.model;
|
|
163
|
-
settings.env = settings.env || {};
|
|
164
|
-
clearModelEnv(settings.env, "override");
|
|
165
|
-
if (primaryModel?.trim()) {
|
|
166
|
-
settings.env.ANTHROPIC_MODEL = primaryModel.trim();
|
|
167
|
-
}
|
|
168
|
-
if (haikuModel?.trim()) {
|
|
169
|
-
settings.env.ANTHROPIC_SMALL_FAST_MODEL = haikuModel.trim();
|
|
170
|
-
settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = haikuModel.trim();
|
|
239
|
+
ensureI18nInitialized();
|
|
240
|
+
console.error(i18n.t("mcp:primaryApiKeySetFailed"), error);
|
|
171
241
|
}
|
|
172
|
-
if (sonnetModel?.trim())
|
|
173
|
-
settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = sonnetModel.trim();
|
|
174
|
-
if (opusModel?.trim())
|
|
175
|
-
settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = opusModel.trim();
|
|
176
|
-
writeJsonConfig(SETTINGS_FILE, settings);
|
|
177
242
|
}
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
settings.env = {};
|
|
186
|
-
}
|
|
187
|
-
if (model !== "custom") {
|
|
188
|
-
clearModelEnv(settings.env);
|
|
189
|
-
}
|
|
190
|
-
if (model === "default" || model === "custom") {
|
|
191
|
-
delete settings.model;
|
|
192
|
-
} else {
|
|
193
|
-
settings.model = model;
|
|
243
|
+
async function selectCcrPreset() {
|
|
244
|
+
ensureI18nInitialized();
|
|
245
|
+
console.log(a.green(`${i18n.t("ccr:fetchingPresets")}`));
|
|
246
|
+
const presets = await fetchProviderPresets();
|
|
247
|
+
if (!presets || presets.length === 0) {
|
|
248
|
+
console.log(a.yellow(`${i18n.t("ccr:noPresetsAvailable")}`));
|
|
249
|
+
return null;
|
|
194
250
|
}
|
|
195
|
-
writeJsonConfig(SETTINGS_FILE, settings);
|
|
196
|
-
}
|
|
197
|
-
function mergeSettingsFile(templatePath, targetPath) {
|
|
198
251
|
try {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const existingSettings = readJsonConfig(targetPath) || {};
|
|
209
|
-
const mergedEnv = {
|
|
210
|
-
...templateSettings.env || {},
|
|
211
|
-
// Template env vars first
|
|
212
|
-
...existingSettings.env || {}
|
|
213
|
-
// User's env vars override (preserving API keys, etc.)
|
|
214
|
-
};
|
|
215
|
-
const mergedSettings = { ...templateSettings };
|
|
216
|
-
const schemaCriticalFields = [
|
|
217
|
-
"$schema",
|
|
218
|
-
"attribution",
|
|
219
|
-
"fileSuggestion",
|
|
220
|
-
"permissions"
|
|
221
|
-
];
|
|
222
|
-
const removeNullFields = [
|
|
223
|
-
"plansDirectory",
|
|
224
|
-
"contextFiles"
|
|
252
|
+
const choices = [
|
|
253
|
+
{
|
|
254
|
+
name: `1. ${i18n.t("ccr:skipOption")}`,
|
|
255
|
+
value: "skip"
|
|
256
|
+
},
|
|
257
|
+
...presets.map((p, index) => ({
|
|
258
|
+
name: `${index + 2}. ${p.name}`,
|
|
259
|
+
value: p
|
|
260
|
+
}))
|
|
225
261
|
];
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
mergedSettings.env = mergedEnv;
|
|
235
|
-
if (mergedSettings.permissions && mergedSettings.permissions.allow) {
|
|
236
|
-
mergedSettings.permissions.allow = mergeAndCleanPermissions(
|
|
237
|
-
templateSettings.permissions?.allow,
|
|
238
|
-
existingSettings.permissions?.allow
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
if (mergedSettings.plansDirectory === null) {
|
|
242
|
-
delete mergedSettings.plansDirectory;
|
|
243
|
-
}
|
|
244
|
-
writeJsonConfig(targetPath, mergedSettings);
|
|
262
|
+
const { preset } = await inquirer.prompt({
|
|
263
|
+
type: "list",
|
|
264
|
+
name: "preset",
|
|
265
|
+
message: i18n.t("ccr:selectCcrPreset"),
|
|
266
|
+
choices
|
|
267
|
+
});
|
|
268
|
+
return preset;
|
|
245
269
|
} catch (error) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
} else {
|
|
250
|
-
copyFile(templatePath, targetPath);
|
|
270
|
+
if (error.name === "ExitPromptError") {
|
|
271
|
+
console.log(a.yellow(i18n.t("common:cancelled")));
|
|
272
|
+
return null;
|
|
251
273
|
}
|
|
274
|
+
throw error;
|
|
252
275
|
}
|
|
253
276
|
}
|
|
254
|
-
function
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
if (!settings.model) {
|
|
267
|
-
return "default";
|
|
268
|
-
}
|
|
269
|
-
const validModels = ["opus", "sonnet", "sonnet[1m]"];
|
|
270
|
-
if (validModels.includes(settings.model)) {
|
|
271
|
-
return settings.model;
|
|
277
|
+
async function configureCcrWithPreset(preset) {
|
|
278
|
+
ensureI18nInitialized();
|
|
279
|
+
const provider = {
|
|
280
|
+
name: preset.name,
|
|
281
|
+
// Use the original name from JSON
|
|
282
|
+
api_base_url: preset.baseURL || "",
|
|
283
|
+
api_key: "",
|
|
284
|
+
models: preset.models
|
|
285
|
+
};
|
|
286
|
+
if (preset.transformer) {
|
|
287
|
+
provider.transformer = preset.transformer;
|
|
272
288
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
289
|
+
if (preset.requiresApiKey) {
|
|
290
|
+
try {
|
|
291
|
+
const { apiKey } = await inquirer.prompt({
|
|
292
|
+
type: "input",
|
|
293
|
+
name: "apiKey",
|
|
294
|
+
message: i18n.t("ccr:enterApiKeyForProvider").replace("{provider}", preset.name),
|
|
295
|
+
validate: async (value) => !!value || i18n.t("api:keyRequired")
|
|
296
|
+
});
|
|
297
|
+
provider.api_key = apiKey;
|
|
298
|
+
} catch (error) {
|
|
299
|
+
if (error.name === "ExitPromptError") {
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
throw error;
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
provider.api_key = "sk-free";
|
|
279
306
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
307
|
+
let defaultModel = preset.models[0];
|
|
308
|
+
if (preset.models.length > 1) {
|
|
309
|
+
try {
|
|
310
|
+
const { model } = await inquirer.prompt({
|
|
311
|
+
type: "list",
|
|
312
|
+
name: "model",
|
|
313
|
+
message: i18n.t("ccr:selectDefaultModelForProvider").replace("{provider}", preset.name),
|
|
314
|
+
choices: preset.models.map((m, index) => ({
|
|
315
|
+
name: `${index + 1}. ${m}`,
|
|
316
|
+
value: m
|
|
317
|
+
}))
|
|
318
|
+
});
|
|
319
|
+
defaultModel = model;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (error.name === "ExitPromptError") {
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
289
326
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
327
|
+
const router = {
|
|
328
|
+
default: `${preset.name},${defaultModel}`,
|
|
329
|
+
// Use the original name
|
|
330
|
+
background: `${preset.name},${defaultModel}`,
|
|
331
|
+
think: `${preset.name},${defaultModel}`,
|
|
332
|
+
longContext: `${preset.name},${defaultModel}`,
|
|
333
|
+
longContextThreshold: 6e4,
|
|
334
|
+
webSearch: `${preset.name},${defaultModel}`
|
|
335
|
+
};
|
|
336
|
+
const config = {
|
|
337
|
+
LOG: true,
|
|
338
|
+
CLAUDE_PATH: "",
|
|
339
|
+
HOST: "127.0.0.1",
|
|
340
|
+
PORT: 3456,
|
|
341
|
+
APIKEY: "sk-ccjk-x-ccr",
|
|
342
|
+
API_TIMEOUT_MS: "600000",
|
|
343
|
+
PROXY_URL: "",
|
|
344
|
+
transformers: [],
|
|
345
|
+
Providers: [provider],
|
|
346
|
+
Router: router
|
|
295
347
|
};
|
|
348
|
+
return config;
|
|
296
349
|
}
|
|
297
|
-
function
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
} else if (ANTHROPIC_API_KEY) {
|
|
312
|
-
authType = "api_key";
|
|
313
|
-
key = ANTHROPIC_API_KEY;
|
|
350
|
+
async function restartAndCheckCcrStatus() {
|
|
351
|
+
ensureI18nInitialized();
|
|
352
|
+
try {
|
|
353
|
+
console.log(a.green(`${i18n.t("ccr:restartingCcr")}`));
|
|
354
|
+
await execAsync("ccr restart");
|
|
355
|
+
console.log(a.green(`\u2714 ${i18n.t("ccr:ccrRestartSuccess")}`));
|
|
356
|
+
console.log(a.green(`${i18n.t("ccr:checkingCcrStatus")}`));
|
|
357
|
+
const { stdout } = await execAsync("ccr status");
|
|
358
|
+
console.log(a.gray(stdout));
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.error(a.red(`${i18n.t("ccr:ccrRestartFailed")}:`), error.message || error);
|
|
361
|
+
if (process__default.env.DEBUG) {
|
|
362
|
+
console.error("Full error:", error);
|
|
363
|
+
}
|
|
314
364
|
}
|
|
365
|
+
}
|
|
366
|
+
async function showConfigurationTips(apiKey) {
|
|
367
|
+
ensureI18nInitialized();
|
|
368
|
+
console.log(a.bold.cyan(`
|
|
369
|
+
\u{1F4CC} ${i18n.t("ccr:configTips")}:`));
|
|
370
|
+
console.log(a.green(` \u2022 ${i18n.t("ccr:advancedConfigTip")}`));
|
|
371
|
+
console.log(a.green(` \u2022 ${i18n.t("ccr:manualConfigTip")}`));
|
|
372
|
+
console.log(a.bold.yellow(` \u2022 ${i18n.t("ccr:useClaudeCommand")}`));
|
|
373
|
+
if (apiKey) {
|
|
374
|
+
console.log(a.bold.green(` \u2022 ${i18n.t("ccr:ccrUiApiKey") || "CCR UI API Key"}: ${apiKey}`));
|
|
375
|
+
console.log(a.gray(` ${i18n.t("ccr:ccrUiApiKeyHint") || "Use this API key to login to CCR UI"}`));
|
|
376
|
+
}
|
|
377
|
+
console.log("");
|
|
378
|
+
}
|
|
379
|
+
function createDefaultCcrConfig() {
|
|
315
380
|
return {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
381
|
+
LOG: false,
|
|
382
|
+
CLAUDE_PATH: "",
|
|
383
|
+
HOST: "127.0.0.1",
|
|
384
|
+
PORT: 3456,
|
|
385
|
+
APIKEY: "sk-ccjk-x-ccr",
|
|
386
|
+
API_TIMEOUT_MS: "600000",
|
|
387
|
+
PROXY_URL: "",
|
|
388
|
+
transformers: [],
|
|
389
|
+
Providers: [],
|
|
390
|
+
// Empty providers array - user configures in UI
|
|
391
|
+
Router: {}
|
|
392
|
+
// Empty router configuration - user configures in UI
|
|
319
393
|
};
|
|
320
394
|
}
|
|
321
|
-
function
|
|
322
|
-
|
|
323
|
-
let directive = "";
|
|
324
|
-
if (aiOutputLang === "custom") {
|
|
325
|
-
return;
|
|
326
|
-
} else if (AI_OUTPUT_LANGUAGES[aiOutputLang]) {
|
|
327
|
-
directive = AI_OUTPUT_LANGUAGES[aiOutputLang].directive;
|
|
328
|
-
} else {
|
|
329
|
-
directive = `Always respond in ${aiOutputLang}`;
|
|
330
|
-
}
|
|
331
|
-
const frontmatter = "---\nscope: user\n---";
|
|
332
|
-
writeFileAtomic(claudeFile, `${frontmatter}
|
|
333
|
-
|
|
334
|
-
${directive}`);
|
|
335
|
-
}
|
|
336
|
-
function switchToOfficialLogin() {
|
|
395
|
+
async function setupCcrConfiguration() {
|
|
396
|
+
ensureI18nInitialized();
|
|
337
397
|
try {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
398
|
+
const existingConfig = readCcrConfig();
|
|
399
|
+
if (existingConfig) {
|
|
400
|
+
console.log(a.green(`\u2139 ${i18n.t("ccr:existingCcrConfig")}`));
|
|
401
|
+
let shouldBackupAndReconfigure = false;
|
|
402
|
+
try {
|
|
403
|
+
shouldBackupAndReconfigure = await promptBoolean({
|
|
404
|
+
message: i18n.t("ccr:overwriteCcrConfig"),
|
|
405
|
+
defaultValue: false
|
|
406
|
+
});
|
|
407
|
+
} catch (error) {
|
|
408
|
+
if (error.name === "ExitPromptError") {
|
|
409
|
+
console.log(a.yellow(i18n.t("common:cancelled")));
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
throw error;
|
|
413
|
+
}
|
|
414
|
+
if (!shouldBackupAndReconfigure) {
|
|
415
|
+
console.log(a.yellow(`${i18n.t("ccr:keepingExistingConfig")}`));
|
|
416
|
+
await configureCcrProxy(existingConfig);
|
|
417
|
+
try {
|
|
418
|
+
const { manageApiKeyApproval } = await import('./claude-config.mjs').then(function (n) { return n.h; });
|
|
419
|
+
const apiKey = existingConfig.APIKEY || "sk-ccjk-x-ccr";
|
|
420
|
+
manageApiKeyApproval(apiKey);
|
|
421
|
+
console.log(a.green(`\u2714 ${i18n.t("ccr:apiKeyApprovalSuccess")}`));
|
|
422
|
+
} catch (error) {
|
|
423
|
+
console.error(a.red(`${i18n.t("ccr:apiKeyApprovalFailed")}:`), error);
|
|
424
|
+
}
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
backupCcrConfig();
|
|
344
428
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
429
|
+
const preset = await selectCcrPreset();
|
|
430
|
+
if (!preset) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
let config;
|
|
434
|
+
if (preset === "skip") {
|
|
435
|
+
console.log(a.yellow(`${i18n.t("ccr:skipConfiguring")}`));
|
|
436
|
+
config = createDefaultCcrConfig();
|
|
437
|
+
} else {
|
|
438
|
+
config = await configureCcrWithPreset(preset);
|
|
439
|
+
}
|
|
440
|
+
writeCcrConfig(config);
|
|
441
|
+
console.log(a.green(`\u2714 ${i18n.t("ccr:ccrConfigSuccess")}`));
|
|
442
|
+
await configureCcrProxy(config);
|
|
443
|
+
console.log(a.green(`\u2714 ${i18n.t("ccr:proxyConfigSuccess")}`));
|
|
444
|
+
await restartAndCheckCcrStatus();
|
|
445
|
+
await showConfigurationTips(config.APIKEY);
|
|
446
|
+
try {
|
|
447
|
+
addCompletedOnboarding();
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.error(a.red(i18n.t("errors:failedToSetOnboarding")), error);
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
const { manageApiKeyApproval } = await import('./claude-config.mjs').then(function (n) { return n.h; });
|
|
453
|
+
const apiKey = config.APIKEY || "sk-ccjk-x-ccr";
|
|
454
|
+
manageApiKeyApproval(apiKey);
|
|
455
|
+
console.log(a.green(`\u2714 ${i18n.t("ccr:apiKeyApprovalSuccess")}`));
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.error(a.red(`${i18n.t("ccr:apiKeyApprovalFailed")}:`), error);
|
|
350
458
|
}
|
|
351
|
-
console.log(i18n.t("api:officialLoginConfigured"));
|
|
352
459
|
return true;
|
|
353
460
|
} catch (error) {
|
|
354
|
-
|
|
355
|
-
|
|
461
|
+
if (error.name === "ExitPromptError") {
|
|
462
|
+
console.log(a.yellow(i18n.t("common:cancelled")));
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
console.error(a.red(`${i18n.t("ccr:ccrConfigFailed")}:`), error);
|
|
356
466
|
return false;
|
|
357
467
|
}
|
|
358
468
|
}
|
|
359
|
-
async function
|
|
469
|
+
async function configureCcrFeature() {
|
|
360
470
|
ensureI18nInitialized();
|
|
361
|
-
const
|
|
362
|
-
if (
|
|
363
|
-
|
|
471
|
+
const backupDir = backupExistingConfig();
|
|
472
|
+
if (backupDir) {
|
|
473
|
+
console.log(a.gray(`\u2714 ${i18n.t("configuration:backupSuccess")}: ${backupDir}`));
|
|
364
474
|
}
|
|
365
|
-
|
|
366
|
-
${a.green(`\u2139 ${i18n.t("api:existingApiConfig")}`)}`);
|
|
367
|
-
console.log(a.gray(` ${i18n.t("api:apiConfigUrl")}: ${existingConfig.url || "N/A"}`));
|
|
368
|
-
console.log(a.gray(` ${i18n.t("api:apiConfigKey")}: ${existingConfig.key ? `***${existingConfig.key.slice(-4)}` : "N/A"}`));
|
|
369
|
-
console.log(a.gray(` ${i18n.t("api:apiConfigAuthType")}: ${existingConfig.authType || "N/A"}
|
|
370
|
-
`));
|
|
371
|
-
const { choice } = await inquirer.prompt({
|
|
372
|
-
type: "list",
|
|
373
|
-
name: "choice",
|
|
374
|
-
message: i18n.t("api:selectCustomConfigAction"),
|
|
375
|
-
choices: [
|
|
376
|
-
{
|
|
377
|
-
name: i18n.t("api:modifyPartialConfig"),
|
|
378
|
-
value: "modify-partial"
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
name: i18n.t("api:modifyAllConfig"),
|
|
382
|
-
value: "modify-all"
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
name: i18n.t("api:keepExistingConfig"),
|
|
386
|
-
value: "keep-existing"
|
|
387
|
-
}
|
|
388
|
-
]
|
|
389
|
-
});
|
|
390
|
-
return choice || null;
|
|
475
|
+
await setupCcrConfiguration();
|
|
391
476
|
}
|
|
392
477
|
|
|
393
|
-
|
|
394
|
-
__proto__: null,
|
|
395
|
-
applyAiLanguageDirective: applyAiLanguageDirective,
|
|
396
|
-
backupExistingConfig: backupExistingConfig,
|
|
397
|
-
configureApi: configureApi,
|
|
398
|
-
configureHooks: configureHooks,
|
|
399
|
-
copyConfigFiles: copyConfigFiles,
|
|
400
|
-
ensureClaudeDir: ensureClaudeDir,
|
|
401
|
-
getExistingApiConfig: getExistingApiConfig,
|
|
402
|
-
getExistingCustomModelConfig: getExistingCustomModelConfig,
|
|
403
|
-
getExistingModelConfig: getExistingModelConfig,
|
|
404
|
-
mergeConfigs: mergeConfigs,
|
|
405
|
-
mergeSettingsFile: mergeSettingsFile,
|
|
406
|
-
promptApiConfigurationAction: promptApiConfigurationAction,
|
|
407
|
-
switchToOfficialLogin: switchToOfficialLogin,
|
|
408
|
-
updateCustomModel: updateCustomModel,
|
|
409
|
-
updateDefaultModel: updateDefaultModel
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
export { clearModelEnv as a, backupExistingConfig as b, copyConfigFiles as c, configureApi as d, applyAiLanguageDirective as e, getExistingModelConfig as f, getExistingApiConfig as g, getExistingCustomModelConfig as h, updateDefaultModel as i, ensureClaudeDir as j, config as k, promptApiConfigurationAction as p, switchToOfficialLogin as s, updateCustomModel as u };
|
|
478
|
+
export { backupCcrConfig, configureCcrFeature, configureCcrProxy, configureCcrWithPreset, createDefaultCcrConfig, ensureCcrConfigDir, readCcrConfig, restartAndCheckCcrStatus, selectCcrPreset, setupCcrConfiguration, showConfigurationTips, writeCcrConfig };
|