aihezu 2.6.4 → 2.7.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/bin/aihezu.js +9 -5
- package/commands/backup.js +3 -2
- package/commands/check.js +237 -0
- package/commands/clear.js +4 -3
- package/commands/config.js +155 -0
- package/commands/install.js +13 -19
- package/commands/recover.js +1 -4
- package/commands/reset.js +1 -4
- package/commands/usage.js +15 -60
- package/lib/backup.js +3 -2
- package/lib/cache.js +5 -4
- package/lib/constants.js +9 -0
- package/lib/hosts.js +2 -13
- package/lib/http.js +101 -0
- package/lib/prompts.js +7 -0
- package/package.json +2 -2
- package/services/claude.js +2 -1
- package/services/codex.js +1 -0
- package/services/gemini.js +1 -0
package/bin/aihezu.js
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const readline = require('readline');
|
|
6
|
+
const { askQuestion } = require('../lib/prompts');
|
|
6
7
|
|
|
7
8
|
const commands = {
|
|
8
9
|
install: require('../commands/install'),
|
|
10
|
+
config: require('../commands/config'),
|
|
11
|
+
check: require('../commands/check'),
|
|
9
12
|
clear: require('../commands/clear'),
|
|
10
13
|
usage: require('../commands/usage'),
|
|
11
14
|
backup: require('../commands/backup'),
|
|
@@ -25,6 +28,8 @@ function showHelp() {
|
|
|
25
28
|
console.log('用法: aihezu <command> [service]');
|
|
26
29
|
console.log('\n命令:');
|
|
27
30
|
console.log(' install <service> 配置服务 (API Key, URL, 网络设置)');
|
|
31
|
+
console.log(' config <service> 只修改配置 (不清理缓存, 不修改 hosts)');
|
|
32
|
+
console.log(' check [service] 检查当前配置状态 (配置文件 + 环境变量)');
|
|
28
33
|
console.log(' clear <service> 清理缓存并刷新网络设置 (自动备份)');
|
|
29
34
|
console.log(' backup <service> 手动备份配置');
|
|
30
35
|
console.log(' recover <service> 从备份恢复配置');
|
|
@@ -53,6 +58,9 @@ function showHelp() {
|
|
|
53
58
|
console.log(' 环境变量: GEMINI_API_KEY, GOOGLE_GEMINI_BASE_URL');
|
|
54
59
|
console.log('\n示例:');
|
|
55
60
|
console.log(' aihezu install claude');
|
|
61
|
+
console.log(' aihezu config claude (只修改配置,不清理缓存和 hosts)');
|
|
62
|
+
console.log(' aihezu check (检查所有服务的配置状态)');
|
|
63
|
+
console.log(' aihezu check claude (只检查 Claude Code 配置)');
|
|
56
64
|
console.log(' aihezu clear codex');
|
|
57
65
|
console.log(' aihezu backup claude (备份 Claude Code 配置)');
|
|
58
66
|
console.log(' aihezu recover claude (恢复 Claude Code 配置)');
|
|
@@ -63,10 +71,6 @@ function showHelp() {
|
|
|
63
71
|
console.log(' aihezu help (显示帮助信息)');
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
async function askQuestion(rl, question) {
|
|
67
|
-
return new Promise(resolve => rl.question(question, answer => resolve(answer.trim())));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
74
|
async function main() {
|
|
71
75
|
try {
|
|
72
76
|
if (args.length === 0 || args[0] === 'help') {
|
|
@@ -82,7 +86,7 @@ async function main() {
|
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
// Commands that don't require a service
|
|
85
|
-
if (commandName === 'usage') {
|
|
89
|
+
if (commandName === 'usage' || commandName === 'check') {
|
|
86
90
|
await commands[commandName](args.slice(1));
|
|
87
91
|
return;
|
|
88
92
|
}
|
package/commands/backup.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { createBackup, cleanOldBackups } = require('../lib/backup');
|
|
2
|
+
const { BACKUP_KEEP_COUNT } = require('../lib/constants');
|
|
2
3
|
|
|
3
4
|
async function backupCommand(service) {
|
|
4
5
|
console.log('');
|
|
@@ -30,11 +31,11 @@ async function backupCommand(service) {
|
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
// Clean old backups (keep
|
|
34
|
+
// Clean old backups (keep most recent)
|
|
34
35
|
console.log('');
|
|
35
36
|
const deletedCount = cleanOldBackups({
|
|
36
37
|
targetDir: targetDir,
|
|
37
|
-
keepCount:
|
|
38
|
+
keepCount: BACKUP_KEEP_COUNT
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
if (deletedCount > 0) {
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const homeDir = os.homedir();
|
|
6
|
+
|
|
7
|
+
// Helper function to mask sensitive data
|
|
8
|
+
function maskSensitive(value) {
|
|
9
|
+
if (!value || value.length <= 10) {
|
|
10
|
+
return value ? value.substring(0, 3) + '...' : '(未设置)';
|
|
11
|
+
}
|
|
12
|
+
return value.substring(0, 8) + '...' + value.substring(value.length - 4);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Check Claude Code configuration
|
|
16
|
+
function checkClaude() {
|
|
17
|
+
const configDir = path.join(homeDir, '.claude');
|
|
18
|
+
const settingsPath = path.join(configDir, 'settings.json');
|
|
19
|
+
|
|
20
|
+
console.log('\n📦 Claude Code 配置检查');
|
|
21
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
22
|
+
|
|
23
|
+
// Check config file
|
|
24
|
+
console.log('\n1️⃣ 配置文件: ~/.claude/settings.json');
|
|
25
|
+
if (fs.existsSync(settingsPath)) {
|
|
26
|
+
try {
|
|
27
|
+
const content = fs.readFileSync(settingsPath, 'utf8');
|
|
28
|
+
const settings = JSON.parse(content);
|
|
29
|
+
|
|
30
|
+
if (settings.env) {
|
|
31
|
+
console.log(' ✅ 文件存在');
|
|
32
|
+
console.log(' 📝 ANTHROPIC_AUTH_TOKEN:', maskSensitive(settings.env.ANTHROPIC_AUTH_TOKEN));
|
|
33
|
+
console.log(' 📝 ANTHROPIC_BASE_URL:', settings.env.ANTHROPIC_BASE_URL || '(未设置)');
|
|
34
|
+
} else {
|
|
35
|
+
console.log(' ⚠️ 文件存在但未包含 env 配置');
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.log(' ❌ 文件存在但格式错误:', e.message);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
console.log(' ❌ 文件不存在');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check environment variables
|
|
45
|
+
console.log('\n2️⃣ 环境变量:');
|
|
46
|
+
const envToken = process.env.ANTHROPIC_AUTH_TOKEN;
|
|
47
|
+
const envUrl = process.env.ANTHROPIC_BASE_URL;
|
|
48
|
+
console.log(' 📝 ANTHROPIC_AUTH_TOKEN:', maskSensitive(envToken));
|
|
49
|
+
console.log(' 📝 ANTHROPIC_BASE_URL:', envUrl || '(未设置)');
|
|
50
|
+
|
|
51
|
+
// Priority explanation
|
|
52
|
+
console.log('\n3️⃣ 优先级说明:');
|
|
53
|
+
console.log(' 🔹 Claude Code 优先使用配置文件 (~/.claude/settings.json)');
|
|
54
|
+
console.log(' 🔹 如果配置文件不存在,才会读取环境变量');
|
|
55
|
+
|
|
56
|
+
// Active configuration
|
|
57
|
+
console.log('\n4️⃣ 当前生效的配置:');
|
|
58
|
+
if (fs.existsSync(settingsPath)) {
|
|
59
|
+
try {
|
|
60
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
61
|
+
if (settings.env) {
|
|
62
|
+
console.log(' ✅ 使用配置文件中的设置');
|
|
63
|
+
console.log(' 📍 API Key:', maskSensitive(settings.env.ANTHROPIC_AUTH_TOKEN));
|
|
64
|
+
console.log(' 📍 API URL:', settings.env.ANTHROPIC_BASE_URL || '(未设置)');
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.log(' ⚠️ 配置文件格式错误,可能使用环境变量');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
console.log(' ℹ️ 配置文件不存在,使用环境变量');
|
|
71
|
+
console.log(' 📍 API Key:', maskSensitive(envToken));
|
|
72
|
+
console.log(' 📍 API URL:', envUrl || '(未设置)');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check Codex configuration
|
|
77
|
+
function checkCodex() {
|
|
78
|
+
const configDir = path.join(homeDir, '.codex');
|
|
79
|
+
const configPath = path.join(configDir, 'config.toml');
|
|
80
|
+
const authPath = path.join(configDir, 'auth.json');
|
|
81
|
+
|
|
82
|
+
console.log('\n📦 Codex 配置检查');
|
|
83
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
84
|
+
|
|
85
|
+
// Check config.toml
|
|
86
|
+
console.log('\n1️⃣ 配置文件: ~/.codex/config.toml');
|
|
87
|
+
if (fs.existsSync(configPath)) {
|
|
88
|
+
try {
|
|
89
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
90
|
+
console.log(' ✅ 文件存在');
|
|
91
|
+
|
|
92
|
+
// Extract model
|
|
93
|
+
const modelMatch = content.match(/model\s*=\s*"([^"]+)"/);
|
|
94
|
+
if (modelMatch) {
|
|
95
|
+
console.log(' 📝 Model:', modelMatch[1]);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Extract base_url
|
|
99
|
+
const baseUrlMatch = content.match(/base_url\s*=\s*"([^"]+)"/);
|
|
100
|
+
if (baseUrlMatch) {
|
|
101
|
+
console.log(' 📝 Base URL:', baseUrlMatch[1]);
|
|
102
|
+
}
|
|
103
|
+
} catch (e) {
|
|
104
|
+
console.log(' ❌ 文件存在但读取错误:', e.message);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
console.log(' ❌ 文件不存在');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check auth.json
|
|
111
|
+
console.log('\n2️⃣ 认证文件: ~/.codex/auth.json');
|
|
112
|
+
if (fs.existsSync(authPath)) {
|
|
113
|
+
try {
|
|
114
|
+
const authData = JSON.parse(fs.readFileSync(authPath, 'utf8'));
|
|
115
|
+
console.log(' ✅ 文件存在');
|
|
116
|
+
console.log(' 📝 AIHEZU_OAI_KEY:', maskSensitive(authData.AIHEZU_OAI_KEY));
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.log(' ❌ 文件存在但格式错误:', e.message);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
console.log(' ❌ 文件不存在');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check environment variables
|
|
125
|
+
console.log('\n3️⃣ 环境变量:');
|
|
126
|
+
const envKey = process.env.AIHEZU_OAI_KEY;
|
|
127
|
+
console.log(' 📝 AIHEZU_OAI_KEY:', maskSensitive(envKey));
|
|
128
|
+
|
|
129
|
+
// Priority explanation
|
|
130
|
+
console.log('\n4️⃣ 优先级说明:');
|
|
131
|
+
console.log(' 🔹 Codex 优先使用配置文件 (~/.codex/auth.json)');
|
|
132
|
+
console.log(' 🔹 如果配置文件不存在,才会读取环境变量');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check Gemini configuration
|
|
136
|
+
function checkGemini() {
|
|
137
|
+
const configDir = path.join(homeDir, '.gemini');
|
|
138
|
+
const envFilePath = path.join(configDir, '.env');
|
|
139
|
+
const settingsPath = path.join(configDir, 'settings.json');
|
|
140
|
+
|
|
141
|
+
console.log('\n📦 Google Gemini 配置检查');
|
|
142
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
143
|
+
|
|
144
|
+
// Check .env file
|
|
145
|
+
console.log('\n1️⃣ 配置文件: ~/.gemini/.env');
|
|
146
|
+
if (fs.existsSync(envFilePath)) {
|
|
147
|
+
try {
|
|
148
|
+
const content = fs.readFileSync(envFilePath, 'utf8');
|
|
149
|
+
console.log(' ✅ 文件存在');
|
|
150
|
+
|
|
151
|
+
const apiKeyMatch = content.match(/GEMINI_API_KEY="([^"]+)"/);
|
|
152
|
+
const baseUrlMatch = content.match(/GOOGLE_GEMINI_BASE_URL="([^"]+)"/);
|
|
153
|
+
|
|
154
|
+
console.log(' 📝 GEMINI_API_KEY:', maskSensitive(apiKeyMatch ? apiKeyMatch[1] : null));
|
|
155
|
+
console.log(' 📝 GOOGLE_GEMINI_BASE_URL:', baseUrlMatch ? baseUrlMatch[1] : '(未设置)');
|
|
156
|
+
} catch (e) {
|
|
157
|
+
console.log(' ❌ 文件存在但读取错误:', e.message);
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
console.log(' ❌ 文件不存在');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check settings.json (optional)
|
|
164
|
+
console.log('\n2️⃣ 可选配置: ~/.gemini/settings.json');
|
|
165
|
+
if (fs.existsSync(settingsPath)) {
|
|
166
|
+
console.log(' ✅ 文件存在');
|
|
167
|
+
} else {
|
|
168
|
+
console.log(' ℹ️ 文件不存在(可选)');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Check environment variables
|
|
172
|
+
console.log('\n3️⃣ 环境变量:');
|
|
173
|
+
const envApiKey = process.env.GEMINI_API_KEY;
|
|
174
|
+
const envBaseUrl = process.env.GOOGLE_GEMINI_BASE_URL;
|
|
175
|
+
console.log(' 📝 GEMINI_API_KEY:', maskSensitive(envApiKey));
|
|
176
|
+
console.log(' 📝 GOOGLE_GEMINI_BASE_URL:', envBaseUrl || '(未设置)');
|
|
177
|
+
|
|
178
|
+
// Priority explanation
|
|
179
|
+
console.log('\n4️⃣ 优先级说明:');
|
|
180
|
+
console.log(' 🔹 Gemini 优先使用配置文件 (~/.gemini/.env)');
|
|
181
|
+
console.log(' 🔹 如果配置文件不存在,才会读取系统环境变量');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function checkCommand(serviceOrArgs, args = []) {
|
|
185
|
+
console.log('');
|
|
186
|
+
console.log('🔍 配置检查工具');
|
|
187
|
+
console.log('🌐 Powered by https://aihezu.dev');
|
|
188
|
+
console.log('');
|
|
189
|
+
|
|
190
|
+
// Handle both calling patterns:
|
|
191
|
+
// 1. checkCommand(service, args) - when called with a specific service
|
|
192
|
+
// 2. checkCommand(args) - when called from main without a service
|
|
193
|
+
let service = null;
|
|
194
|
+
let actualArgs = args;
|
|
195
|
+
|
|
196
|
+
if (serviceOrArgs && typeof serviceOrArgs === 'object' && serviceOrArgs.name) {
|
|
197
|
+
// Called with a service object
|
|
198
|
+
service = serviceOrArgs;
|
|
199
|
+
} else if (Array.isArray(serviceOrArgs)) {
|
|
200
|
+
// Called with args array, need to parse for service name
|
|
201
|
+
actualArgs = serviceOrArgs;
|
|
202
|
+
const serviceName = actualArgs[0];
|
|
203
|
+
|
|
204
|
+
if (serviceName && !serviceName.startsWith('-')) {
|
|
205
|
+
// Load the service
|
|
206
|
+
const services = {
|
|
207
|
+
claude: require('../services/claude'),
|
|
208
|
+
codex: require('../services/codex'),
|
|
209
|
+
gemini: require('../services/gemini')
|
|
210
|
+
};
|
|
211
|
+
service = services[serviceName.toLowerCase()];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (service) {
|
|
216
|
+
// Check specific service
|
|
217
|
+
if (service.name === 'claude') {
|
|
218
|
+
checkClaude();
|
|
219
|
+
} else if (service.name === 'codex') {
|
|
220
|
+
checkCodex();
|
|
221
|
+
} else if (service.name === 'gemini') {
|
|
222
|
+
checkGemini();
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
// Check all services
|
|
226
|
+
checkClaude();
|
|
227
|
+
checkCodex();
|
|
228
|
+
checkGemini();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
232
|
+
console.log('💡 提示: 配置文件的优先级高于环境变量');
|
|
233
|
+
console.log('💡 使用 npx aihezu config <service> 可以快速修改配置');
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
module.exports = checkCommand;
|
package/commands/clear.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { modifyHostsFile } = require('../lib/hosts');
|
|
2
2
|
const { cleanCache } = require('../lib/cache');
|
|
3
3
|
const { createBackup, cleanOldBackups } = require('../lib/backup');
|
|
4
|
+
const { BACKUP_KEEP_COUNT } = require('../lib/constants');
|
|
4
5
|
|
|
5
6
|
async function clearCommand(service) {
|
|
6
7
|
console.log('');
|
|
@@ -37,10 +38,10 @@ async function clearCommand(service) {
|
|
|
37
38
|
showHeader: true
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
// Clean old backups (keep
|
|
41
|
+
// Clean old backups (keep most recent)
|
|
41
42
|
cleanOldBackups({
|
|
42
43
|
targetDir: service.cacheConfig.dir,
|
|
43
|
-
keepCount:
|
|
44
|
+
keepCount: BACKUP_KEEP_COUNT
|
|
44
45
|
});
|
|
45
46
|
} else {
|
|
46
47
|
console.log('ℹ️ 此服务未定义缓存配置。');
|
|
@@ -60,4 +61,4 @@ async function clearCommand(service) {
|
|
|
60
61
|
console.log('✅ 清理完成!');
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
module.exports = clearCommand;
|
|
64
|
+
module.exports = clearCommand;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { askQuestion } = require('../lib/prompts');
|
|
5
|
+
|
|
6
|
+
// Helper to extract value from args like --api=xxx or --api xxx
|
|
7
|
+
function getArgValue(args, keys) {
|
|
8
|
+
for (let i = 0; i < args.length; i++) {
|
|
9
|
+
const arg = args[i];
|
|
10
|
+
|
|
11
|
+
// Check for --key=value style
|
|
12
|
+
for (const key of keys) {
|
|
13
|
+
if (arg.startsWith(key + '=')) {
|
|
14
|
+
return arg.split('=')[1].trim();
|
|
15
|
+
}
|
|
16
|
+
// Check for --key value style
|
|
17
|
+
if (arg === key && i + 1 < args.length) {
|
|
18
|
+
return args[i + 1].trim();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function configCommand(service, args = []) {
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log('⚙️ ' + service.displayName + ' 配置修改工具');
|
|
28
|
+
console.log('🌐 Powered by https://aihezu.dev');
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('💡 提示: config 命令只修改配置,不清理缓存,不修改 hosts 文件');
|
|
31
|
+
console.log('');
|
|
32
|
+
|
|
33
|
+
const rl = readline.createInterface({
|
|
34
|
+
input: process.stdin,
|
|
35
|
+
output: process.stdout
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Parse CLI args for defaults
|
|
40
|
+
const cliApiUrl = getArgValue(args, ['--api', '--url', '--base-url', '--host']);
|
|
41
|
+
const cliApiKey = getArgValue(args, ['--key', '--token', '--api-key']);
|
|
42
|
+
const cliModel = getArgValue(args, ['--model']);
|
|
43
|
+
|
|
44
|
+
// 1. API URL
|
|
45
|
+
const defaultUrl = cliApiUrl || service.defaultApiUrl;
|
|
46
|
+
|
|
47
|
+
if (cliApiUrl) {
|
|
48
|
+
console.log('💡 检测到命令行参数,默认 API 地址: ' + defaultUrl);
|
|
49
|
+
} else {
|
|
50
|
+
console.log('💡 默认 API 地址: ' + defaultUrl);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const apiUrlInput = await askQuestion(rl, '请输入 API 地址 (直接回车使用默认地址): ');
|
|
54
|
+
let apiUrl = apiUrlInput || defaultUrl;
|
|
55
|
+
|
|
56
|
+
// Append service-specific suffix if needed
|
|
57
|
+
const suffix = service.apiSuffix || '';
|
|
58
|
+
if (suffix) {
|
|
59
|
+
const normalizedSuffix = suffix.startsWith('/') ? suffix : `/${suffix}`;
|
|
60
|
+
const trimmedUrl = apiUrl.replace(/\/+$/, '');
|
|
61
|
+
if (!trimmedUrl.endsWith(normalizedSuffix)) {
|
|
62
|
+
apiUrl = trimmedUrl + normalizedSuffix;
|
|
63
|
+
} else {
|
|
64
|
+
apiUrl = trimmedUrl;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Basic URL validation/normalization
|
|
69
|
+
// This part should handle http/https prefixing.
|
|
70
|
+
if (!/^https?:\/\//i.test(apiUrl)) {
|
|
71
|
+
apiUrl = 'https://' + apiUrl;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. API Key
|
|
75
|
+
let defaultApiKey = cliApiKey || '';
|
|
76
|
+
let apiKeyPrompt = '请输入您的 API Key: ';
|
|
77
|
+
if (defaultApiKey) {
|
|
78
|
+
console.log('💡 检测到命令行参数提供了 API Key');
|
|
79
|
+
apiKeyPrompt = '请输入您的 API Key (直接回车使用提供的 Key): ';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const apiKeyInput = await askQuestion(rl, apiKeyPrompt);
|
|
83
|
+
let apiKey = apiKeyInput || defaultApiKey;
|
|
84
|
+
|
|
85
|
+
if (!apiKey) {
|
|
86
|
+
console.error('❌ API Key 不能为空。');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 3. Extra Options (Service specific)
|
|
91
|
+
const options = {};
|
|
92
|
+
if (service.name === 'codex') {
|
|
93
|
+
let defaultModel = cliModel || 'gpt-5-codex';
|
|
94
|
+
|
|
95
|
+
console.log('请选择模型:');
|
|
96
|
+
console.log(' [1] gpt-5-codex' + (defaultModel === 'gpt-5-codex' ? ' (默认)' : ''));
|
|
97
|
+
console.log(' [2] claude-sonnet-4.5' + (defaultModel === 'claude-sonnet-4.5' ? ' (默认)' : ''));
|
|
98
|
+
console.log(' [3] 自定义模型名称' + (!['gpt-5-codex', 'claude-sonnet-4.5'].includes(defaultModel) ? ' (默认: ' + defaultModel + ')' : ''));
|
|
99
|
+
|
|
100
|
+
const choice = await askQuestion(rl, '请选择 [1/2/3]: ');
|
|
101
|
+
|
|
102
|
+
if (choice === '2') {
|
|
103
|
+
options.modelName = 'claude-sonnet-4.5';
|
|
104
|
+
} else if (choice === '3') {
|
|
105
|
+
const customModel = await askQuestion(rl, '请输入自定义模型名称: ');
|
|
106
|
+
options.modelName = customModel || defaultModel;
|
|
107
|
+
} else if (choice === '1' || choice === '') {
|
|
108
|
+
// Default choice (Enter or '1')
|
|
109
|
+
if (choice === '') {
|
|
110
|
+
options.modelName = defaultModel;
|
|
111
|
+
} else {
|
|
112
|
+
options.modelName = 'gpt-5-codex';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 4. Confirmation
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log('=== 配置摘要 ===');
|
|
120
|
+
console.log('服务类型: ' + service.displayName);
|
|
121
|
+
console.log('API 地址: ' + apiUrl);
|
|
122
|
+
if (options.modelName) console.log('模型: ' + options.modelName);
|
|
123
|
+
console.log('API Key: ' + apiKey.substring(0, 5) + '...');
|
|
124
|
+
|
|
125
|
+
console.log('');
|
|
126
|
+
const confirm = await askQuestion(rl, '是否继续? (Y/n, 默认回车确认): ');
|
|
127
|
+
// If user enters 'n' or 'no', cancel. Otherwise, proceed (including empty string for default Y).
|
|
128
|
+
if (confirm.toLowerCase() === 'n' || confirm.toLowerCase() === 'no') {
|
|
129
|
+
console.log('已取消。');
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 5. Execution - Only setup config, no cache cleaning, no hosts modification
|
|
134
|
+
console.log('');
|
|
135
|
+
console.log('=== 开始执行配置修改 ===');
|
|
136
|
+
|
|
137
|
+
// Setup Config only
|
|
138
|
+
const configFiles = await service.setupConfig(apiKey, apiUrl, options);
|
|
139
|
+
console.log('✅ 配置已写入:');
|
|
140
|
+
configFiles.forEach(f => console.log(' - ' + f));
|
|
141
|
+
|
|
142
|
+
console.log('');
|
|
143
|
+
console.log('ℹ️ 提示: config 命令不清理缓存,不修改 hosts 文件');
|
|
144
|
+
console.log('ℹ️ 如需完整安装(清理缓存 + 修改 hosts),请使用 install 命令');
|
|
145
|
+
console.log('');
|
|
146
|
+
console.log('✅ 配置修改完成!');
|
|
147
|
+
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error('❌ 配置修改过程中发生错误:', error);
|
|
150
|
+
} finally {
|
|
151
|
+
rl.close();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = configCommand;
|
package/commands/install.js
CHANGED
|
@@ -3,10 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { modifyHostsFile } = require('../lib/hosts');
|
|
5
5
|
const { cleanCache } = require('../lib/cache');
|
|
6
|
-
|
|
7
|
-
function askQuestion(rl, question) {
|
|
8
|
-
return new Promise(resolve => rl.question(question, answer => resolve(answer.trim())));
|
|
9
|
-
}
|
|
6
|
+
const { askQuestion } = require('../lib/prompts');
|
|
10
7
|
|
|
11
8
|
// Helper to extract value from args like --api=xxx or --api xxx
|
|
12
9
|
function getArgValue(args, keys) {
|
|
@@ -56,19 +53,16 @@ async function installCommand(service, args = []) {
|
|
|
56
53
|
const apiUrlInput = await askQuestion(rl, '请输入 API 地址 (直接回车使用默认地址): ');
|
|
57
54
|
let apiUrl = apiUrlInput || defaultUrl;
|
|
58
55
|
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// Append suffix if apiUrl does not already end with it
|
|
70
|
-
if (suffix && !apiUrl.endsWith(suffix)) {
|
|
71
|
-
apiUrl = apiUrl.replace(/\/+$/, '') + suffix; // Remove any trailing slash and add suffix
|
|
56
|
+
// Append service-specific suffix if needed
|
|
57
|
+
const suffix = service.apiSuffix || '';
|
|
58
|
+
if (suffix) {
|
|
59
|
+
const normalizedSuffix = suffix.startsWith('/') ? suffix : `/${suffix}`;
|
|
60
|
+
const trimmedUrl = apiUrl.replace(/\/+$/, '');
|
|
61
|
+
if (!trimmedUrl.endsWith(normalizedSuffix)) {
|
|
62
|
+
apiUrl = trimmedUrl + normalizedSuffix;
|
|
63
|
+
} else {
|
|
64
|
+
apiUrl = trimmedUrl;
|
|
65
|
+
}
|
|
72
66
|
}
|
|
73
67
|
|
|
74
68
|
// Basic URL validation/normalization
|
|
@@ -165,7 +159,7 @@ async function installCommand(service, args = []) {
|
|
|
165
159
|
} else {
|
|
166
160
|
console.log('');
|
|
167
161
|
console.log('=== 网络配置 ===');
|
|
168
|
-
console.log('
|
|
162
|
+
console.log('ℹ️ 此服务不需要修改 hosts 文件。');
|
|
169
163
|
}
|
|
170
164
|
|
|
171
165
|
console.log('');
|
|
@@ -178,4 +172,4 @@ async function installCommand(service, args = []) {
|
|
|
178
172
|
}
|
|
179
173
|
}
|
|
180
174
|
|
|
181
|
-
module.exports = installCommand;
|
|
175
|
+
module.exports = installCommand;
|
package/commands/recover.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
const readline = require('readline');
|
|
2
2
|
const { listBackups, restoreBackup, createBackup } = require('../lib/backup');
|
|
3
|
-
|
|
4
|
-
async function askQuestion(rl, question) {
|
|
5
|
-
return new Promise(resolve => rl.question(question, answer => resolve(answer.trim())));
|
|
6
|
-
}
|
|
3
|
+
const { askQuestion } = require('../lib/prompts');
|
|
7
4
|
|
|
8
5
|
async function recoverCommand(service) {
|
|
9
6
|
console.log('');
|
package/commands/reset.js
CHANGED
|
@@ -2,10 +2,7 @@ const readline = require('readline');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { createBackup } = require('../lib/backup');
|
|
5
|
-
|
|
6
|
-
async function askQuestion(rl, question) {
|
|
7
|
-
return new Promise(resolve => rl.question(question, answer => resolve(answer.trim())));
|
|
8
|
-
}
|
|
5
|
+
const { askQuestion } = require('../lib/prompts');
|
|
9
6
|
|
|
10
7
|
async function resetCommand(service) {
|
|
11
8
|
console.log('');
|
package/commands/usage.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const
|
|
5
|
-
const https = require('https');
|
|
4
|
+
const { postJson } = require('../lib/http');
|
|
6
5
|
|
|
7
6
|
function normalizeHttpUrl(url) {
|
|
8
7
|
if (!url) return '';
|
|
@@ -249,62 +248,6 @@ function readGeminiConfig() {
|
|
|
249
248
|
return configs;
|
|
250
249
|
}
|
|
251
250
|
|
|
252
|
-
function postJson(urlString, body, options = {}) {
|
|
253
|
-
const { timeoutMs = 15000 } = options;
|
|
254
|
-
const url = new URL(urlString);
|
|
255
|
-
const payload = JSON.stringify(body ?? {});
|
|
256
|
-
|
|
257
|
-
const transport = url.protocol === 'https:' ? https : http;
|
|
258
|
-
|
|
259
|
-
return new Promise((resolve, reject) => {
|
|
260
|
-
const req = transport.request(
|
|
261
|
-
{
|
|
262
|
-
method: 'POST',
|
|
263
|
-
hostname: url.hostname,
|
|
264
|
-
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
265
|
-
path: `${url.pathname}${url.search || ''}`,
|
|
266
|
-
headers: {
|
|
267
|
-
'Content-Type': 'application/json',
|
|
268
|
-
'Accept': 'application/json',
|
|
269
|
-
'Content-Length': Buffer.byteLength(payload),
|
|
270
|
-
'User-Agent': 'aihezu-cli'
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
res => {
|
|
274
|
-
res.setEncoding('utf8');
|
|
275
|
-
let raw = '';
|
|
276
|
-
|
|
277
|
-
res.on('data', chunk => {
|
|
278
|
-
raw += chunk;
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
res.on('end', () => {
|
|
282
|
-
let json = null;
|
|
283
|
-
try {
|
|
284
|
-
json = raw ? JSON.parse(raw) : null;
|
|
285
|
-
} catch (error) {
|
|
286
|
-
// keep json as null, return raw for debugging
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
resolve({
|
|
290
|
-
statusCode: res.statusCode || 0,
|
|
291
|
-
raw,
|
|
292
|
-
json
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
req.on('error', reject);
|
|
299
|
-
req.setTimeout(timeoutMs, () => {
|
|
300
|
-
req.destroy(new Error(`Request timed out after ${timeoutMs}ms`));
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
req.write(payload);
|
|
304
|
-
req.end();
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
251
|
function asNumber(value) {
|
|
309
252
|
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
310
253
|
if (typeof value === 'string' && value.trim() !== '' && Number.isFinite(Number(value))) {
|
|
@@ -613,12 +556,24 @@ async function usageCommand(args = []) {
|
|
|
613
556
|
? uniqueConfigsByOrigin(service.configs)
|
|
614
557
|
: service.configs;
|
|
615
558
|
|
|
616
|
-
|
|
559
|
+
const requests = configsToUse.map(config => {
|
|
617
560
|
const effectiveKey = overrideKey || config.authToken;
|
|
618
561
|
const sourceLabel = overrideKey ? `命令行 Key (${config.source})` : config.source;
|
|
619
562
|
console.log(`[查询中] ${sourceLabel} - Token: ${maskToken(effectiveKey)}`);
|
|
563
|
+
return {
|
|
564
|
+
sourceLabel,
|
|
565
|
+
promise: queryUsage(config.baseUrl, effectiveKey)
|
|
566
|
+
};
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
const results = await Promise.allSettled(requests.map(item => item.promise));
|
|
620
570
|
|
|
621
|
-
|
|
571
|
+
for (let i = 0; i < results.length; i += 1) {
|
|
572
|
+
const outcome = results[i];
|
|
573
|
+
const sourceLabel = requests[i].sourceLabel;
|
|
574
|
+
const result = outcome.status === 'fulfilled'
|
|
575
|
+
? outcome.value
|
|
576
|
+
: { error: outcome.reason ? outcome.reason.message : '查询失败' };
|
|
622
577
|
|
|
623
578
|
if (result.error) {
|
|
624
579
|
console.log(`[错误] ${result.error}`);
|
package/lib/backup.js
CHANGED
|
@@ -2,6 +2,7 @@ const path = require('path');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const { getLocalTimestamp } = require('./cache');
|
|
5
|
+
const { BACKUP_KEEP_COUNT } = require('./constants');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Create a tar.gz backup of files/directories
|
|
@@ -186,11 +187,11 @@ function restoreBackup(options = {}) {
|
|
|
186
187
|
* Clean old backups, keeping only the most recent N backups
|
|
187
188
|
* @param {Object} options - Clean options
|
|
188
189
|
* @param {string} options.targetDir - Directory containing backups
|
|
189
|
-
* @param {number} options.keepCount - Number of recent backups to keep (default:
|
|
190
|
+
* @param {number} options.keepCount - Number of recent backups to keep (default: BACKUP_KEEP_COUNT)
|
|
190
191
|
* @returns {number} - Number of backups deleted
|
|
191
192
|
*/
|
|
192
193
|
function cleanOldBackups(options = {}) {
|
|
193
|
-
const { targetDir, keepCount =
|
|
194
|
+
const { targetDir, keepCount = BACKUP_KEEP_COUNT } = options;
|
|
194
195
|
|
|
195
196
|
if (!targetDir || !fs.existsSync(targetDir)) {
|
|
196
197
|
return 0;
|
package/lib/cache.js
CHANGED
|
@@ -2,6 +2,7 @@ const path = require('path');
|
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
|
+
const { BACKUP_KEEP_COUNT } = require('./constants');
|
|
5
6
|
|
|
6
7
|
// Generate local timestamp: YYYYMMDDHHMMSS
|
|
7
8
|
function getLocalTimestamp() {
|
|
@@ -91,7 +92,7 @@ function cleanCache(options = {}) {
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
// Clean old backups (keep only the
|
|
95
|
+
// Clean old backups (keep only the most recent backups)
|
|
95
96
|
try {
|
|
96
97
|
const items = fs.readdirSync(targetDir);
|
|
97
98
|
const backupItems = [];
|
|
@@ -129,15 +130,15 @@ function cleanCache(options = {}) {
|
|
|
129
130
|
backupGroups[backup.baseName].push(backup);
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
// For each group, keep only the
|
|
133
|
+
// For each group, keep only the most recent backups
|
|
133
134
|
for (const baseName in backupGroups) {
|
|
134
135
|
const group = backupGroups[baseName];
|
|
135
136
|
|
|
136
137
|
// Sort by timestamp descending (newest first)
|
|
137
138
|
group.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
138
139
|
|
|
139
|
-
// Delete backups beyond the
|
|
140
|
-
const backupsToDelete = group.slice(
|
|
140
|
+
// Delete backups beyond the most recent
|
|
141
|
+
const backupsToDelete = group.slice(BACKUP_KEEP_COUNT);
|
|
141
142
|
|
|
142
143
|
for (const backup of backupsToDelete) {
|
|
143
144
|
try {
|
package/lib/constants.js
ADDED
package/lib/hosts.js
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
const { execSync } = require('child_process');
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
// Generate local timestamp: YYYYMMDDHHMMSS
|
|
6
|
-
function getLocalTimestamp() {
|
|
7
|
-
const now = new Date();
|
|
8
|
-
const year = now.getFullYear();
|
|
9
|
-
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
10
|
-
const day = String(now.getDate()).padStart(2, '0');
|
|
11
|
-
const hours = String(now.getHours()).padStart(2, '0');
|
|
12
|
-
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
13
|
-
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
14
|
-
return `${year}${month}${day}${hours}${minutes}${seconds}`;
|
|
15
|
-
}
|
|
4
|
+
const { getLocalTimestamp } = require('./cache');
|
|
16
5
|
|
|
17
6
|
/**
|
|
18
7
|
* Modifies the system hosts file.
|
|
@@ -72,7 +61,7 @@ function modifyHostsFile(hostEntries = [], markerText = '# Added by aihezu CLI')
|
|
|
72
61
|
|
|
73
62
|
// Remove lines matching our target domains
|
|
74
63
|
return !targetDomains.some(domain => {
|
|
75
|
-
const regex = new RegExp('\\s+' + domain.replace(
|
|
64
|
+
const regex = new RegExp('\\s+' + domain.replace(/\./g, '\\.') + '(\\s|$)', 'i');
|
|
76
65
|
return regex.test(trimmed);
|
|
77
66
|
});
|
|
78
67
|
});
|
package/lib/http.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const http = require('http');
|
|
2
|
+
const https = require('https');
|
|
3
|
+
const { HTTP_TIMEOUT, HTTP_RETRY_COUNT, HTTP_RETRY_DELAY } = require('./constants');
|
|
4
|
+
|
|
5
|
+
const DEFAULT_RETRY_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);
|
|
6
|
+
|
|
7
|
+
function sleep(ms) {
|
|
8
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function sendJsonRequest(method, urlString, body, timeoutMs) {
|
|
12
|
+
const url = new URL(urlString);
|
|
13
|
+
const payload = JSON.stringify(body ?? {});
|
|
14
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
15
|
+
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const req = transport.request(
|
|
18
|
+
{
|
|
19
|
+
method,
|
|
20
|
+
hostname: url.hostname,
|
|
21
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
22
|
+
path: `${url.pathname}${url.search || ''}`,
|
|
23
|
+
headers: {
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'Accept': 'application/json',
|
|
26
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
27
|
+
'User-Agent': 'aihezu-cli'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
res => {
|
|
31
|
+
res.setEncoding('utf8');
|
|
32
|
+
let raw = '';
|
|
33
|
+
|
|
34
|
+
res.on('data', chunk => {
|
|
35
|
+
raw += chunk;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
res.on('end', () => {
|
|
39
|
+
let json = null;
|
|
40
|
+
try {
|
|
41
|
+
json = raw ? JSON.parse(raw) : null;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// ignore JSON parse errors
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
resolve({
|
|
47
|
+
statusCode: res.statusCode || 0,
|
|
48
|
+
raw,
|
|
49
|
+
json
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
req.on('error', reject);
|
|
56
|
+
req.setTimeout(timeoutMs, () => {
|
|
57
|
+
req.destroy(new Error(`Request timed out after ${timeoutMs}ms`));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
req.write(payload);
|
|
61
|
+
req.end();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function postJson(urlString, body, options = {}) {
|
|
66
|
+
const {
|
|
67
|
+
timeoutMs = HTTP_TIMEOUT,
|
|
68
|
+
retryCount = HTTP_RETRY_COUNT,
|
|
69
|
+
retryDelayMs = HTTP_RETRY_DELAY,
|
|
70
|
+
retryStatusCodes = DEFAULT_RETRY_STATUS_CODES
|
|
71
|
+
} = options;
|
|
72
|
+
|
|
73
|
+
const retryCodes = retryStatusCodes instanceof Set ? retryStatusCodes : new Set(retryStatusCodes);
|
|
74
|
+
let attempt = 0;
|
|
75
|
+
let lastError = null;
|
|
76
|
+
|
|
77
|
+
while (attempt <= retryCount) {
|
|
78
|
+
try {
|
|
79
|
+
const response = await sendJsonRequest('POST', urlString, body, timeoutMs);
|
|
80
|
+
if (retryCodes.has(response.statusCode) && attempt < retryCount) {
|
|
81
|
+
await sleep(retryDelayMs * Math.pow(2, attempt));
|
|
82
|
+
attempt += 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
return response;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
lastError = error;
|
|
88
|
+
if (attempt >= retryCount) {
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
await sleep(retryDelayMs * Math.pow(2, attempt));
|
|
92
|
+
attempt += 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw lastError || new Error('Request failed');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
postJson
|
|
101
|
+
};
|
package/lib/prompts.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aihezu",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "AI 开发环境配置工具 - 支持 Claude Code, Codex, Google Gemini 的本地化配置、代理设置与缓存清理",
|
|
5
5
|
"main": "bin/aihezu.js",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"author": "aihezu",
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": ">=14.
|
|
36
|
+
"node": ">=14.14.0"
|
|
37
37
|
},
|
|
38
38
|
"repository": {
|
|
39
39
|
"type": "git",
|
package/services/claude.js
CHANGED
|
@@ -10,7 +10,8 @@ const settingsPath = path.join(configDir, 'settings.json');
|
|
|
10
10
|
module.exports = {
|
|
11
11
|
name: 'claude',
|
|
12
12
|
displayName: 'Claude Code',
|
|
13
|
-
defaultApiUrl: 'https://
|
|
13
|
+
defaultApiUrl: 'https://cn.aihezu.dev/api',
|
|
14
|
+
apiSuffix: '/api',
|
|
14
15
|
|
|
15
16
|
// Cache cleaning configuration
|
|
16
17
|
cacheConfig: {
|
package/services/codex.js
CHANGED