aihezu 1.7.2 → 1.8.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.
@@ -0,0 +1,152 @@
1
+ # aihezu install 使用说明
2
+
3
+ ## 功能概述
4
+
5
+ `npx aihezu install` 命令现在是完全交互式的,支持配置 Claude 和 Codex 服务。
6
+
7
+ ## 使用方式
8
+
9
+ ### 1. 基本用法(无参数)
10
+
11
+ ```bash
12
+ npx aihezu install
13
+ ```
14
+
15
+ 交互流程:
16
+ 1. 选择服务类型(Claude / Codex)
17
+ 2. 输入 API 地址(显示系统默认值,可直接回车使用)
18
+ 3. 选择模型(仅 Codex)
19
+ 4. 输入 API Key
20
+ 5. 查看配置摘要并确认
21
+ 6. 自动备份现有配置
22
+ 7. 自动清理缓存
23
+ 8. 写入新配置
24
+
25
+ **示例输出:**
26
+ ```
27
+ 🔧 Claude / Codex API 配置工具
28
+ 🌐 Powered by https://aihezu.dev
29
+
30
+ 请选择服务类型 [1] Claude (默认) / [2] Codex: 1
31
+
32
+ ✅ 已选择服务: Claude
33
+
34
+ 💡 Claude 默认 API 地址: https://cc.aihezu.dev/api
35
+ 请输入 API 地址(直接回车使用默认地址):
36
+ ✅ API 地址: https://cc.aihezu.dev/api
37
+ ...
38
+ ```
39
+
40
+ ### 2. 带参数用法(推荐)
41
+
42
+ #### Claude 企业域名
43
+
44
+ ```bash
45
+ npx aihezu install --api hk.aihezu.dev
46
+ ```
47
+
48
+ **交互提示:**
49
+ ```
50
+ ✅ 已选择服务: Claude
51
+
52
+ 💡 检测到命令行参数,默认 API 地址: https://hk.aihezu.dev/api
53
+ 请输入 API 地址(直接回车使用默认地址):
54
+ ```
55
+
56
+ 此时用户可以:
57
+ - **直接回车**:使用命令行参数 `https://hk.aihezu.dev/api`
58
+ - **输入其他值**:覆盖命令行参数
59
+
60
+ #### Codex 企业域名
61
+
62
+ ```bash
63
+ npx aihezu install --provider codex --api hk.aihezu.dev
64
+ ```
65
+
66
+ **交互提示:**
67
+ ```
68
+ ✅ 已选择服务: Codex
69
+
70
+ 💡 检测到命令行参数,默认 API 地址: https://hk.aihezu.dev/openai
71
+ 请输入 API 地址(直接回车使用默认地址):
72
+
73
+ 请选择模型:
74
+ [1] gpt-5-codex (默认)
75
+ [2] claude-sonnet-4.5
76
+ [3] 自定义模型名称
77
+ 请选择 [1/2/3]:
78
+ ```
79
+
80
+ ## 参数优先级
81
+
82
+ 对于 API 地址配置:
83
+
84
+ 1. **用户交互输入** > 命令行参数 > 系统默认值
85
+ 2. 如果带了 `--api` 参数,会将参数值作为默认值显示给用户
86
+ 3. 用户可以直接回车使用这个默认值,也可以输入新值覆盖
87
+
88
+ ## 配置摘要示例
89
+
90
+ ```
91
+ === 配置摘要 ===
92
+ 服务类型: Claude
93
+ API 地址: https://hk.aihezu.dev/api
94
+ API Key: sk-ant-123...xyz
95
+
96
+ ⚠️ 即将执行以下操作:
97
+ 1. 备份现有配置文件
98
+ 2. 清理缓存数据
99
+ 3. 写入新的配置
100
+ 4. 修改 hosts 文件
101
+
102
+ 是否继续?(y/n):
103
+ ```
104
+
105
+ ## 自动备份
106
+
107
+ 无论是否首次使用,每次运行都会:
108
+ - 自动备份现有配置文件(如果存在)
109
+ - 备份文件命名格式:`原文件名.backup-时间戳`
110
+ - 示例:`settings.json.backup-2024-12-10T08-30-45-123Z`
111
+
112
+ ## 模型选择(Codex)
113
+
114
+ Codex 用户可以选择:
115
+ 1. `gpt-5-codex`(默认)
116
+ 2. `claude-sonnet-4.5`
117
+ 3. 自定义模型名称
118
+
119
+ ## 完整示例
120
+
121
+ ### 场景 1:企业用户首次配置 Claude
122
+
123
+ ```bash
124
+ npx aihezu install --api company.aihezu.dev
125
+ ```
126
+
127
+ 只需要:
128
+ 1. 直接回车确认企业域名
129
+ 2. 输入 API Key
130
+ 3. 确认配置摘要
131
+ 4. 等待完成
132
+
133
+ ### 场景 2:切换到 Codex 服务
134
+
135
+ ```bash
136
+ npx aihezu install --provider codex
137
+ ```
138
+
139
+ 1. 系统识别 Codex 服务
140
+ 2. 显示默认地址 `https://cc.aihezu.dev/openai`
141
+ 3. 选择模型
142
+ 4. 输入 API Key
143
+ 5. 自动备份 Claude 配置
144
+ 6. 完成切换
145
+
146
+ ### 场景 3:完全自定义配置
147
+
148
+ ```bash
149
+ npx aihezu install
150
+ ```
151
+
152
+ 交互式输入所有选项,完全自定义。
package/bin/ccinstall.js CHANGED
@@ -147,11 +147,6 @@ function writeClaudeSettings(apiKey, apiBaseUrl) {
147
147
  settings = JSON.parse(content);
148
148
  } catch (e) {
149
149
  console.log('⚠️ 现有配置文件格式错误,将创建新的配置');
150
- // 备份错误的配置文件
151
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
152
- const backupPath = `${claudeSettingsPath}.backup-${timestamp}`;
153
- fs.writeFileSync(backupPath, content);
154
- console.log(`📦 已备份原配置文件到: ${path.basename(backupPath)}`);
155
150
  }
