aihezu 1.6.0 → 1.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/README.md +13 -7
- package/bin/aihezu.js +5 -4
- package/bin/ccinstall.js +206 -83
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# aihezu - Claude Code CLI 工具集
|
|
2
2
|
|
|
3
|
-
快速配置或清理 Claude Code
|
|
3
|
+
快速配置或清理 Claude Code / Codex,本地修改 hosts 并提供企业独立域名支持。
|
|
4
4
|
|
|
5
5
|
## 功能特性
|
|
6
6
|
|
|
7
|
-
- 🔑 一键配置 Claude
|
|
7
|
+
- 🔑 一键配置 Claude(默认)或 Codex,支持企业独立域名(`--api` / `--api-url`)
|
|
8
8
|
- 🧹 一键清理 Claude Code CLI 的所有本地数据
|
|
9
9
|
- 🌐 自动修改 hosts 文件,将 Anthropic 域名指向本地
|
|
10
10
|
- 📦 自动备份配置文件和 hosts 文件(带时间戳)
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
## 命令列表
|
|
16
16
|
|
|
17
|
-
- `ccinstall` / `install
|
|
18
|
-
-
|
|
19
|
-
-
|
|
17
|
+
- `ccinstall` / `install`:交互式配置 Claude(默认)或 Codex
|
|
18
|
+
- Claude:默认 `https://cc.aihezu.dev/api`,企业可用 `--api` / `--api-url` 指定独立域名
|
|
19
|
+
- Codex:写入 `~/.codex/config.toml` 和 `auth.json`,使用 `AIHEZU_OAI_KEY`
|
|
20
20
|
- `ccclear`:清理 Claude Code 缓存和配置
|
|
21
21
|
|
|
22
22
|
## 清理内容
|
|
@@ -49,9 +49,9 @@ sudo npx aihezu install
|
|
|
49
49
|
- 企业独立域名(支持传入域名或完整 URL,自动补全为 `/api`):
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
sudo npx aihezu install --api https://
|
|
52
|
+
sudo npx aihezu install --api https://your-org.aihezu.dev
|
|
53
53
|
# 或
|
|
54
|
-
sudo npx aihezu install --api
|
|
54
|
+
sudo npx aihezu install --api your-org.aihezu.dev
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
也可以全局安装后运行:
|
|
@@ -61,6 +61,12 @@ npm install -g aihezu
|
|
|
61
61
|
sudo ccinstall --api your-company.aihezu.dev
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
- 直接选择 Codex(可在交互中选择,也可用参数指定):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
sudo npx aihezu install --provider codex
|
|
68
|
+
```
|
|
69
|
+
|
|
64
70
|
### 清理 Claude Code(ccclear)
|
|
65
71
|
|
|
66
72
|
- 使用 npx(推荐):
|
package/bin/aihezu.js
CHANGED
|
@@ -9,17 +9,18 @@ const commandArgs = args.slice(1);
|
|
|
9
9
|
|
|
10
10
|
// 显示帮助信息
|
|
11
11
|
function showHelp() {
|
|
12
|
-
console.log('🌐 aihezu - Claude
|
|
12
|
+
console.log('🌐 aihezu - Claude / Codex 工具集');
|
|
13
13
|
console.log('Powered by https://aihezu.dev\n');
|
|
14
14
|
console.log('使用方法:');
|
|
15
15
|
console.log(' npx aihezu <command>\n');
|
|
16
16
|
console.log('可用命令:');
|
|
17
|
-
console.log(' ccinstall / install 配置 Claude
|
|
17
|
+
console.log(' ccinstall / install 配置 Claude (默认) 或 Codex,支持企业自定义域名');
|
|
18
18
|
console.log(' ccclear 清理 Claude Code 缓存和配置');
|
|
19
19
|
console.log(' help 显示帮助信息\n');
|
|
20
20
|
console.log('示例:');
|
|
21
|
-
console.log(' npx aihezu ccinstall # 配置 API Key');
|
|
22
|
-
console.log(' npx aihezu install --api
|
|
21
|
+
console.log(' npx aihezu ccinstall # 配置 API Key (默认 Claude)');
|
|
22
|
+
console.log(' npx aihezu install --api your-org.aihezu.dev # 企业用户自定义域名');
|
|
23
|
+
console.log(' npx aihezu install --provider codex # 直接配置 Codex');
|
|
23
24
|
console.log(' npx aihezu ccclear # 清理缓存');
|
|
24
25
|
}
|
|
25
26
|
|
package/bin/ccinstall.js
CHANGED
|
@@ -8,11 +8,39 @@ const { modifyHostsFile } = require('../lib/hosts');
|
|
|
8
8
|
const { cleanCache } = require('../lib/cache');
|
|
9
9
|
|
|
10
10
|
const homeDir = os.homedir();
|
|
11
|
-
const
|
|
11
|
+
const claudeSettingsPath = path.join(homeDir, '.claude', 'settings.json');
|
|
12
12
|
const claudeDir = path.join(homeDir, '.claude');
|
|
13
|
-
const
|
|
13
|
+
const codexDir = path.join(homeDir, '.codex');
|
|
14
|
+
const codexConfigPath = path.join(codexDir, 'config.toml');
|
|
15
|
+
const codexAuthPath = path.join(codexDir, 'auth.json');
|
|
16
|
+
const DEFAULT_CLAUDE_API_BASE = 'https://cc.aihezu.dev/api';
|
|
17
|
+
const DEFAULT_CODEX_BASE_URL = 'https://cc.aihezu.dev/openai';
|
|
18
|
+
const PROVIDERS = {
|
|
19
|
+
CLAUDE: 'claude',
|
|
20
|
+
CODEX: 'codex'
|
|
21
|
+
};
|
|
14
22
|
const cliArgs = process.argv.slice(2);
|
|
15
23
|
|
|
24
|
+
function askQuestion(rl, question) {
|
|
25
|
+
return new Promise(resolve => rl.question(question, answer => resolve(answer.trim())));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getArgValue(argv, keys) {
|
|
29
|
+
for (let i = 0; i < argv.length; i++) {
|
|
30
|
+
const arg = argv[i];
|
|
31
|
+
const [key, inlineValue] = arg.split('=');
|
|
32
|
+
if (keys.includes(key)) {
|
|
33
|
+
return (inlineValue || argv[i + 1] || '').trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseProviderArg(argv) {
|
|
40
|
+
const value = getArgValue(argv, ['--provider', '--product', '--service', '--mode']);
|
|
41
|
+
return value.toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
|
|
16
44
|
function parseApiBaseInput(argv) {
|
|
17
45
|
let apiBaseInput = '';
|
|
18
46
|
|
|
@@ -34,7 +62,7 @@ function parseApiBaseInput(argv) {
|
|
|
34
62
|
}
|
|
35
63
|
|
|
36
64
|
function normalizeApiBaseUrl(input) {
|
|
37
|
-
if (!input) return
|
|
65
|
+
if (!input) return DEFAULT_CLAUDE_API_BASE;
|
|
38
66
|
|
|
39
67
|
let normalized = input;
|
|
40
68
|
|
|
@@ -46,7 +74,7 @@ function normalizeApiBaseUrl(input) {
|
|
|
46
74
|
try {
|
|
47
75
|
url = new URL(normalized);
|
|
48
76
|
} catch (error) {
|
|
49
|
-
throw new Error('无效的域名或 URL,请输入类似
|
|
77
|
+
throw new Error('无效的域名或 URL,请输入类似 your-org.aihezu.dev 或 https://your-org.aihezu.dev');
|
|
50
78
|
}
|
|
51
79
|
|
|
52
80
|
let pathname = url.pathname.replace(/\/+$/, '');
|
|
@@ -61,115 +89,210 @@ function normalizeApiBaseUrl(input) {
|
|
|
61
89
|
return `${url.origin}${url.pathname}`;
|
|
62
90
|
}
|
|
63
91
|
|
|
64
|
-
|
|
65
|
-
|
|
92
|
+
function resolveProvider(input) {
|
|
93
|
+
const value = (input || '').toLowerCase();
|
|
94
|
+
if (['2', 'codex', 'c'].includes(value)) return PROVIDERS.CODEX;
|
|
95
|
+
return PROVIDERS.CLAUDE;
|
|
96
|
+
}
|
|
66
97
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
function backupFile(filePath) {
|
|
99
|
+
if (!fs.existsSync(filePath)) return null;
|
|
100
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
101
|
+
const backupPath = `${filePath}.backup-${timestamp}`;
|
|
102
|
+
fs.copyFileSync(filePath, backupPath);
|
|
103
|
+
return backupPath;
|
|
72
104
|
}
|
|
73
105
|
|
|
74
|
-
|
|
106
|
+
function writeClaudeSettings(apiKey, apiBaseUrl) {
|
|
107
|
+
// 确保 .claude 目录存在
|
|
108
|
+
if (!fs.existsSync(claudeDir)) {
|
|
109
|
+
console.log('📁 创建 ~/.claude 目录...');
|
|
110
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
111
|
+
}
|
|
75
112
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
113
|
+
// 读取或创建 settings.json
|
|
114
|
+
let settings = {};
|
|
115
|
+
if (fs.existsSync(claudeSettingsPath)) {
|
|
116
|
+
console.log('📖 读取现有配置文件...');
|
|
117
|
+
const content = fs.readFileSync(claudeSettingsPath, 'utf8');
|
|
118
|
+
try {
|
|
119
|
+
settings = JSON.parse(content);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.log('⚠️ 现有配置文件格式错误,将创建新的配置');
|
|
122
|
+
// 备份错误的配置文件
|
|
123
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
124
|
+
const backupPath = `${claudeSettingsPath}.backup-${timestamp}`;
|
|
125
|
+
fs.writeFileSync(backupPath, content);
|
|
126
|
+
console.log(`📦 已备份原配置文件到: ${path.basename(backupPath)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
85
129
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
130
|
+
// 完全替换 env 对象,清除所有旧的环境变量配置
|
|
131
|
+
settings.env = {
|
|
132
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
133
|
+
ANTHROPIC_BASE_URL: apiBaseUrl
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// 写入配置文件
|
|
137
|
+
fs.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
138
|
+
|
|
139
|
+
console.log('\n=== API 配置 (Claude) ===\n');
|
|
140
|
+
console.log('✅ 配置成功!');
|
|
141
|
+
console.log('📝 已更新配置文件:', claudeSettingsPath);
|
|
142
|
+
console.log('\n配置内容:');
|
|
143
|
+
console.log(' ANTHROPIC_AUTH_TOKEN:', apiKey);
|
|
144
|
+
console.log(' ANTHROPIC_BASE_URL:', apiBaseUrl);
|
|
145
|
+
}
|
|
91
146
|
|
|
147
|
+
function writeCodexConfig(apiKey) {
|
|
148
|
+
if (!fs.existsSync(codexDir)) {
|
|
149
|
+
console.log('📁 创建 ~/.codex 目录...');
|
|
150
|
+
fs.mkdirSync(codexDir, { recursive: true });
|
|
151
|
+
}
|
|
92
152
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
153
|
+
const configBackup = backupFile(codexConfigPath);
|
|
154
|
+
if (configBackup) {
|
|
155
|
+
console.log(`📦 已备份原配置文件到: ${path.basename(configBackup)}`);
|
|
156
|
+
}
|
|
96
157
|
|
|
97
|
-
|
|
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');
|
|
173
|
+
|
|
174
|
+
fs.writeFileSync(codexConfigPath, configContent, 'utf8');
|
|
175
|
+
|
|
176
|
+
let authData = {};
|
|
177
|
+
if (fs.existsSync(codexAuthPath)) {
|
|
98
178
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
console.log('继续配置流程...\n');
|
|
179
|
+
const content = fs.readFileSync(codexAuthPath, 'utf8');
|
|
180
|
+
authData = JSON.parse(content);
|
|
181
|
+
} catch (e) {
|
|
182
|
+
const backupPath = backupFile(codexAuthPath);
|
|
183
|
+
console.log('⚠️ 现有 auth.json 解析失败,已备份旧文件:', backupPath ? path.basename(backupPath) : '未备份');
|
|
184
|
+
authData = {};
|
|
106
185
|
}
|
|
107
186
|
}
|
|
108
187
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
188
|
+
authData.OPENAI_API_KEY = null;
|
|
189
|
+
authData.AIHEZU_OAI_KEY = apiKey;
|
|
190
|
+
|
|
191
|
+
fs.writeFileSync(codexAuthPath, JSON.stringify(authData, null, 2), 'utf8');
|
|
192
|
+
|
|
193
|
+
console.log('\n=== API 配置 (Codex) ===\n');
|
|
194
|
+
console.log('✅ 配置成功!');
|
|
195
|
+
console.log('📝 已更新配置文件:');
|
|
196
|
+
console.log(' -', codexConfigPath);
|
|
197
|
+
console.log(' -', codexAuthPath);
|
|
198
|
+
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="<你的密钥>"');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function main() {
|
|
206
|
+
console.log('🔧 Claude / Codex API 配置工具');
|
|
207
|
+
console.log('🌐 Powered by https://aihezu.dev\n');
|
|
116
208
|
|
|
117
|
-
|
|
209
|
+
const rl = readline.createInterface({
|
|
210
|
+
input: process.stdin,
|
|
211
|
+
output: process.stdout
|
|
212
|
+
});
|
|
118
213
|
|
|
119
214
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
215
|
+
const cliProviderInput = parseProviderArg(cliArgs);
|
|
216
|
+
let provider = resolveProvider(cliProviderInput);
|
|
217
|
+
|
|
218
|
+
if (!cliProviderInput) {
|
|
219
|
+
const providerAnswer = await askQuestion(rl, '请选择服务类型 [1] Claude (默认) / [2] Codex: ');
|
|
220
|
+
provider = resolveProvider(providerAnswer);
|
|
124
221
|
}
|
|
125
222
|
|
|
126
|
-
|
|
127
|
-
let
|
|
128
|
-
if (
|
|
129
|
-
console.log('📖 读取现有配置文件...');
|
|
130
|
-
const content = fs.readFileSync(settingsPath, 'utf8');
|
|
223
|
+
const apiBaseInput = provider === PROVIDERS.CLAUDE ? parseApiBaseInput(cliArgs) : '';
|
|
224
|
+
let apiBaseUrl = DEFAULT_CLAUDE_API_BASE;
|
|
225
|
+
if (provider === PROVIDERS.CLAUDE) {
|
|
131
226
|
try {
|
|
132
|
-
|
|
133
|
-
} catch (
|
|
134
|
-
console.
|
|
135
|
-
|
|
136
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
137
|
-
const backupPath = `${settingsPath}.backup-${timestamp}`;
|
|
138
|
-
fs.writeFileSync(backupPath, content);
|
|
139
|
-
console.log(`📦 已备份原配置文件到: ${path.basename(backupPath)}`);
|
|
227
|
+
apiBaseUrl = normalizeApiBaseUrl(apiBaseInput);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error(`❌ ${error.message}`);
|
|
230
|
+
process.exit(1);
|
|
140
231
|
}
|
|
141
232
|
}
|
|
142
233
|
|
|
143
|
-
|
|
144
|
-
// 这样可以避免旧配置(如 ANTHROPIC_MODEL 等)干扰新配置
|
|
145
|
-
settings.env = {
|
|
146
|
-
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
147
|
-
ANTHROPIC_BASE_URL: apiBaseUrl
|
|
148
|
-
};
|
|
234
|
+
const usingCustomDomain = provider === PROVIDERS.CLAUDE && !!apiBaseInput;
|
|
149
235
|
|
|
150
|
-
|
|
151
|
-
|
|
236
|
+
if (provider === PROVIDERS.CLAUDE) {
|
|
237
|
+
if (usingCustomDomain) {
|
|
238
|
+
console.log(`🏢 已启用企业独立域名: ${apiBaseUrl}\n`);
|
|
239
|
+
} else {
|
|
240
|
+
console.log(`ℹ️ 未指定域名,将使用默认地址: ${DEFAULT_CLAUDE_API_BASE}`);
|
|
241
|
+
console.log(' 企业用户可使用 --api 或 --api-url 指定独立域名,例如:');
|
|
242
|
+
console.log(' sudo npx aihezu install --api your-org.aihezu.dev\n');
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
console.log(`🤖 已选择 Codex,默认网关: ${DEFAULT_CODEX_BASE_URL}\n`);
|
|
246
|
+
}
|
|
152
247
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
console.log(' ANTHROPIC_BASE_URL:', apiBaseUrl);
|
|
248
|
+
const needCleanAnswer = await askQuestion(
|
|
249
|
+
rl,
|
|
250
|
+
'您是从其他服务切换过来的吗?(y/n,如果是首次使用请输入 n): '
|
|
251
|
+
);
|
|
252
|
+
const needClean = ['y', 'yes'].includes(needCleanAnswer.toLowerCase());
|
|
159
253
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
254
|
+
if (needClean) {
|
|
255
|
+
try {
|
|
256
|
+
console.log('\n=== 清理旧缓存 ===');
|
|
257
|
+
const count = cleanCache({ showHeader: false });
|
|
258
|
+
console.log(`\n✅ 缓存清理完成!(共处理 ${count} 项)`);
|
|
259
|
+
console.log('💡 配置文件已保留,即将配置新的 API Key\n');
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error('⚠️ 清理缓存时出错:', error.message);
|
|
262
|
+
console.log('继续配置流程...\n');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const apiKey = await askQuestion(rl, '请输入您的 API Key: ');
|
|
267
|
+
if (!apiKey) {
|
|
268
|
+
console.error('❌ API Key 不能为空');
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (provider === PROVIDERS.CLAUDE) {
|
|
273
|
+
writeClaudeSettings(apiKey.trim(), apiBaseUrl);
|
|
274
|
+
} else {
|
|
275
|
+
writeCodexConfig(apiKey.trim());
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (provider === PROVIDERS.CLAUDE) {
|
|
279
|
+
console.log('\n=== 修改 hosts 文件 ===\n');
|
|
280
|
+
modifyHostsFile();
|
|
281
|
+
}
|
|
163
282
|
|
|
164
283
|
console.log('\n=== 全部完成 ===');
|
|
165
|
-
|
|
284
|
+
if (provider === PROVIDERS.CLAUDE) {
|
|
285
|
+
console.log('💡 您现在可以开始使用 Claude Code 了!');
|
|
286
|
+
} else {
|
|
287
|
+
console.log('💡 您现在可以开始使用 Codex 了!');
|
|
288
|
+
}
|
|
166
289
|
console.log('🌐 更多服务请访问: https://aihezu.dev');
|
|
167
|
-
|
|
168
290
|
} catch (error) {
|
|
169
291
|
console.error('❌ 配置失败:', error.message);
|
|
170
292
|
process.exit(1);
|
|
171
293
|
} finally {
|
|
172
294
|
rl.close();
|
|
173
295
|
}
|
|
174
|
-
|
|
175
|
-
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
main();
|