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.
- package/INSTALL_USAGE.md +152 -0
- package/bin/ccinstall.js +134 -58
- package/package.json +1 -1
package/INSTALL_USAGE.md
ADDED
|
@@ -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
|
|
199
|
-
if (
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
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) {
|