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 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:写入 `~/.codex/config.toml` 和 `auth.json`,使用 `AIHEZU_OAI_KEY`
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 normalizeApiBaseUrl(input) {
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
- const configBackup = backupFile(codexConfigPath);
154
- if (configBackup) {
155
- console.log(`📦 已备份原配置文件到: ${path.basename(configBackup)}`);
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
- const configContent = [
159
- 'model_provider = "aihezu"',
160
- 'model = "gpt-5-codex"',
161
- 'model_reasoning_effort = "high"',
162
- 'disable_response_storage = true',
163
- 'preferred_auth_method = "apikey"',
164
- '',
165
- '[model_providers.aihezu]',
166
- 'name = "aihezu"',
167
- `base_url = "${DEFAULT_CODEX_BASE_URL}"`,
168
- 'wire_api = "responses"',
169
- 'requires_openai_auth = true',
170
- 'env_key = "AIHEZU_OAI_KEY"',
171
- ''
172
- ].join('\n');
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
- authData.OPENAI_API_KEY = null;
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:', apiKey);
200
- console.log(' base_url:', DEFAULT_CODEX_BASE_URL);
201
- console.log('\n💡 已在 auth.json 中写入密钥,并在 config.toml 中指定 env_key = "AIHEZU_OAI_KEY"');
202
- console.log(' 如需在 shell 中直接使用,可运行: export AIHEZU_OAI_KEY="<你的密钥>"');
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 = normalizeApiBaseUrl(apiBaseInput);
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
- console.log(`🤖 已选择 Codex,默认网关: ${DEFAULT_CODEX_BASE_URL}\n`);
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=== 全部完成 ===');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aihezu",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Claude Code CLI 清理工具 - 快速备份和清理 Claude Code 的本地配置和缓存,同时修改 hosts 文件实现本地代理",
5
5
  "main": "bin/ccclear.js",
6
6
  "bin": {