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.
Files changed (44) hide show
  1. package/dist/chunks/api-cli.mjs +1 -1
  2. package/dist/chunks/api.mjs +1 -1
  3. package/dist/chunks/auto-fix.mjs +4 -4
  4. package/dist/chunks/boost.mjs +1 -1
  5. package/dist/chunks/ccjk-mcp.mjs +2 -2
  6. package/dist/chunks/ccr.mjs +24 -20
  7. package/dist/chunks/check-updates.mjs +2 -1
  8. package/dist/chunks/claude-code-config-manager.mjs +4 -4
  9. package/dist/chunks/claude-code-incremental-manager.mjs +5 -5
  10. package/dist/chunks/claude-config.mjs +2 -2
  11. package/dist/chunks/claude-wrapper.mjs +1 -1
  12. package/dist/chunks/codex-config-switch.mjs +4 -3
  13. package/dist/chunks/codex-provider-manager.mjs +2 -1
  14. package/dist/chunks/codex.mjs +4 -332
  15. package/dist/chunks/config-switch.mjs +2 -1
  16. package/dist/chunks/config.mjs +424 -358
  17. package/dist/chunks/config2.mjs +358 -424
  18. package/dist/chunks/config3.mjs +6 -1
  19. package/dist/chunks/doctor.mjs +2 -2
  20. package/dist/chunks/features.mjs +812 -0
  21. package/dist/chunks/index.mjs +1 -1
  22. package/dist/chunks/init.mjs +43 -89
  23. package/dist/chunks/installer.mjs +1 -1
  24. package/dist/chunks/mcp-cli.mjs +18 -17
  25. package/dist/chunks/mcp.mjs +5 -4
  26. package/dist/chunks/menu-hierarchical.mjs +27 -23
  27. package/dist/chunks/menu.mjs +17 -791
  28. package/dist/chunks/onboarding-wizard.mjs +2 -2
  29. package/dist/chunks/package.mjs +1 -1
  30. package/dist/chunks/platform.mjs +1 -1
  31. package/dist/chunks/quick-setup.mjs +14 -11
  32. package/dist/chunks/slash-commands.mjs +1 -1
  33. package/dist/chunks/uninstall.mjs +1 -1
  34. package/dist/chunks/update.mjs +17 -15
  35. package/dist/cli.mjs +1 -1
  36. package/dist/index.mjs +1 -1
  37. package/dist/shared/ccjk.0aJQmVxS.mjs +336 -0
  38. package/dist/shared/ccjk.C3o4BXvM.mjs +444 -0
  39. package/dist/shared/{ccjk.DfZKjHvG.mjs → ccjk.Dgq22o6V.mjs} +2 -438
  40. package/dist/shared/{ccjk.KpFl2RDA.mjs → ccjk.LTONy3IS.mjs} +4 -3
  41. package/dist/shared/ccjk.Si-T_ccK.mjs +75 -0
  42. package/dist/shared/{ccjk.I6IuYdc_.mjs → ccjk.Xla_pE3y.mjs} +1 -1
  43. package/dist/shared/{ccjk.DZ4ehAHg.mjs → ccjk.byom1b8z.mjs} +1 -1
  44. package/package.json +1 -1
@@ -1,412 +1,478 @@
1
- import { fileURLToPath } from 'node:url';
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 { CLAUDE_DIR, SETTINGS_FILE, CLAUDE_VSC_CONFIG_FILE, AI_OUTPUT_LANGUAGES } from './constants.mjs';
9
+ import { SETTINGS_FILE } from './constants.mjs';
6
10
  import { ensureI18nInitialized, i18n } from './index5.mjs';
