aihezu 1.7.0 → 1.7.2
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/README.md +3 -1
- package/bin/aihezu.js +1 -0
- package/bin/ccinstall.js +138 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
- `ccinstall` / `install`:交互式配置 Claude(默认)或 Codex
|
|
18
18
|
- Claude:默认 `https://cc.aihezu.dev/api`,企业可用 `--api` / `--api-url` 指定独立域名
|
|
19
|
-
- Codex
|
|
19
|
+
- Codex:默认 `https://cc.aihezu.dev/openai`,企业可用 `--api` / `--api-url` 指定独立域名;写入 `~/.codex/config.toml` 和 `auth.json`,使用 `AIHEZU_OAI_KEY`
|
|
20
20
|
- `ccclear`:清理 Claude Code 缓存和配置
|
|
21
21
|
|
|
22
22
|
## 清理内容
|
|
@@ -65,6 +65,8 @@ sudo ccinstall --api your-company.aihezu.dev
|
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
sudo npx aihezu install --provider codex
|
|
68
|
+
# Codex 企业独立域名(自动补全为 /openai)
|
|
69
|
+
sudo npx aihezu install --provider codex --api your-org.aihezu.dev
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
### 清理 Claude Code(ccclear)
|
package/bin/aihezu.js
CHANGED
|
@@ -21,6 +21,7 @@ function showHelp() {
|
|
|
21
21
|
console.log(' npx aihezu ccinstall # 配置 API Key (默认 Claude)');
|
|
22
22
|
console.log(' npx aihezu install --api your-org.aihezu.dev # 企业用户自定义域名');
|
|
23
23
|
console.log(' npx aihezu install --provider codex # 直接配置 Codex');
|
|
24
|
+
console.log(' npx aihezu install --provider codex --api your-org.aihezu.dev # Codex 企业域名');
|
|
24
25
|
console.log(' npx aihezu ccclear # 清理缓存');
|
|
25
26
|
}
|
|
26
27
|
|
package/bin/ccinstall.js
CHANGED
|
@@ -61,7 +61,7 @@ function parseApiBaseInput(argv) {
|
|
|
61
61
|
return apiBaseInput.trim();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function
|
|
64
|
+
function normalizeClaudeApiBaseUrl(input) {
|
|
65
65
|
if (!input) return DEFAULT_CLAUDE_API_BASE;
|
|
66
66
|
|
|
67
67
|
let normalized = input;
|
|
@@ -89,6 +89,34 @@ function normalizeApiBaseUrl(input) {
|
|
|
89
89
|
return `${url.origin}${url.pathname}`;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
function normalizeCodexBaseUrl(input) {
|
|
93
|
+
if (!input) return DEFAULT_CODEX_BASE_URL;
|
|
94
|
+
|
|
95
|
+
let normalized = input;
|
|
96
|
+
|
|
97
|
+
if (!/^https?:\/\//i.test(normalized)) {
|
|
98
|
+
normalized = `https://${normalized}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let url;
|
|
102
|
+
try {
|
|
103
|
+
url = new URL(normalized);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw new Error('无效的域名或 URL,请输入类似 your-org.aihezu.dev 或 https://your-org.aihezu.dev');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let pathname = url.pathname.replace(/\/+$/, '');
|
|
109
|
+
if (!pathname || pathname === '/') {
|
|
110
|
+
pathname = '/openai';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
url.pathname = pathname;
|
|
114
|
+
url.search = '';
|
|
115
|
+
url.hash = '';
|
|
116
|
+
|
|
117
|
+
return `${url.origin}${url.pathname}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
92
120
|
function resolveProvider(input) {
|
|
93
121
|
const value = (input || '').toLowerCase();
|
|
94
122
|
if (['2', 'codex', 'c'].includes(value)) return PROVIDERS.CODEX;
|
|
@@ -144,32 +172,86 @@ function writeClaudeSettings(apiKey, apiBaseUrl) {
|
|
|
144
172
|
console.log(' ANTHROPIC_BASE_URL:', apiBaseUrl);
|
|
145
173
|
}
|
|
146
174
|
|
|
147
|
-
function writeCodexConfig(apiKey) {
|
|
175
|
+
function writeCodexConfig(apiKey, codexBaseUrl) {
|
|
148
176
|
if (!fs.existsSync(codexDir)) {
|
|
149
177
|
console.log('📁 创建 ~/.codex 目录...');
|
|
150
178
|
fs.mkdirSync(codexDir, { recursive: true });
|
|
151
179
|
}
|
|
152
180
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
181
|
+
let configContent = '';
|
|
182
|
+
let existingConfig = '';
|
|
183
|
+
let providerName = 'aihezu'; // 默认 provider 名称
|
|
184
|
+
|
|
185
|
+
// 读取现有配置
|
|
186
|
+
if (fs.existsSync(codexConfigPath)) {
|
|
187
|
+
console.log('📖 读取现有 Codex 配置文件...');
|
|
188
|
+
existingConfig = fs.readFileSync(codexConfigPath, 'utf8');
|
|
189
|
+
|
|
190
|
+
// 提取现有的 provider 名称
|
|
191
|
+
const providerMatch = existingConfig.match(/model_provider\s*=\s*"([^"]+)"/);
|
|
192
|
+
if (providerMatch) {
|
|
193
|
+
providerName = providerMatch[1];
|
|
194
|
+
console.log(`ℹ️ 检测到现有 provider: ${providerName}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 备份原配置
|
|
198
|
+
const configBackup = backupFile(codexConfigPath);
|
|
199
|
+
if (configBackup) {
|
|
200
|
+
console.log(`📦 已备份原配置文件到: ${path.basename(configBackup)}`);
|
|
201
|
+
}
|
|
157
202
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
203
|
+
// 更新 base_url
|
|
204
|
+
const baseUrlPattern = new RegExp(
|
|
205
|
+
`(\\[model_providers\\.${providerName}\\][\\s\\S]*?base_url\\s*=\\s*)"[^"]*"`,
|
|
206
|
+
'm'
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (baseUrlPattern.test(existingConfig)) {
|
|
210
|
+
// 如果找到了 base_url,就替换它
|
|
211
|
+
configContent = existingConfig.replace(baseUrlPattern, `$1"${codexBaseUrl}"`);
|
|
212
|
+
console.log('✏️ 已更新现有配置中的 base_url');
|
|
213
|
+
} else {
|
|
214
|
+
// 如果没有找到完整的 provider 配置,尝试添加或创建
|
|
215
|
+
const providerSectionPattern = new RegExp(`\\[model_providers\\.${providerName}\\]`, 'm');
|
|
216
|
+
|
|
217
|
+
if (providerSectionPattern.test(existingConfig)) {
|
|
218
|
+
// provider section 存在但没有 base_url,添加 base_url
|
|
219
|
+
configContent = existingConfig.replace(
|
|
220
|
+
providerSectionPattern,
|
|
221
|
+
`[model_providers.${providerName}]\nbase_url = "${codexBaseUrl}"`
|
|
222
|
+
);
|
|
223
|
+
console.log('✏️ 已在现有 provider 配置中添加 base_url');
|
|
224
|
+
} else {
|
|
225
|
+
// 完全没有这个 provider,保留现有配置并追加新的
|
|
226
|
+
configContent = existingConfig.trim() + '\n\n' +
|
|
227
|
+
`[model_providers.${providerName}]\n` +
|
|
228
|
+
`name = "${providerName}"\n` +
|
|
229
|
+
`base_url = "${codexBaseUrl}"\n` +
|
|
230
|
+
`wire_api = "responses"\n` +
|
|
231
|
+
`requires_openai_auth = true\n` +
|
|
232
|
+
`env_key = "AIHEZU_OAI_KEY"\n`;
|
|
233
|
+
console.log('✏️ 已追加新的 provider 配置');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
// 文件不存在,创建新配置
|
|
238
|
+
console.log('📝 创建新的 Codex 配置文件...');
|
|
239
|
+
configContent = [
|
|
240
|
+
'model_provider = "aihezu"',
|
|
241
|
+
'model = "gpt-5-codex"',
|
|
242
|
+
'model_reasoning_effort = "high"',
|
|
243
|
+
'disable_response_storage = true',
|
|
244
|
+
'preferred_auth_method = "apikey"',
|
|
245
|
+
'',
|
|
246
|
+
'[model_providers.aihezu]',
|
|
247
|
+
'name = "aihezu"',
|
|
248
|
+
`base_url = "${codexBaseUrl}"`,
|
|
249
|
+
'wire_api = "responses"',
|
|
250
|
+
'requires_openai_auth = true',
|
|
251
|
+
'env_key = "AIHEZU_OAI_KEY"',
|
|
252
|
+
''
|
|
253
|
+
].join('\n');
|
|
254
|
+
}
|
|
173
255
|
|
|
174
256
|
fs.writeFileSync(codexConfigPath, configContent, 'utf8');
|
|
175
257
|
|
|
@@ -178,6 +260,11 @@ function writeCodexConfig(apiKey) {
|
|
|
178
260
|
try {
|
|
179
261
|
const content = fs.readFileSync(codexAuthPath, 'utf8');
|
|
180
262
|
authData = JSON.parse(content);
|
|
263
|
+
// 备份旧的 auth.json
|
|
264
|
+
const authBackup = backupFile(codexAuthPath);
|
|
265
|
+
if (authBackup) {
|
|
266
|
+
console.log(`📦 已备份原 auth.json 到: ${path.basename(authBackup)}`);
|
|
267
|
+
}
|
|
181
268
|
} catch (e) {
|
|
182
269
|
const backupPath = backupFile(codexAuthPath);
|
|
183
270
|
console.log('⚠️ 现有 auth.json 解析失败,已备份旧文件:', backupPath ? path.basename(backupPath) : '未备份');
|
|
@@ -185,7 +272,7 @@ function writeCodexConfig(apiKey) {
|
|
|
185
272
|
}
|
|
186
273
|
}
|
|
187
274
|
|
|
188
|
-
|
|
275
|
+
// 只设置 AIHEZU_OAI_KEY,不修改其他环境变量
|
|
189
276
|
authData.AIHEZU_OAI_KEY = apiKey;
|
|
190
277
|
|
|
191
278
|
fs.writeFileSync(codexAuthPath, JSON.stringify(authData, null, 2), 'utf8');
|
|
@@ -196,10 +283,13 @@ function writeCodexConfig(apiKey) {
|
|
|
196
283
|
console.log(' -', codexConfigPath);
|
|
197
284
|
console.log(' -', codexAuthPath);
|
|
198
285
|
console.log('\n配置内容:');
|
|
199
|
-
console.log(' AIHEZU_OAI_KEY
|
|
200
|
-
console.log('
|
|
201
|
-
console.log('
|
|
202
|
-
console.log('
|
|
286
|
+
console.log(' 环境变量名: AIHEZU_OAI_KEY');
|
|
287
|
+
console.log(' API 密钥:', apiKey);
|
|
288
|
+
console.log(' API 地址:', codexBaseUrl);
|
|
289
|
+
console.log('\n💡 Codex 会自动从 auth.json 中读取 AIHEZU_OAI_KEY');
|
|
290
|
+
console.log(' 如果遇到认证问题,请确保:');
|
|
291
|
+
console.log(' 1. auth.json 文件存在且格式正确');
|
|
292
|
+
console.log(' 2. 或者在终端中手动设置: export AIHEZU_OAI_KEY="' + apiKey + '"');
|
|
203
293
|
}
|
|
204
294
|
|
|
205
295
|
async function main() {
|
|
@@ -221,10 +311,20 @@ async function main() {
|
|
|
221
311
|
}
|
|
222
312
|
|
|
223
313
|
const apiBaseInput = provider === PROVIDERS.CLAUDE ? parseApiBaseInput(cliArgs) : '';
|
|
314
|
+
const codexBaseInput = provider === PROVIDERS.CODEX ? parseApiBaseInput(cliArgs) : '';
|
|
315
|
+
|
|
224
316
|
let apiBaseUrl = DEFAULT_CLAUDE_API_BASE;
|
|
317
|
+
let codexBaseUrl = DEFAULT_CODEX_BASE_URL;
|
|
225
318
|
if (provider === PROVIDERS.CLAUDE) {
|
|
226
319
|
try {
|
|
227
|
-
apiBaseUrl =
|
|
320
|
+
apiBaseUrl = normalizeClaudeApiBaseUrl(apiBaseInput);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error(`❌ ${error.message}`);
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
try {
|
|
327
|
+
codexBaseUrl = normalizeCodexBaseUrl(codexBaseInput);
|
|
228
328
|
} catch (error) {
|
|
229
329
|
console.error(`❌ ${error.message}`);
|
|
230
330
|
process.exit(1);
|
|
@@ -232,6 +332,7 @@ async function main() {
|
|
|
232
332
|
}
|
|
233
333
|
|
|
234
334
|
const usingCustomDomain = provider === PROVIDERS.CLAUDE && !!apiBaseInput;
|
|
335
|
+
const usingCustomCodexDomain = provider === PROVIDERS.CODEX && !!codexBaseInput;
|
|
235
336
|
|
|
236
337
|
if (provider === PROVIDERS.CLAUDE) {
|
|
237
338
|
if (usingCustomDomain) {
|
|
@@ -242,7 +343,13 @@ async function main() {
|
|
|
242
343
|
console.log(' sudo npx aihezu install --api your-org.aihezu.dev\n');
|
|
243
344
|
}
|
|
244
345
|
} else {
|
|
245
|
-
|
|
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
|
+
}
|
|
246
353
|
}
|
|
247
354
|
|
|
248
355
|
const needCleanAnswer = await askQuestion(
|
|
@@ -272,12 +379,14 @@ async function main() {
|
|
|
272
379
|
if (provider === PROVIDERS.CLAUDE) {
|
|
273
380
|
writeClaudeSettings(apiKey.trim(), apiBaseUrl);
|
|
274
381
|
} else {
|
|
275
|
-
writeCodexConfig(apiKey.trim());
|
|
382
|
+
writeCodexConfig(apiKey.trim(), codexBaseUrl);
|
|
276
383
|
}
|
|
277
384
|
|
|
278
385
|
if (provider === PROVIDERS.CLAUDE) {
|
|
279
386
|
console.log('\n=== 修改 hosts 文件 ===\n');
|
|
280
387
|
modifyHostsFile();
|
|
388
|
+
} else {
|
|
389
|
+
console.log('\n=== Codex 不需要修改 hosts,已跳过 ===');
|
|
281
390
|
}
|
|
282
391
|
|
|
283
392
|
console.log('\n=== 全部完成 ===');
|