aihezu 2.8.0 → 2.8.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 +1 -1
- package/bin/aihezu.js +1 -1
- package/commands/check.js +3 -3
- package/commands/config.js +23 -12
- package/commands/install.js +23 -12
- package/commands/usage.js +2 -2
- package/docs/TROUBLESHOOTING.md +1 -1
- package/package.json +1 -1
- package/services/codex.js +19 -70
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:默认 `https://cc.aihezu.dev/openai`,企业可用 `--api` / `--api-url` 指定独立域名;写入 `~/.codex/config.toml` 和 `auth.json`,使用 `
|
|
19
|
+
- Codex:默认 `https://cc.aihezu.dev/openai`,企业可用 `--api` / `--api-url` 指定独立域名;写入 `~/.codex/config.toml` 和 `auth.json`,使用 `OPENAI_API_KEY`
|
|
20
20
|
- `ccclear`:清理 Claude Code 缓存和配置
|
|
21
21
|
- `usage`:查看 Claude Code / Codex / Gemini 用量统计(支持 `--key` 指定 API Key)
|
|
22
22
|
|
package/bin/aihezu.js
CHANGED
|
@@ -52,7 +52,7 @@ function showHelp() {
|
|
|
52
52
|
console.log(' 环境变量: ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL');
|
|
53
53
|
console.log(' Codex:');
|
|
54
54
|
console.log(' 配置文件: ~/.codex/config.toml, ~/.codex/auth.json');
|
|
55
|
-
console.log(' 环境变量:
|
|
55
|
+
console.log(' 环境变量: OPENAI_API_KEY');
|
|
56
56
|
console.log(' Google Gemini:');
|
|
57
57
|
console.log(' 配置文件: ~/.gemini/.env, ~/.gemini/settings.json (可选)');
|
|
58
58
|
console.log(' 环境变量: GEMINI_API_KEY, GOOGLE_GEMINI_BASE_URL');
|
package/commands/check.js
CHANGED
|
@@ -113,7 +113,7 @@ function checkCodex() {
|
|
|
113
113
|
try {
|
|
114
114
|
const authData = JSON.parse(fs.readFileSync(authPath, 'utf8'));
|
|
115
115
|
console.log(' ✅ 文件存在');
|
|
116
|
-
console.log(' 📝
|
|
116
|
+
console.log(' 📝 OPENAI_API_KEY:', maskSensitive(authData.OPENAI_API_KEY));
|
|
117
117
|
} catch (e) {
|
|
118
118
|
console.log(' ❌ 文件存在但格式错误:', e.message);
|
|
119
119
|
}
|
|
@@ -123,8 +123,8 @@ function checkCodex() {
|
|
|
123
123
|
|
|
124
124
|
// Check environment variables
|
|
125
125
|
console.log('\n3️⃣ 环境变量:');
|
|
126
|
-
const envKey = process.env.
|
|
127
|
-
console.log(' 📝
|
|
126
|
+
const envKey = process.env.OPENAI_API_KEY;
|
|
127
|
+
console.log(' 📝 OPENAI_API_KEY:', maskSensitive(envKey));
|
|
128
128
|
|
|
129
129
|
// Priority explanation
|
|
130
130
|
console.log('\n4️⃣ 优先级说明:');
|
package/commands/config.js
CHANGED
|
@@ -53,24 +53,35 @@ async function configCommand(service, args = []) {
|
|
|
53
53
|
const apiUrlInput = await askQuestion(rl, '请输入 API 地址 (直接回车使用默认地址): ');
|
|
54
54
|
let apiUrl = apiUrlInput || defaultUrl;
|
|
55
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
56
|
// Basic URL validation/normalization
|
|
69
57
|
// This part should handle http/https prefixing.
|
|
70
58
|
if (!/^https?:\/\//i.test(apiUrl)) {
|
|
71
59
|
apiUrl = 'https://' + apiUrl;
|
|
72
60
|
}
|
|
73
61
|
|
|
62
|
+
// Append service-specific suffix only for aihezu.dev domains
|
|
63
|
+
const suffix = service.apiSuffix || '';
|
|
64
|
+
if (suffix) {
|
|
65
|
+
try {
|
|
66
|
+
const urlObj = new URL(apiUrl);
|
|
67
|
+
const hostname = urlObj.hostname;
|
|
68
|
+
|
|
69
|
+
// Only add suffix for aihezu.dev and its subdomains
|
|
70
|
+
if (hostname === 'aihezu.dev' || hostname.endsWith('.aihezu.dev')) {
|
|
71
|
+
const normalizedSuffix = suffix.startsWith('/') ? suffix : `/${suffix}`;
|
|
72
|
+
const trimmedUrl = apiUrl.replace(/\/+$/, '');
|
|
73
|
+
if (!trimmedUrl.endsWith(normalizedSuffix)) {
|
|
74
|
+
apiUrl = trimmedUrl + normalizedSuffix;
|
|
75
|
+
} else {
|
|
76
|
+
apiUrl = trimmedUrl;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch (e) {
|
|
80
|
+
// If URL parsing fails, keep the URL as-is
|
|
81
|
+
console.log('⚠️ URL 解析失败,保持原样');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
74
85
|
// 2. API Key
|
|
75
86
|
let defaultApiKey = cliApiKey || '';
|
|
76
87
|
let apiKeyPrompt = '请输入您的 API Key: ';
|
package/commands/install.js
CHANGED
|
@@ -54,24 +54,35 @@ async function installCommand(service, args = []) {
|
|
|
54
54
|
const apiUrlInput = await askQuestion(rl, '请输入 API 地址 (直接回车使用默认地址): ');
|
|
55
55
|
let apiUrl = apiUrlInput || defaultUrl;
|
|
56
56
|
|
|
57
|
-
// Append service-specific suffix if needed
|
|
58
|
-
const suffix = service.apiSuffix || '';
|
|
59
|
-
if (suffix) {
|
|
60
|
-
const normalizedSuffix = suffix.startsWith('/') ? suffix : `/${suffix}`;
|
|
61
|
-
const trimmedUrl = apiUrl.replace(/\/+$/, '');
|
|
62
|
-
if (!trimmedUrl.endsWith(normalizedSuffix)) {
|
|
63
|
-
apiUrl = trimmedUrl + normalizedSuffix;
|
|
64
|
-
} else {
|
|
65
|
-
apiUrl = trimmedUrl;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
57
|
// Basic URL validation/normalization
|
|
70
58
|
// This part should handle http/https prefixing.
|
|
71
59
|
if (!/^https?:\/\//i.test(apiUrl)) {
|
|
72
60
|
apiUrl = 'https://' + apiUrl;
|
|
73
61
|
}
|
|
74
62
|
|
|
63
|
+
// Append service-specific suffix only for aihezu.dev domains
|
|
64
|
+
const suffix = service.apiSuffix || '';
|
|
65
|
+
if (suffix) {
|
|
66
|
+
try {
|
|
67
|
+
const urlObj = new URL(apiUrl);
|
|
68
|
+
const hostname = urlObj.hostname;
|
|
69
|
+
|
|
70
|
+
// Only add suffix for aihezu.dev and its subdomains
|
|
71
|
+
if (hostname === 'aihezu.dev' || hostname.endsWith('.aihezu.dev')) {
|
|
72
|
+
const normalizedSuffix = suffix.startsWith('/') ? suffix : `/${suffix}`;
|
|
73
|
+
const trimmedUrl = apiUrl.replace(/\/+$/, '');
|
|
74
|
+
if (!trimmedUrl.endsWith(normalizedSuffix)) {
|
|
75
|
+
apiUrl = trimmedUrl + normalizedSuffix;
|
|
76
|
+
} else {
|
|
77
|
+
apiUrl = trimmedUrl;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
// If URL parsing fails, keep the URL as-is
|
|
82
|
+
console.log('⚠️ URL 解析失败,保持原样');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
75
86
|
// 2. API Key
|
|
76
87
|
let defaultApiKey = cliApiKey || '';
|
|
77
88
|
let apiKeyPrompt = '请输入您的 API Key: ';
|
package/commands/usage.js
CHANGED
|
@@ -133,7 +133,7 @@ function readCodexConfig() {
|
|
|
133
133
|
const configs = [];
|
|
134
134
|
|
|
135
135
|
// 读取环境变量
|
|
136
|
-
const envAuthToken = process.env.
|
|
136
|
+
const envAuthToken = process.env.OPENAI_API_KEY || '';
|
|
137
137
|
|
|
138
138
|
// 读取配置文件中的 base_url
|
|
139
139
|
let fileBaseUrl = '';
|
|
@@ -159,7 +159,7 @@ function readCodexConfig() {
|
|
|
159
159
|
try {
|
|
160
160
|
const content = fs.readFileSync(authPath, 'utf8');
|
|
161
161
|
const authData = JSON.parse(content);
|
|
162
|
-
fileAuthToken = authData.
|
|
162
|
+
fileAuthToken = authData.OPENAI_API_KEY || '';
|
|
163
163
|
} catch (error) {
|
|
164
164
|
// 忽略错误
|
|
165
165
|
}
|
package/docs/TROUBLESHOOTING.md
CHANGED
package/package.json
CHANGED
package/services/codex.js
CHANGED
|
@@ -32,75 +32,22 @@ module.exports = {
|
|
|
32
32
|
fs.mkdirSync(configDir, { recursive: true });
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
// Handle config.toml
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Update model
|
|
52
|
-
const modelPattern = /^model\s*=\s*"[^"]*"/m;
|
|
53
|
-
if (modelPattern.test(newConfig)) {
|
|
54
|
-
newConfig = newConfig.replace(modelPattern, `model = "${modelName}"`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Update base_url
|
|
58
|
-
const baseUrlPattern = new RegExp(
|
|
59
|
-
`(\[model_providers\.${providerName}\][\s\S]*?base_url\s*=\s*)"[^"]*"`,
|
|
60
|
-
'm'
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
if (baseUrlPattern.test(newConfig)) {
|
|
64
|
-
newConfig = newConfig.replace(baseUrlPattern, `$1"${apiUrl}"`);
|
|
65
|
-
} else {
|
|
66
|
-
// If provider block exists but no base_url (rare), or provider block missing
|
|
67
|
-
const providerSectionPattern = new RegExp(`\[model_providers\.${providerName}\]`, 'm');
|
|
68
|
-
if (providerSectionPattern.test(newConfig)) {
|
|
69
|
-
newConfig = newConfig.replace(
|
|
70
|
-
providerSectionPattern,
|
|
71
|
-
`[model_providers.${providerName}]\nbase_url = "${apiUrl}"`
|
|
72
|
-
);
|
|
73
|
-
} else {
|
|
74
|
-
// Append new provider
|
|
75
|
-
newConfig = newConfig.trim() + '\n\n' +
|
|
76
|
-
`[model_providers.${providerName}]\n` +
|
|
77
|
-
`name = "${providerName}"\n` +
|
|
78
|
-
`base_url = "${apiUrl}"\n` +
|
|
79
|
-
`wire_api = "responses"\n` +
|
|
80
|
-
`requires_openai_auth = true\n` +
|
|
81
|
-
`env_key = "AIHEZU_OAI_KEY"\n`;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
configContent = newConfig;
|
|
85
|
-
|
|
86
|
-
} else {
|
|
87
|
-
// New Config
|
|
88
|
-
configContent = [
|
|
89
|
-
'model_provider = "aihezu"',
|
|
90
|
-
`model = "${modelName}"`,
|
|
91
|
-
'model_reasoning_effort = "high"',
|
|
92
|
-
'disable_response_storage = true',
|
|
93
|
-
'preferred_auth_method = "apikey"',
|
|
94
|
-
'',
|
|
95
|
-
'[model_providers.aihezu]',
|
|
96
|
-
'name = "aihezu"',
|
|
97
|
-
`base_url = "${apiUrl}"`,
|
|
98
|
-
'wire_api = "responses"',
|
|
99
|
-
'requires_openai_auth = true',
|
|
100
|
-
'env_key = "AIHEZU_OAI_KEY"',
|
|
101
|
-
''
|
|
102
|
-
].join('\n');
|
|
103
|
-
}
|
|
35
|
+
// Handle config.toml - always regenerate to avoid duplication issues
|
|
36
|
+
const providerName = 'aihezu';
|
|
37
|
+
const configContent = [
|
|
38
|
+
`model_provider = "${providerName}"`,
|
|
39
|
+
`model = "${modelName}"`,
|
|
40
|
+
'model_reasoning_effort = "high"',
|
|
41
|
+
'disable_response_storage = true',
|
|
42
|
+
'preferred_auth_method = "apikey"',
|
|
43
|
+
'',
|
|
44
|
+
`[model_providers.${providerName}]`,
|
|
45
|
+
`name = "${providerName}"`,
|
|
46
|
+
`base_url = "${apiUrl}"`,
|
|
47
|
+
'wire_api = "responses"',
|
|
48
|
+
'requires_openai_auth = true',
|
|
49
|
+
''
|
|
50
|
+
].join('\n');
|
|
104
51
|
|
|
105
52
|
fs.writeFileSync(configPath, configContent, 'utf8');
|
|
106
53
|
|
|
@@ -113,7 +60,9 @@ module.exports = {
|
|
|
113
60
|
authData = {};
|
|
114
61
|
}
|
|
115
62
|
}
|
|
116
|
-
|
|
63
|
+
// Remove legacy key if present
|
|
64
|
+
delete authData.AIHEZU_OAI_KEY;
|
|
65
|
+
authData.OPENAI_API_KEY = apiKey;
|
|
117
66
|
fs.writeFileSync(authPath, JSON.stringify(authData, null, 2), 'utf8');
|
|
118
67
|
|
|
119
68
|
// Fix permissions if running with sudo
|