7
- import { s as setPrimaryApiKey, a as addCompletedOnboarding, d as deepMerge } from './claude-config.mjs';
8
- import { exists, ensureDir, copyDir, writeFileAtomic, copyFile } from './fs-operations.mjs';
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 { m as mergeAndCleanPermissions } from '../shared/ccjk.DScm_NnL.mjs';
11
- import { j as join, d as dirname } from '../shared/ccjk.bQ7Dh1g4.mjs';
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 MODEL_ENV_KEYS = [
14
- "ANTHROPIC_MODEL",
15
- "ANTHROPIC_DEFAULT_HAIKU_MODEL",
16
- "ANTHROPIC_DEFAULT_SONNET_MODEL",
17
- "ANTHROPIC_DEFAULT_OPUS_MODEL",
18
- // Deprecated but still cleaned to avoid stale values
19
- "ANTHROPIC_SMALL_FAST_MODEL"
20
- ];
21
- function clearModelEnv(env, mode = "reset") {
22
- for (const key of MODEL_ENV_KEYS) {
23
- if (mode === "reset" && key === "ANTHROPIC_MODEL") {
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
- function ensureClaudeDir() {
32
- ensureDir(CLAUDE_DIR);
33
- }
34
- function backupExistingConfig() {
35
- if (!exists(CLAUDE_DIR)) {
36
- return null;
37
- }
38
- const timestamp = dayjs().format("YYYY-MM-DD_HH-mm-ss");
39
- const backupBaseDir = join(CLAUDE_DIR, "backup");
40
- const backupDir = join(backupBaseDir, `backup_${timestamp}`);
41
- ensureDir(backupDir);
42
- const filter = (path) => {
43
- return !path.includes("/backup");
44
- };
45
- copyDir(CLAUDE_DIR, backupDir, { filter });
46
- return backupDir;
47
- }
48
- function copyConfigFiles(onlyMd = false) {
49
- const currentFilePath = fileURLToPath(import.meta.url);
50
- const distDir = dirname(dirname(currentFilePath));
51
- const rootDir = dirname(distDir);
52
- const baseTemplateDir = join(rootDir, "templates", "claude-code");
53
- if (!onlyMd) {
54
- const baseSettingsPath = join(baseTemplateDir, "common", "settings.json");
55
- const destSettingsPath = join(CLAUDE_DIR, "settings.json");
56
- if (exists(baseSettingsPath)) {
57
- mergeSettingsFile(baseSettingsPath, destSettingsPath);
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 getDefaultSettings() {
196
+ async function backupCcrConfig() {
197
+ ensureI18nInitialized();
62
198
  try {
63
- const currentFilePath = fileURLToPath(import.meta.url);
64
- const distDir = dirname(dirname(currentFilePath));
65
- const rootDir = dirname(distDir);
66
- const templateSettingsPath = join(rootDir, "templates", "claude-code", "common", "settings.json");
67
- return readJsonConfig(templateSettingsPath) || {};
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("Failed to read template settings", error);
70
- return {};
210
+ console.error(a.red(`${i18n.t("ccr:ccrBackupFailed")}:`), error.message);
211
+ return null;
71
212
  }
72
213
  }
73
- function configureApi(apiConfig) {
74
- if (!apiConfig)
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
- if (apiConfig.authType === "api_key") {
87
- settings.env.ANTHROPIC_API_KEY = apiConfig.key;
88
- delete settings.env.ANTHROPIC_AUTH_TOKEN;
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
- addCompletedOnboarding();
237
+ setPrimaryApiKey();
107
238
  } catch (error) {
108
- console.error("Failed to set onboarding flag", error);
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 updateDefaultModel(model) {
179
- let settings = getDefaultSettings();
180
- const existingSettings = readJsonConfig(SETTINGS_FILE);
181
- if (existingSettings) {
182
- settings = existingSettings;
183
- }
184
- if (!settings.env) {
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 templateSettings = readJsonConfig(templatePath);
200
- if (!templateSettings) {
201
- console.error("Failed to read template settings");
202
- return;
203
- }
204
- if (!exists(targetPath)) {
205
- copyFile(templatePath, targetPath);
206
- return;
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
- for (const [key, value] of Object.entries(existingSettings)) {
227
- if (!schemaCriticalFields.includes(key)) {
228
- if (removeNullFields.includes(key) && value === null) {
229
- continue;
230
- }
231
- mergedSettings[key] = value;
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
- console.error("Failed to merge settings", error);
247
- if (exists(targetPath)) {
248
- console.log("Preserving existing settings");
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 getExistingModelConfig() {
255
- const settings = readJsonConfig(SETTINGS_FILE);
256
- if (!settings) {
257
- return null;
258
- }
259
- const hasModelEnv = MODEL_ENV_KEYS.some((key) => {
260
- const value = settings.env?.[key];
261
- return value !== void 0 && value !== null && value !== "";
262
- });
263
- if (hasModelEnv) {
264
- return "custom";
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
- return "default";
274
- }
275
- function getExistingCustomModelConfig() {
276
- const settings = readJsonConfig(SETTINGS_FILE);
277
- if (!settings || !settings.env) {
278
- return null;
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
- const {
281
- ANTHROPIC_MODEL,
282
- ANTHROPIC_SMALL_FAST_MODEL,
283
- ANTHROPIC_DEFAULT_HAIKU_MODEL,
284
- ANTHROPIC_DEFAULT_SONNET_MODEL,
285
- ANTHROPIC_DEFAULT_OPUS_MODEL
286
- } = settings.env;
287
- if (!ANTHROPIC_MODEL && !ANTHROPIC_DEFAULT_HAIKU_MODEL && !ANTHROPIC_DEFAULT_SONNET_MODEL && !ANTHROPIC_DEFAULT_OPUS_MODEL) {
288
- return null;
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
- return {
291
- primaryModel: ANTHROPIC_MODEL,
292
- haikuModel: ANTHROPIC_DEFAULT_HAIKU_MODEL || ANTHROPIC_SMALL_FAST_MODEL,
293
- sonnetModel: ANTHROPIC_DEFAULT_SONNET_MODEL,
294
- opusModel: ANTHROPIC_DEFAULT_OPUS_MODEL
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 getExistingApiConfig() {
298
- const settings = readJsonConfig(SETTINGS_FILE);
299
- if (!settings || !settings.env) {
300
- return null;
301
- }
302
- const { ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL } = settings.env;
303
- if (!ANTHROPIC_BASE_URL && !ANTHROPIC_API_KEY && !ANTHROPIC_AUTH_TOKEN) {
304
- return null;
305
- }
306
- let authType;
307
- let key;
308
- if (ANTHROPIC_AUTH_TOKEN) {
309
- authType = "auth_token";
310
- key = ANTHROPIC_AUTH_TOKEN;
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
- url: ANTHROPIC_BASE_URL || "",
317
- key: key || "",
318
- authType
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 applyAiLanguageDirective(aiOutputLang) {
322
- const claudeFile = join(CLAUDE_DIR, "CLAUDE.md");
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
- ensureI18nInitialized();
339
- const settings = readJsonConfig(SETTINGS_FILE) || {};
340
- if (settings.env) {
341
- delete settings.env.ANTHROPIC_BASE_URL;
342
- delete settings.env.ANTHROPIC_AUTH_TOKEN;
343
- delete settings.env.ANTHROPIC_API_KEY;
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
- writeJsonConfig(SETTINGS_FILE, settings);
346
- const vscConfig = readJsonConfig(CLAUDE_VSC_CONFIG_FILE);
347
- if (vscConfig) {
348
- delete vscConfig.primaryApiKey;
349
- writeJsonConfig(CLAUDE_VSC_CONFIG_FILE, vscConfig);
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
- ensureI18nInitialized();
355
- console.error(i18n.t("api:officialLoginFailed"), error);
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 promptApiConfigurationAction() {
469
+ async function configureCcrFeature() {
360
470
  ensureI18nInitialized();
361
- const existingConfig = getExistingApiConfig();
362
- if (!existingConfig) {
363
- return null;
471
+ const backupDir = backupExistingConfig();
472
+ if (backupDir) {
473
+ console.log(a.gray(`\u2714 ${i18n.t("configuration:backupSuccess")}: ${backupDir}`));
364
474
  }
365
- console.log(`
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
- const config = {
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 };