156
151
  }
157
152
 
@@ -172,7 +167,7 @@ function writeClaudeSettings(apiKey, apiBaseUrl) {
172
167
  console.log(' ANTHROPIC_BASE_URL:', apiBaseUrl);
173
168
  }
174
169
 
175
- function writeCodexConfig(apiKey, codexBaseUrl) {
170
+ function writeCodexConfig(apiKey, codexBaseUrl, modelName = 'gpt-5-codex') {
176
171
  if (!fs.existsSync(codexDir)) {
177
172
  console.log('📁 创建 ~/.codex 目录...');
178
173
  fs.mkdirSync(codexDir, { recursive: true });
@@ -194,10 +189,11 @@ function writeCodexConfig(apiKey, codexBaseUrl) {
194
189
  console.log(`ℹ️ 检测到现有 provider: ${providerName}`);
195
190
  }
196
191
 
197
- // 备份原配置
198
- const configBackup = backupFile(codexConfigPath);
199
- if (configBackup) {
200
- console.log(`📦 已备份原配置文件到: ${path.basename(configBackup)}`);
192
+ // 更新 model
193
+ const modelPattern = /^model\s*=\s*"[^"]*"/m;
194
+ if (modelPattern.test(existingConfig)) {
195
+ existingConfig = existingConfig.replace(modelPattern, `model = "${modelName}"`);
196
+ console.log('✏️ 已更新模型配置');
201
197
  }
202
198
 
203
199
  // 更新 base_url
@@ -238,7 +234,7 @@ function writeCodexConfig(apiKey, codexBaseUrl) {
238
234
  console.log('📝 创建新的 Codex 配置文件...');
239
235
  configContent = [
240
236
  'model_provider = "aihezu"',
241
- 'model = "gpt-5-codex"',
237
+ `model = "${modelName}"`,
242
238
  'model_reasoning_effort = "high"',
243
239
  'disable_response_storage = true',
244
240
  'preferred_auth_method = "apikey"',
@@ -260,14 +256,8 @@ function writeCodexConfig(apiKey, codexBaseUrl) {
260
256
  try {
261
257
  const content = fs.readFileSync(codexAuthPath, 'utf8');
262
258
  authData = JSON.parse(content);
263
- // 备份旧的 auth.json
264
- const authBackup = backupFile(codexAuthPath);
265
- if (authBackup) {
266
- console.log(`📦 已备份原 auth.json 到: ${path.basename(authBackup)}`);
267
- }
268
259
  } catch (e) {
269
- const backupPath = backupFile(codexAuthPath);
270
- console.log('⚠️ 现有 auth.json 解析失败,已备份旧文件:', backupPath ? path.basename(backupPath) : '未备份');
260
+ console.log('⚠️ 现有 auth.json 解析失败,将创建新文件');
271
261
  authData = {};
272
262
  }
273
263
  }
@@ -302,6 +292,7 @@ async function main() {
302
292
  });
303
293
 
304
294
  try {
295
+ // 1. 选择服务类型
305
296
  const cliProviderInput = parseProviderArg(cliArgs);
306
297
  let provider = resolveProvider(cliProviderInput);
307
298
 
@@ -310,76 +301,161 @@ async function main() {
310
301
  provider = resolveProvider(providerAnswer);
311
302
  }
312
303
 
313
- const apiBaseInput = provider === PROVIDERS.CLAUDE ? parseApiBaseInput(cliArgs) : '';
314
- const codexBaseInput = provider === PROVIDERS.CODEX ? parseApiBaseInput(cliArgs) : '';
304
+ console.log(`\n✅ 已选择服务: ${provider === PROVIDERS.CLAUDE ? 'Claude' : 'Codex'}\n`);
315
305
 
306
+ // 2. 交互式询问 API 地址
316
307
  let apiBaseUrl = DEFAULT_CLAUDE_API_BASE;
317
308
  let codexBaseUrl = DEFAULT_CODEX_BASE_URL;
309
+
318
310
  if (provider === PROVIDERS.CLAUDE) {
311
+ // 先检查命令行参数
312
+ const cliApiInput = parseApiBaseInput(cliArgs);
313
+ let defaultApiBase = DEFAULT_CLAUDE_API_BASE;
314
+ let promptMessage = '';
315
+
316
+ if (cliApiInput) {
317
+ // 如果用户传了参数,将参数作为默认值
318
+ try {
319
+ defaultApiBase = normalizeClaudeApiBaseUrl(cliApiInput);
320
+ promptMessage = `💡 检测到命令行参数,默认 API 地址: ${defaultApiBase}`;
321
+ } catch (error) {
322
+ console.error(`❌ 命令行参数错误: ${error.message}`);
323
+ process.exit(1);
324
+ }
325
+ } else {
326
+ promptMessage = `💡 Claude 默认 API 地址: ${defaultApiBase}`;
327
+ }
328
+
329
+ console.log(promptMessage);
330
+ const apiBaseInput = await askQuestion(rl, '请输入 API 地址(直接回车使用默认地址): ');
331
+
319
332
  try {
320
- apiBaseUrl = normalizeClaudeApiBaseUrl(apiBaseInput);
333
+ apiBaseUrl = normalizeClaudeApiBaseUrl(apiBaseInput || defaultApiBase);
334
+ console.log(`✅ API 地址: ${apiBaseUrl}\n`);
321
335
  } catch (error) {
322
336
  console.error(`❌ ${error.message}`);
323
337
  process.exit(1);
324
338
  }
325
339
  } else {
340
+ // 先检查命令行参数
341
+ const cliCodexInput = parseApiBaseInput(cliArgs);
342
+ let defaultCodexBase = DEFAULT_CODEX_BASE_URL;
343
+ let promptMessage = '';
344
+
345
+ if (cliCodexInput) {
346
+ // 如果用户传了参数,将参数作为默认值
347
+ try {
348
+ defaultCodexBase = normalizeCodexBaseUrl(cliCodexInput);
349
+ promptMessage = `💡 检测到命令行参数,默认 API 地址: ${defaultCodexBase}`;
350
+ } catch (error) {
351
+ console.error(`❌ 命令行参数错误: ${error.message}`);
352
+ process.exit(1);
353
+ }
354
+ } else {
355
+ promptMessage = `💡 Codex 默认 API 地址: ${defaultCodexBase}`;
356
+ }
357
+
358
+ console.log(promptMessage);
359
+ const codexBaseInput = await askQuestion(rl, '请输入 API 地址(直接回车使用默认地址): ');
360
+
326
361
  try {
327
- codexBaseUrl = normalizeCodexBaseUrl(codexBaseInput);
362
+ codexBaseUrl = normalizeCodexBaseUrl(codexBaseInput || defaultCodexBase);
363
+ console.log(`✅ API 地址: ${codexBaseUrl}\n`);
328
364
  } catch (error) {
329
365
  console.error(`❌ ${error.message}`);
330
366
  process.exit(1);
331
367
  }
332
368
  }
333
369
 
334
- const usingCustomDomain = provider === PROVIDERS.CLAUDE && !!apiBaseInput;
335
- const usingCustomCodexDomain = provider === PROVIDERS.CODEX && !!codexBaseInput;
336
-
337
- if (provider === PROVIDERS.CLAUDE) {
338
- if (usingCustomDomain) {
339
- console.log(`🏢 已启用企业独立域名: ${apiBaseUrl}\n`);
370
+ // 3. 交互式询问模型(Codex 专用)
371
+ let selectedModel = '';
372
+ if (provider === PROVIDERS.CODEX) {
373
+ console.log('请选择模型:');
374
+ console.log(' [1] gpt-5-codex (默认)');
375
+ console.log(' [2] claude-sonnet-4.5');
376
+ console.log(' [3] 自定义模型名称');
377
+ const modelChoice = await askQuestion(rl, '请选择 [1/2/3]: ');
378
+
379
+ if (modelChoice === '2') {
380
+ selectedModel = 'claude-sonnet-4.5';
381
+ } else if (modelChoice === '3') {
382
+ const customModel = await askQuestion(rl, '请输入自定义模型名称: ');
383
+ selectedModel = customModel.trim() || 'gpt-5-codex';
340
384
  } else {
341
- console.log(`ℹ️ 未指定域名,将使用默认地址: ${DEFAULT_CLAUDE_API_BASE}`);
342
- console.log(' 企业用户可使用 --api 或 --api-url 指定独立域名,例如:');
343
- console.log(' sudo npx aihezu install --api your-org.aihezu.dev\n');
344
- }
345
- } else {
346
- if (usingCustomCodexDomain) {
347
- console.log(`🏢 Codex 已使用企业域名: ${codexBaseUrl}\n`);
348
- } else {
349
- console.log(`🤖 已选择 Codex,默认网关: ${DEFAULT_CODEX_BASE_URL}`);
350
- console.log(' 企业用户可用 --api 或 --api-url 指定独立域名,例如:');
351
- console.log(' npx aihezu install --provider codex --api your-org.aihezu.dev\n');
352
- }
353
- }
354
-
355
- const needCleanAnswer = await askQuestion(
356
- rl,
357
- '您是从其他服务切换过来的吗?(y/n,如果是首次使用请输入 n): '
358
- );
359
- const needClean = ['y', 'yes'].includes(needCleanAnswer.toLowerCase());
360
-
361
- if (needClean) {
362
- try {
363
- console.log('\n=== 清理旧缓存 ===');
364
- const count = cleanCache({ showHeader: false });
365
- console.log(`\n✅ 缓存清理完成!(共处理 ${count} 项)`);
366
- console.log('💡 配置文件已保留,即将配置新的 API Key\n');
367
- } catch (error) {
368
- console.error('⚠️ 清理缓存时出错:', error.message);
369
- console.log('继续配置流程...\n');
385
+ selectedModel = 'gpt-5-codex';
370
386
  }
387
+ console.log(`✅ 已选择模型: ${selectedModel}\n`);
371
388
  }
372
389
 
390
+ // 4. 询问 API Key
373
391
  const apiKey = await askQuestion(rl, '请输入您的 API Key: ');
374
392
  if (!apiKey) {
375
393
  console.error('❌ API Key 不能为空');
376
394
  process.exit(1);
377
395
  }
378
396
 
397
+ // 5. 显示配置摘要并确认
398
+ console.log('\n=== 配置摘要 ===');
399
+ console.log(`服务类型: ${provider === PROVIDERS.CLAUDE ? 'Claude' : 'Codex'}`);
400
+ if (provider === PROVIDERS.CLAUDE) {
401
+ console.log(`API 地址: ${apiBaseUrl}`);
402
+ } else {
403
+ console.log(`API 地址: ${codexBaseUrl}`);
404
+ console.log(`模型: ${selectedModel}`);
405
+ }
406
+ console.log(`API Key: ${apiKey.substring(0, 10)}...${apiKey.substring(apiKey.length - 4)}`);
407
+ console.log('\n⚠️ 即将执行以下操作:');
408
+ console.log(' 1. 备份现有配置文件');
409
+ console.log(' 2. 清理缓存数据');
410
+ console.log(' 3. 写入新的配置');
411
+ if (provider === PROVIDERS.CLAUDE) {
412
+ console.log(' 4. 修改 hosts 文件');
413
+ }
414
+
415
+ const confirmAnswer = await askQuestion(rl, '\n是否继续?(y/n): ');
416
+ if (!['y', 'yes'].includes(confirmAnswer.toLowerCase())) {
417
+ console.log('❌ 已取消配置');
418
+ process.exit(0);
419
+ }
420
+
421
+ // 6. 默认执行备份和清理缓存
422
+ try {
423
+ console.log('\n=== 备份与清理 ===');
424
+
425
+ // 备份现有配置文件
426
+ if (provider === PROVIDERS.CLAUDE && fs.existsSync(claudeSettingsPath)) {
427
+ const backup = backupFile(claudeSettingsPath);
428
+ if (backup) {
429
+ console.log(`📦 已备份 Claude 配置: ${path.basename(backup)}`);
430
+ }
431
+ } else if (provider === PROVIDERS.CODEX) {
432
+ if (fs.existsSync(codexConfigPath)) {
433
+ const backup = backupFile(codexConfigPath);
434
+ if (backup) {
435
+ console.log(`📦 已备份 Codex config.toml: ${path.basename(backup)}`);
436
+ }
437
+ }
438
+ if (fs.existsSync(codexAuthPath)) {
439
+ const backup = backupFile(codexAuthPath);
440
+ if (backup) {
441
+ console.log(`📦 已备份 Codex auth.json: ${path.basename(backup)}`);
442
+ }
443
+ }
444
+ }
445
+
446
+ // 清理缓存
447
+ const count = cleanCache({ showHeader: false });
448
+ console.log(`✅ 缓存清理完成!(共处理 ${count} 项)\n`);
449
+ } catch (error) {
450
+ console.error('⚠️ 备份或清理时出错:', error.message);
451
+ console.log('继续配置流程...\n');
452
+ }
453
+
454
+ // 7. 写入配置
379
455
  if (provider === PROVIDERS.CLAUDE) {
380
456
  writeClaudeSettings(apiKey.trim(), apiBaseUrl);
381
457
  } else {
382
- writeCodexConfig(apiKey.trim(), codexBaseUrl);
458
+ writeCodexConfig(apiKey.trim(), codexBaseUrl, selectedModel);
383
459
  }
384
460
 
385
461
  if (provider === PROVIDERS.CLAUDE) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aihezu",
3
- "version": "1.7.2",
3
+ "version": "1.8.0",
4
4
  "description": "Claude Code CLI 清理工具 - 快速备份和清理 Claude Code 的本地配置和缓存,同时修改 hosts 文件实现本地代理",
5
5
  "main": "bin/ccclear.js",
6
6
  "bin": {