pumpkinai-config 1.0.21 → 1.3.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/claude-api.js CHANGED
@@ -49,16 +49,6 @@ function getUserInput(question) {
49
49
  });
50
50
  }
51
51
 
52
- // 检查 @anthropic-ai/claude-code 是否已安装
53
- function checkClaudeInstalled() {
54
- try {
55
- const output = execSync('npm list -g @anthropic-ai/claude-code --depth=0', { encoding: 'utf8' });
56
- return output.includes('@anthropic-ai/claude-code');
57
- } catch (error) {
58
- return false;
59
- }
60
- }
61
-
62
52
  // 获取 @anthropic-ai/claude-code 版本号
63
53
  function getClaudeVersion() {
64
54
  try {
@@ -75,15 +65,7 @@ function installClaude() {
75
65
  const osType = getOS();
76
66
  let command;
77
67
 
78
- log('\n[检查] 检查 @anthropic-ai/claude-code 安装状态...', 'cyan');
79
-
80
- // 先检查是否已安装
81
- if (checkClaudeInstalled()) {
82
- log('[成功] @anthropic-ai/claude-code 已安装,跳过安装步骤', 'green');
83
- return true;
84
- }
85
-
86
- log('[安装] 开始安装 @anthropic-ai/claude-code...', 'cyan');
68
+ log('\n[安装] 开始安装 @anthropic-ai/claude-code...', 'cyan');
87
69
 
88
70
  if (osType === 'windows') {
89
71
  command = 'npm install -g @anthropic-ai/claude-code@2.0.59 --registry https://registry.npmmirror.com';
package/claude-fix.js CHANGED
File without changes
@@ -0,0 +1,372 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Claude Code 网络修复工具
5
+ * 检测当前域名并允许切换到备用域名
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const { execSync } = require('child_process');
12
+
13
+ // 颜色输出
14
+ const colors = {
15
+ red: '\x1b[31m',
16
+ green: '\x1b[32m',
17
+ yellow: '\x1b[33m',
18
+ blue: '\x1b[34m',
19
+ magenta: '\x1b[35m',
20
+ cyan: '\x1b[36m',
21
+ reset: '\x1b[0m',
22
+ dim: '\x1b[2m',
23
+ bright: '\x1b[1m'
24
+ };
25
+
26
+ function log(message, color = 'reset') {
27
+ console.log(`${colors[color]}${message}${colors.reset}`);
28
+ }
29
+
30
+ // 可用域名列表
31
+ const domainOptions = [
32
+ { name: '主域名', url: 'https://code.ppchat.vip' },
33
+ { name: '备用域名1', url: 'https://code2.ppchat.vip' },
34
+ { name: '备用域名2', url: 'https://code.pumpkinai.vip' },
35
+ { name: '清理DNS缓存-推荐', url: 'dns_flush' }
36
+ ];
37
+
38
+ // 检测操作系统
39
+ function getOS() {
40
+ const platform = os.platform();
41
+ if (platform === 'win32') return 'windows';
42
+ if (platform === 'darwin') return 'mac';
43
+ return 'linux';
44
+ }
45
+
46
+ // 获取用户目录
47
+ function getHomeDir() {
48
+ if (process.platform !== 'win32' && process.env.SUDO_USER) {
49
+ const actualUser = process.env.SUDO_USER;
50
+ return process.platform === 'darwin' ? `/Users/${actualUser}` : `/home/${actualUser}`;
51
+ }
52
+ return os.homedir();
53
+ }
54
+
55
+ // 获取当前使用的域名
56
+ function getCurrentDomain() {
57
+ const homeDir = getHomeDir();
58
+ const settingsPath = path.join(homeDir, '.claude', 'settings.json');
59
+
60
+ // 优先从 settings.json 读取
61
+ if (fs.existsSync(settingsPath)) {
62
+ try {
63
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
64
+ if (settings.env && settings.env.ANTHROPIC_BASE_URL) {
65
+ return settings.env.ANTHROPIC_BASE_URL;
66
+ }
67
+ } catch (error) {}
68
+ }
69
+
70
+ // 从环境变量读取
71
+ if (process.env.ANTHROPIC_BASE_URL) {
72
+ return process.env.ANTHROPIC_BASE_URL;
73
+ }
74
+
75
+ return null;
76
+ }
77
+
78
+ // 更新 settings.json 中的域名
79
+ function updateSettingsJson(newUrl) {
80
+ const homeDir = getHomeDir();
81
+ const settingsPath = path.join(homeDir, '.claude', 'settings.json');
82
+
83
+ try {
84
+ if (fs.existsSync(settingsPath)) {
85
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
86
+ if (!settings.env) settings.env = {};
87
+ settings.env.ANTHROPIC_BASE_URL = newUrl;
88
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
89
+
90
+ // 如果是通过 sudo 运行的,修改文件所有者
91
+ if (process.platform !== 'win32' && process.env.SUDO_USER) {
92
+ const actualUser = process.env.SUDO_USER;
93
+ const group = process.platform === 'darwin' ? 'staff' : actualUser;
94
+ try {
95
+ execSync(`chown ${actualUser}:${group} ${settingsPath}`);
96
+ } catch (error) {}
97
+ }
98
+
99
+ return true;
100
+ }
101
+ } catch (error) {
102
+ log(`[警告] 更新 settings.json 失败: ${error.message}`, 'yellow');
103
+ }
104
+ return false;
105
+ }
106
+
107
+ // 更新系统环境变量中的域名
108
+ function updateSystemEnv(newUrl) {
109
+ const osType = getOS();
110
+ const homeDir = getHomeDir();
111
+
112
+ try {
113
+ if (osType === 'windows') {
114
+ execSync(`setx ANTHROPIC_BASE_URL "${newUrl}"`, { stdio: 'pipe' });
115
+ return true;
116
+ } else if (osType === 'mac') {
117
+ const zshrcPath = path.join(homeDir, '.zshrc');
118
+ if (fs.existsSync(zshrcPath)) {
119
+ let content = fs.readFileSync(zshrcPath, 'utf8');
120
+ const regex = /export ANTHROPIC_BASE_URL=.*/g;
121
+ if (content.match(regex)) {
122
+ content = content.replace(regex, `export ANTHROPIC_BASE_URL="${newUrl}"`);
123
+ fs.writeFileSync(zshrcPath, content, 'utf8');
124
+
125
+ if (process.env.SUDO_USER) {
126
+ const actualUser = process.env.SUDO_USER;
127
+ try {
128
+ execSync(`chown ${actualUser}:staff ${zshrcPath}`);
129
+ } catch (error) {}
130
+ }
131
+ return true;
132
+ }
133
+ }
134
+ } else {
135
+ const bashrcPath = path.join(homeDir, '.bashrc');
136
+ if (fs.existsSync(bashrcPath)) {
137
+ let content = fs.readFileSync(bashrcPath, 'utf8');
138
+ const regex = /export ANTHROPIC_BASE_URL=.*/g;
139
+ if (content.match(regex)) {
140
+ content = content.replace(regex, `export ANTHROPIC_BASE_URL="${newUrl}"`);
141
+ fs.writeFileSync(bashrcPath, content, 'utf8');
142
+
143
+ if (process.env.SUDO_USER) {
144
+ const actualUser = process.env.SUDO_USER;
145
+ try {
146
+ execSync(`chown ${actualUser}:${actualUser} ${bashrcPath}`);
147
+ } catch (error) {}
148
+ }
149
+ return true;
150
+ }
151
+ }
152
+ }
153
+ } catch (error) {
154
+ log(`[警告] 更新系统环境变量失败: ${error.message}`, 'yellow');
155
+ }
156
+ return false;
157
+ }
158
+
159
+ // 清理 DNS 缓存
160
+ function flushDnsCache() {
161
+ const osType = getOS();
162
+ log('\n[DNS] 正在清理 DNS 缓存...', 'cyan');
163
+
164
+ try {
165
+ if (osType === 'windows') {
166
+ execSync('ipconfig /flushdns', { stdio: 'pipe' });
167
+ log('[成功] DNS 缓存已清理 (ipconfig /flushdns)', 'green');
168
+ } else if (osType === 'mac') {
169
+ // macOS 需要 sudo 权限
170
+ const isRoot = process.getuid && process.getuid() === 0;
171
+ if (isRoot) {
172
+ execSync('dscacheutil -flushcache && killall -HUP mDNSResponder', { stdio: 'pipe' });
173
+ } else {
174
+ execSync('sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder', { stdio: 'pipe' });
175
+ }
176
+ log('[成功] DNS 缓存已清理 (dscacheutil -flushcache)', 'green');
177
+ } else {
178
+ // Linux - 不同发行版命令不同,尝试常见的
179
+ const isRoot = process.getuid && process.getuid() === 0;
180
+ try {
181
+ if (isRoot) {
182
+ execSync('systemd-resolve --flush-caches 2>/dev/null || resolvectl flush-caches 2>/dev/null || true', { stdio: 'pipe' });
183
+ } else {
184
+ execSync('sudo systemd-resolve --flush-caches 2>/dev/null || sudo resolvectl flush-caches 2>/dev/null || true', { stdio: 'pipe' });
185
+ }
186
+ log('[成功] DNS 缓存已清理', 'green');
187
+ } catch (e) {
188
+ log('[提示] DNS 缓存清理命令不适用于此系统,已跳过', 'yellow');
189
+ }
190
+ }
191
+ return true;
192
+ } catch (error) {
193
+ log(`[警告] DNS 缓存清理失败: ${error.message}`, 'yellow');
194
+ return false;
195
+ }
196
+ }
197
+
198
+ // 切换域名
199
+ function switchDomain(newUrl) {
200
+ log(`\n[切换] 正在切换到: ${newUrl}`, 'cyan');
201
+
202
+ // 1. 更新 settings.json
203
+ const settingsUpdated = updateSettingsJson(newUrl);
204
+ if (settingsUpdated) {
205
+ log('[成功] settings.json 已更新', 'green');
206
+ } else {
207
+ log('[跳过] settings.json 不存在或更新失败', 'yellow');
208
+ }
209
+
210
+ // 2. 更新系统环境变量
211
+ const envUpdated = updateSystemEnv(newUrl);
212
+ if (envUpdated) {
213
+ log('[成功] 系统环境变量已更新', 'green');
214
+ } else {
215
+ log('[跳过] 系统环境变量更新失败或不存在', 'yellow');
216
+ }
217
+
218
+ if (settingsUpdated || envUpdated) {
219
+ log('\n[完成] 域名切换成功!', 'green');
220
+ const osType = getOS();
221
+ if (osType === 'windows') {
222
+ log('[提示] 请重新打开终端使环境变量生效', 'yellow');
223
+ } else if (osType === 'mac') {
224
+ log('[提示] 请重新打开终端或运行: source ~/.zshrc', 'yellow');
225
+ } else {
226
+ log('[提示] 请重新打开终端或运行: source ~/.bashrc', 'yellow');
227
+ }
228
+ } else {
229
+ log('\n[警告] 没有配置文件被更新,请先运行安装配置', 'yellow');
230
+ }
231
+ }
232
+
233
+ let selectedIndex = 0;
234
+
235
+ // 渲染域名选择菜单
236
+ function renderMenu() {
237
+ console.clear();
238
+ log('\n', 'reset');
239
+ log(' ' + '='.repeat(54), 'cyan');
240
+ log(' 网络修复 - 域名切换', 'magenta');
241
+ log(' ' + '='.repeat(54), 'cyan');
242
+ log('', 'reset');
243
+
244
+ const currentDomain = getCurrentDomain();
245
+
246
+ log(' 请选择要使用的域名 (↑↓选择, Enter确认, ESC返回):\n', 'cyan');
247
+
248
+ domainOptions.forEach((option, index) => {
249
+ const isSelected = index === selectedIndex;
250
+ const prefix = isSelected ? ' ▶ ' : ' ';
251
+ const nameColor = isSelected ? '\x1b[1m\x1b[33m' : '\x1b[0m';
252
+ const numLabel = `${index + 1}. `;
253
+
254
+ // 检查是否是当前使用的域名
255
+ const isCurrent = option.url && currentDomain === option.url;
256
+ const currentIndicator = isCurrent ? ' \x1b[32m← 当前使用\x1b[0m' : '';
257
+
258
+ console.log(`${prefix}${nameColor}${numLabel}${option.name}${currentIndicator}\x1b[0m`);
259
+ if (option.url && option.url !== 'dns_flush') {
260
+ const descColor = isSelected ? '\x1b[36m' : '\x1b[2m';
261
+ console.log(` ${descColor}${option.url}\x1b[0m`);
262
+ }
263
+ console.log('');
264
+ });
265
+
266
+ log(' ' + '='.repeat(54), 'cyan');
267
+
268
+ if (currentDomain) {
269
+ log(`\n 当前域名: ${currentDomain}`, 'dim');
270
+ } else {
271
+ log('\n 当前域名: 未配置', 'dim');
272
+ }
273
+ }
274
+
275
+ // 主函数
276
+ async function main() {
277
+ // 启用原始模式以捕获按键
278
+ if (process.stdin.isTTY) {
279
+ process.stdin.setRawMode(true);
280
+ }
281
+ process.stdin.resume();
282
+ process.stdin.setEncoding('utf8');
283
+
284
+ renderMenu();
285
+
286
+ return new Promise((resolve) => {
287
+ const handleKeyPress = (key) => {
288
+ // Ctrl+C 退出
289
+ if (key === '\u0003') {
290
+ console.clear();
291
+ process.exit(0);
292
+ }
293
+
294
+ // ESC 返回
295
+ if (key === '\u001b') {
296
+ if (process.stdin.isTTY) {
297
+ process.stdin.setRawMode(false);
298
+ }
299
+ process.stdin.removeAllListeners('data');
300
+ resolve(false);
301
+ return;
302
+ }
303
+
304
+ // 上箭头
305
+ if (key === '\u001b[A' || key === 'k') {
306
+ selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : domainOptions.length - 1;
307
+ renderMenu();
308
+ }
309
+
310
+ // 下箭头
311
+ if (key === '\u001b[B' || key === 'j') {
312
+ selectedIndex = selectedIndex < domainOptions.length - 1 ? selectedIndex + 1 : 0;
313
+ renderMenu();
314
+ }
315
+
316
+ // Enter 确认
317
+ if (key === '\r' || key === '\n') {
318
+ const selected = domainOptions[selectedIndex];
319
+
320
+ // 切换域名
321
+ if (process.stdin.isTTY) {
322
+ process.stdin.setRawMode(false);
323
+ }
324
+ process.stdin.removeAllListeners('data');
325
+
326
+ // 根据选项执行不同操作
327
+ if (selected.url === 'dns_flush') {
328
+ flushDnsCache();
329
+ } else {
330
+ switchDomain(selected.url);
331
+ }
332
+
333
+ // 等待用户按键返回
334
+ log('\n按任意键返回菜单...', 'dim');
335
+
336
+ if (process.stdin.isTTY) {
337
+ process.stdin.setRawMode(true);
338
+ }
339
+ process.stdin.resume();
340
+ process.stdin.once('data', () => {
341
+ if (process.stdin.isTTY) {
342
+ process.stdin.setRawMode(false);
343
+ }
344
+ selectedIndex = 0;
345
+ resolve(false);
346
+ });
347
+ return;
348
+ }
349
+
350
+ // 数字键快捷选择
351
+ const num = parseInt(key);
352
+ if (num >= 1 && num <= domainOptions.length) {
353
+ selectedIndex = num - 1;
354
+ renderMenu();
355
+ }
356
+ };
357
+
358
+ process.stdin.on('data', handleKeyPress);
359
+ });
360
+ }
361
+
362
+ // 如果直接运行此脚本
363
+ if (require.main === module) {
364
+ main().then(() => {
365
+ process.exit(0);
366
+ }).catch(error => {
367
+ log(`\n[错误] ${error.message}`, 'red');
368
+ process.exit(1);
369
+ });
370
+ }
371
+
372
+ module.exports = { main, getCurrentDomain, switchDomain };
package/claude-setup.js CHANGED
@@ -49,16 +49,6 @@ function getUserInput(question) {
49
49
  });
50
50
  }
51
51
 
52
- // 检查 @anthropic-ai/claude-code 是否已安装
53
- function checkClaudeInstalled() {
54
- try {
55
- const output = execSync('npm list -g @anthropic-ai/claude-code --depth=0', { encoding: 'utf8' });
56
- return output.includes('@anthropic-ai/claude-code');
57
- } catch (error) {
58
- return false;
59
- }
60
- }
61
-
62
52
  // 获取 @anthropic-ai/claude-code 版本号
63
53
  function getClaudeVersion() {
64
54
  try {
@@ -75,15 +65,7 @@ function installClaude() {
75
65
  const osType = getOS();
76
66
  let command;
77
67
 
78
- log('\n[检查] 检查 @anthropic-ai/claude-code 安装状态...', 'cyan');
79
-
80
- // 先检查是否已安装
81
- if (checkClaudeInstalled()) {
82
- log('[成功] @anthropic-ai/claude-code 已安装,跳过安装步骤', 'green');
83
- return true;
84
- }
85
-
86
- log('[安装] 开始安装 @anthropic-ai/claude-code...', 'cyan');
68
+ log('\n[安装] 开始安装 @anthropic-ai/claude-code...', 'cyan');
87
69
 
88
70
  if (osType === 'windows') {
89
71
  command = 'npm install -g @anthropic-ai/claude-code@2.0.59 --registry https://registry.npmmirror.com';
package/codex-api.js CHANGED
@@ -61,16 +61,6 @@ function getUserInput(question) {
61
61
  });
62
62
  }
63
63
 
64
- // 检查 @openai/codex 是否已安装
65
- function checkCodexInstalled() {
66
- try {
67
- const output = execSync('npm list -g @openai/codex --depth=0', { encoding: 'utf8' });
68
- return output.includes('@openai/codex');
69
- } catch (error) {
70
- return false;
71
- }
72
- }
73
-
74
64
  // 获取 @openai/codex 版本号
75
65
  function getCodexVersion() {
76
66
  try {
@@ -87,15 +77,7 @@ function installCodex() {
87
77
  const osType = getOS();
88
78
  let command;
89
79
 
90
- log('\n[检查] 检查 @openai/codex 安装状态...', 'cyan');
91
-
92
- // 先检查是否已安装
93
- if (checkCodexInstalled()) {
94
- log('[成功] @openai/codex 已安装,跳过安装步骤', 'green');
95
- return true;
96
- }
97
-
98
- log('[安装] 开始安装 @openai/codex...', 'cyan');
80
+ log('\n[安装] 开始安装 @openai/codex...', 'cyan');
99
81
 
100
82
  if (osType === 'windows') {
101
83
  command = 'npm install -g @openai/codex --registry https://registry.npmmirror.com';
@@ -0,0 +1,307 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Codex 网络修复工具
5
+ * 检测当前域名并允许切换到备用域名
6
+ * 只修改 config.toml 配置文件,不修改环境变量
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const os = require('os');
12
+ const { execSync } = require('child_process');
13
+
14
+ // 颜色输出
15
+ const colors = {
16
+ red: '\x1b[31m',
17
+ green: '\x1b[32m',
18
+ yellow: '\x1b[33m',
19
+ blue: '\x1b[34m',
20
+ magenta: '\x1b[35m',
21
+ cyan: '\x1b[36m',
22
+ reset: '\x1b[0m',
23
+ dim: '\x1b[2m',
24
+ bright: '\x1b[1m'
25
+ };
26
+
27
+ function log(message, color = 'reset') {
28
+ console.log(`${colors[color]}${message}${colors.reset}`);
29
+ }
30
+
31
+ // 可用域名列表 (Codex 使用 /v1 后缀)
32
+ const domainOptions = [
33
+ { name: '主域名', url: 'https://code.ppchat.vip/v1' },
34
+ { name: '备用域名1', url: 'https://code2.ppchat.vip/v1' },
35
+ { name: '备用域名2', url: 'https://code.pumpkinai.vip/v1' },
36
+ { name: '清理DNS缓存-推荐', url: 'dns_flush' }
37
+ ];
38
+
39
+ // 检测操作系统
40
+ function getOS() {
41
+ const platform = os.platform();
42
+ if (platform === 'win32') return 'windows';
43
+ if (platform === 'darwin') return 'mac';
44
+ return 'linux';
45
+ }
46
+
47
+ // 获取用户目录
48
+ function getHomeDir() {
49
+ if (process.platform !== 'win32' && process.env.SUDO_USER) {
50
+ const actualUser = process.env.SUDO_USER;
51
+ return process.platform === 'darwin' ? `/Users/${actualUser}` : `/home/${actualUser}`;
52
+ }
53
+ return os.homedir();
54
+ }
55
+
56
+ // 获取当前使用的域名 (从 config.toml 读取)
57
+ function getCurrentDomain() {
58
+ const homeDir = getHomeDir();
59
+ const configPath = path.join(homeDir, '.codex', 'config.toml');
60
+
61
+ if (fs.existsSync(configPath)) {
62
+ try {
63
+ const content = fs.readFileSync(configPath, 'utf8');
64
+ // 匹配 base_url = "xxx" 格式
65
+ const match = content.match(/base_url\s*=\s*"([^"]+)"/);
66
+ if (match) {
67
+ return match[1];
68
+ }
69
+ } catch (error) {}
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ // 更新 config.toml 中的域名
76
+ function updateConfigToml(newUrl) {
77
+ const homeDir = getHomeDir();
78
+ const configPath = path.join(homeDir, '.codex', 'config.toml');
79
+
80
+ try {
81
+ if (fs.existsSync(configPath)) {
82
+ let content = fs.readFileSync(configPath, 'utf8');
83
+
84
+ // 替换 base_url 的值
85
+ const regex = /base_url\s*=\s*"[^"]+"/;
86
+ if (content.match(regex)) {
87
+ content = content.replace(regex, `base_url = "${newUrl}"`);
88
+ fs.writeFileSync(configPath, content, 'utf8');
89
+
90
+ // 如果是通过 sudo 运行的,修改文件所有者
91
+ if (process.platform !== 'win32' && process.env.SUDO_USER) {
92
+ const actualUser = process.env.SUDO_USER;
93
+ const group = process.platform === 'darwin' ? 'staff' : actualUser;
94
+ try {
95
+ execSync(`chown ${actualUser}:${group} ${configPath}`);
96
+ } catch (error) {}
97
+ }
98
+
99
+ return true;
100
+ } else {
101
+ log('[警告] config.toml 中未找到 base_url 配置', 'yellow');
102
+ return false;
103
+ }
104
+ } else {
105
+ log('[警告] config.toml 文件不存在,请先运行安装配置', 'yellow');
106
+ return false;
107
+ }
108
+ } catch (error) {
109
+ log(`[警告] 更新 config.toml 失败: ${error.message}`, 'yellow');
110
+ }
111
+ return false;
112
+ }
113
+
114
+ // 清理 DNS 缓存
115
+ function flushDnsCache() {
116
+ const osType = getOS();
117
+ log('\n[DNS] 正在清理 DNS 缓存...', 'cyan');
118
+
119
+ try {
120
+ if (osType === 'windows') {
121
+ execSync('ipconfig /flushdns', { stdio: 'pipe' });
122
+ log('[成功] DNS 缓存已清理 (ipconfig /flushdns)', 'green');
123
+ } else if (osType === 'mac') {
124
+ // macOS 需要 sudo 权限
125
+ const isRoot = process.getuid && process.getuid() === 0;
126
+ if (isRoot) {
127
+ execSync('dscacheutil -flushcache && killall -HUP mDNSResponder', { stdio: 'pipe' });
128
+ } else {
129
+ execSync('sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder', { stdio: 'pipe' });
130
+ }
131
+ log('[成功] DNS 缓存已清理 (dscacheutil -flushcache)', 'green');
132
+ } else {
133
+ // Linux - 不同发行版命令不同,尝试常见的
134
+ const isRoot = process.getuid && process.getuid() === 0;
135
+ try {
136
+ if (isRoot) {
137
+ execSync('systemd-resolve --flush-caches 2>/dev/null || resolvectl flush-caches 2>/dev/null || true', { stdio: 'pipe' });
138
+ } else {
139
+ execSync('sudo systemd-resolve --flush-caches 2>/dev/null || sudo resolvectl flush-caches 2>/dev/null || true', { stdio: 'pipe' });
140
+ }
141
+ log('[成功] DNS 缓存已清理', 'green');
142
+ } catch (e) {
143
+ log('[提示] DNS 缓存清理命令不适用于此系统,已跳过', 'yellow');
144
+ }
145
+ }
146
+ return true;
147
+ } catch (error) {
148
+ log(`[警告] DNS 缓存清理失败: ${error.message}`, 'yellow');
149
+ return false;
150
+ }
151
+ }
152
+
153
+ // 切换域名
154
+ function switchDomain(newUrl) {
155
+ log(`\n[切换] 正在切换到: ${newUrl}`, 'cyan');
156
+
157
+ // 更新 config.toml
158
+ const configUpdated = updateConfigToml(newUrl);
159
+ if (configUpdated) {
160
+ log('[成功] config.toml 已更新', 'green');
161
+ log('\n[完成] 域名切换成功!', 'green');
162
+ log('[提示] 新域名将在下次启动 Codex 时生效', 'yellow');
163
+ } else {
164
+ log('\n[警告] 配置文件更新失败,请先运行安装配置', 'yellow');
165
+ }
166
+ }
167
+
168
+ let selectedIndex = 0;
169
+
170
+ // 渲染域名选择菜单
171
+ function renderMenu() {
172
+ console.clear();
173
+ log('\n', 'reset');
174
+ log(' ' + '='.repeat(54), 'cyan');
175
+ log(' Codex 网络修复 - 域名切换', 'magenta');
176
+ log(' ' + '='.repeat(54), 'cyan');
177
+ log('', 'reset');
178
+
179
+ const currentDomain = getCurrentDomain();
180
+
181
+ log(' 请选择要使用的域名 (↑↓选择, Enter确认, ESC返回):\n', 'cyan');
182
+
183
+ domainOptions.forEach((option, index) => {
184
+ const isSelected = index === selectedIndex;
185
+ const prefix = isSelected ? ' ▶ ' : ' ';
186
+ const nameColor = isSelected ? '\x1b[1m\x1b[33m' : '\x1b[0m';
187
+ const numLabel = `${index + 1}. `;
188
+
189
+ // 检查是否是当前使用的域名
190
+ const isCurrent = option.url && currentDomain === option.url;
191
+ const currentIndicator = isCurrent ? ' \x1b[32m← 当前使用\x1b[0m' : '';
192
+
193
+ console.log(`${prefix}${nameColor}${numLabel}${option.name}${currentIndicator}\x1b[0m`);
194
+ if (option.url && option.url !== 'dns_flush') {
195
+ const descColor = isSelected ? '\x1b[36m' : '\x1b[2m';
196
+ console.log(` ${descColor}${option.url}\x1b[0m`);
197
+ }
198
+ console.log('');
199
+ });
200
+
201
+ log(' ' + '='.repeat(54), 'cyan');
202
+
203
+ if (currentDomain) {
204
+ log(`\n 当前域名: ${currentDomain}`, 'dim');
205
+ } else {
206
+ log('\n 当前域名: 未配置', 'dim');
207
+ }
208
+ }
209
+
210
+ // 主函数
211
+ async function main() {
212
+ // 启用原始模式以捕获按键
213
+ if (process.stdin.isTTY) {
214
+ process.stdin.setRawMode(true);
215
+ }
216
+ process.stdin.resume();
217
+ process.stdin.setEncoding('utf8');
218
+
219
+ renderMenu();
220
+
221
+ return new Promise((resolve) => {
222
+ const handleKeyPress = (key) => {
223
+ // Ctrl+C 退出
224
+ if (key === '\u0003') {
225
+ console.clear();
226
+ process.exit(0);
227
+ }
228
+
229
+ // ESC 返回
230
+ if (key === '\u001b') {
231
+ if (process.stdin.isTTY) {
232
+ process.stdin.setRawMode(false);
233
+ }
234
+ process.stdin.removeAllListeners('data');
235
+ resolve(false);
236
+ return;
237
+ }
238
+
239
+ // 上箭头
240
+ if (key === '\u001b[A' || key === 'k') {
241
+ selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : domainOptions.length - 1;
242
+ renderMenu();
243
+ }
244
+
245
+ // 下箭头
246
+ if (key === '\u001b[B' || key === 'j') {
247
+ selectedIndex = selectedIndex < domainOptions.length - 1 ? selectedIndex + 1 : 0;
248
+ renderMenu();
249
+ }
250
+
251
+ // Enter 确认
252
+ if (key === '\r' || key === '\n') {
253
+ const selected = domainOptions[selectedIndex];
254
+
255
+ // 切换域名
256
+ if (process.stdin.isTTY) {
257
+ process.stdin.setRawMode(false);
258
+ }
259
+ process.stdin.removeAllListeners('data');
260
+
261
+ // 根据选项执行不同操作
262
+ if (selected.url === 'dns_flush') {
263
+ flushDnsCache();
264
+ } else {
265
+ switchDomain(selected.url);
266
+ }
267
+
268
+ // 等待用户按键返回
269
+ log('\n按任意键返回菜单...', 'dim');
270
+
271
+ if (process.stdin.isTTY) {
272
+ process.stdin.setRawMode(true);
273
+ }
274
+ process.stdin.resume();
275
+ process.stdin.once('data', () => {
276
+ if (process.stdin.isTTY) {
277
+ process.stdin.setRawMode(false);
278
+ }
279
+ selectedIndex = 0;
280
+ resolve(false);
281
+ });
282
+ return;
283
+ }
284
+
285
+ // 数字键快捷选择
286
+ const num = parseInt(key);
287
+ if (num >= 1 && num <= domainOptions.length) {
288
+ selectedIndex = num - 1;
289
+ renderMenu();
290
+ }
291
+ };
292
+
293
+ process.stdin.on('data', handleKeyPress);
294
+ });
295
+ }
296
+
297
+ // 如果直接运行此脚本
298
+ if (require.main === module) {
299
+ main().then(() => {
300
+ process.exit(0);
301
+ }).catch(error => {
302
+ log(`\n[错误] ${error.message}`, 'red');
303
+ process.exit(1);
304
+ });
305
+ }
306
+
307
+ module.exports = { main, getCurrentDomain, switchDomain };
package/codex-setup.js CHANGED
@@ -61,16 +61,6 @@ function getUserInput(question) {
61
61
  });
62
62
  }
63
63
 
64
- // 检查 @openai/codex 是否已安装
65
- function checkCodexInstalled() {
66
- try {
67
- const output = execSync('npm list -g @openai/codex --depth=0', { encoding: 'utf8' });
68
- return output.includes('@openai/codex');
69
- } catch (error) {
70
- return false;
71
- }
72
- }
73
-
74
64
  // 获取 @openai/codex 版本号
75
65
  function getCodexVersion() {
76
66
  try {
@@ -87,15 +77,7 @@ function installCodex() {
87
77
  const osType = getOS();
88
78
  let command;
89
79
 
90
- log('\n[检查] 检查 @openai/codex 安装状态...', 'cyan');
91
-
92
- // 先检查是否已安装
93
- if (checkCodexInstalled()) {
94
- log('[成功] @openai/codex 已安装,跳过安装步骤', 'green');
95
- return true;
96
- }
97
-
98
- log('[安装] 开始安装 @openai/codex...', 'cyan');
80
+ log('\n[安装] 开始安装 @openai/codex...', 'cyan');
99
81
 
100
82
  if (osType === 'windows') {
101
83
  command = 'npm install -g @openai/codex --registry https://registry.npmmirror.com';
package/main-config.js CHANGED
@@ -32,38 +32,42 @@ const mainMenuOptions = [
32
32
  // Claude Code 子菜单选项
33
33
  const claudeSubMenuOptions = [
34
34
  { name: '按请求计费套餐', desc: 'API地址: https://code.ppchat.vip' },
35
- { name: '按量计费套餐', desc: 'API地址: https://new.aicode.us.com' },
36
- { name: '报错修复', desc: '' },
37
- { name: '返回上级', desc: '' }
35
+ { name: '按token计费套餐', desc: 'API地址: https://new.aicode.us.com' },
36
+ { name: '报错修复', desc: '' }
38
37
  ];
39
38
 
40
39
  // Codex 子菜单选项
41
40
  const codexSubMenuOptions = [
42
- { name: '按请求计费套餐', desc: 'API地址: https://code.ppchat.vip' },
43
- { name: '按量计费套餐', desc: 'API地址: https://new.aicode.us.com' },
44
- { name: '返回上级', desc: '' }
41
+ { name: '按请求计费套餐', desc: 'API地址: https://code.ppchat.vip/v1' },
42
+ { name: '按量token计费套餐', desc: 'API地址: https://new.aicode.us.com/v1' },
43
+ { name: '报错修复', desc: '' }
45
44
  ];
46
45
 
47
- // 报错修复子菜单选项
46
+ // 报错修复子菜单选项 (Claude)
48
47
  const fixSubMenuOptions = [
49
48
  { name: '彻底重新安装', desc: '删除所有配置并重装 Claude Code' },
50
- { name: '返回上级', desc: '' }
49
+ { name: '网络修复', desc: '切换API域名 (主域名/备用域名)' }
50
+ ];
51
+
52
+ // 报错修复子菜单选项 (Codex)
53
+ const codexFixSubMenuOptions = [
54
+ { name: '网络修复', desc: '切换API域名 (主域名/备用域名)' }
51
55
  ];
52
56
 
53
57
  // 脚本映射
54
58
  const scriptMap = {
55
59
  'Claude Code': {
56
60
  '按请求计费套餐': './claude-setup.js',
57
- '按量计费套餐': './claude-api.js'
61
+ '按量token计费套餐': './claude-api.js'
58
62
  },
59
63
  'Codex': {
60
64
  '按请求计费套餐': './codex-setup.js',
61
- '按量计费套餐': './codex-api.js'
65
+ '按token计费套餐': './codex-api.js'
62
66
  }
63
67
  };
64
68
 
65
69
  let selectedIndex = 0;
66
- let currentMenu = 'main'; // 'main', 'claude_sub', 'codex_sub', 'fix_sub'
70
+ let currentMenu = 'main'; // 'main', 'claude_sub', 'codex_sub', 'fix_sub', 'codex_fix_sub'
67
71
 
68
72
  // 显示 Logo
69
73
  function showLogo() {
@@ -91,8 +95,9 @@ function renderGenericMenu(title, options) {
91
95
  const prefix = isSelected ? ' ▶ ' : ' ';
92
96
  const nameColor = isSelected ? '\x1b[1m\x1b[33m' : '\x1b[0m';
93
97
  const descColor = isSelected ? '\x1b[36m' : '\x1b[2m';
98
+ const numLabel = `${index + 1}. `;
94
99
 
95
- console.log(`${prefix}${nameColor}${option.name}\x1b[0m`);
100
+ console.log(`${prefix}${nameColor}${numLabel}${option.name}\x1b[0m`);
96
101
  if (option.desc) {
97
102
  console.log(` ${descColor}${option.desc}\x1b[0m`);
98
103
  }
@@ -109,13 +114,16 @@ function renderMenu() {
109
114
  renderGenericMenu('请选择要配置的工具 (↑↓选择, Enter确认):', mainMenuOptions);
110
115
  break;
111
116
  case 'claude_sub':
112
- renderGenericMenu('Claude Code - 请选择购买的套餐 (↑↓选择, Enter确认):', claudeSubMenuOptions);
117
+ renderGenericMenu('Claude Code - 请选择购买的套餐 (↑↓选择, Enter确认, ESC返回):', claudeSubMenuOptions);
113
118
  break;
114
119
  case 'codex_sub':
115
- renderGenericMenu('Codex - 请选择购买的套餐 (↑↓选择, Enter确认):', codexSubMenuOptions);
120
+ renderGenericMenu('Codex - 请选择购买的套餐 (↑↓选择, Enter确认, ESC返回):', codexSubMenuOptions);
116
121
  break;
117
122
  case 'fix_sub':
118
- renderGenericMenu('报错修复 - 请选择操作 (↑↓选择, Enter确认):', fixSubMenuOptions);
123
+ renderGenericMenu('Claude 报错修复 - 请选择操作 (↑↓选择, Enter确认, ESC返回):', fixSubMenuOptions);
124
+ break;
125
+ case 'codex_fix_sub':
126
+ renderGenericMenu('Codex 报错修复 - 请选择操作 (↑↓选择, Enter确认, ESC返回):', codexFixSubMenuOptions);
119
127
  break;
120
128
  }
121
129
  }
@@ -127,6 +135,7 @@ function getCurrentOptions() {
127
135
  case 'claude_sub': return claudeSubMenuOptions;
128
136
  case 'codex_sub': return codexSubMenuOptions;
129
137
  case 'fix_sub': return fixSubMenuOptions;
138
+ case 'codex_fix_sub': return codexFixSubMenuOptions;
130
139
  default: return mainMenuOptions;
131
140
  }
132
141
  }
@@ -154,13 +163,6 @@ function handleMainMenuSelection() {
154
163
  async function handleClaudeSubMenuSelection() {
155
164
  const selected = claudeSubMenuOptions[selectedIndex];
156
165
 
157
- if (selected.name === '返回上级') {
158
- currentMenu = 'main';
159
- selectedIndex = 0;
160
- renderMenu();
161
- return false;
162
- }
163
-
164
166
  if (selected.name === '报错修复') {
165
167
  currentMenu = 'fix_sub';
166
168
  selectedIndex = 0;
@@ -187,8 +189,8 @@ async function handleClaudeSubMenuSelection() {
187
189
  async function handleCodexSubMenuSelection() {
188
190
  const selected = codexSubMenuOptions[selectedIndex];
189
191
 
190
- if (selected.name === '返回上级') {
191
- currentMenu = 'main';
192
+ if (selected.name === '报错修复') {
193
+ currentMenu = 'codex_fix_sub';
192
194
  selectedIndex = 0;
193
195
  renderMenu();
194
196
  return false;
@@ -209,17 +211,10 @@ async function handleCodexSubMenuSelection() {
209
211
  }
210
212
  }
211
213
 
212
- // 处理报错修复子菜单选择
214
+ // 处理 Claude 报错修复子菜单选择
213
215
  async function handleFixSubMenuSelection() {
214
216
  const selected = fixSubMenuOptions[selectedIndex];
215
217
 
216
- if (selected.name === '返回上级') {
217
- currentMenu = 'claude_sub';
218
- selectedIndex = 0;
219
- renderMenu();
220
- return false;
221
- }
222
-
223
218
  if (selected.name === '彻底重新安装') {
224
219
  // 调用 claude-fix.js 中的修复功能
225
220
  const claudeFix = require('./claude-fix.js');
@@ -229,6 +224,31 @@ async function handleFixSubMenuSelection() {
229
224
  return false;
230
225
  }
231
226
 
227
+ if (selected.name === '网络修复') {
228
+ // 调用 claude-network.js 中的网络修复功能
229
+ const claudeNetwork = require('./claude-network.js');
230
+ await claudeNetwork.main();
231
+ currentMenu = 'fix_sub';
232
+ selectedIndex = 0;
233
+ return false;
234
+ }
235
+
236
+ return false;
237
+ }
238
+
239
+ // 处理 Codex 报错修复子菜单选择
240
+ async function handleCodexFixSubMenuSelection() {
241
+ const selected = codexFixSubMenuOptions[selectedIndex];
242
+
243
+ if (selected.name === '网络修复') {
244
+ // 调用 codex-network.js 中的网络修复功能
245
+ const codexNetwork = require('./codex-network.js');
246
+ await codexNetwork.main();
247
+ currentMenu = 'codex_fix_sub';
248
+ selectedIndex = 0;
249
+ return false;
250
+ }
251
+
232
252
  return false;
233
253
  }
234
254
 
@@ -241,6 +261,8 @@ async function handleSubMenuSelection() {
241
261
  return await handleCodexSubMenuSelection();
242
262
  case 'fix_sub':
243
263
  return await handleFixSubMenuSelection();
264
+ case 'codex_fix_sub':
265
+ return await handleCodexFixSubMenuSelection();
244
266
  default:
245
267
  return false;
246
268
  }
@@ -274,6 +296,11 @@ async function main() {
274
296
  selectedIndex = 0;
275
297
  renderMenu();
276
298
  return;
299
+ } else if (currentMenu === 'codex_fix_sub') {
300
+ currentMenu = 'codex_sub';
301
+ selectedIndex = 0;
302
+ renderMenu();
303
+ return;
277
304
  } else if (currentMenu === 'claude_sub' || currentMenu === 'codex_sub') {
278
305
  currentMenu = 'main';
279
306
  selectedIndex = 0;
package/package.json CHANGED
@@ -1,15 +1,10 @@
1
1
  {
2
2
  "name": "pumpkinai-config",
3
- "version": "1.0.21",
3
+ "version": "1.3.0",
4
4
  "description": "PumpkinAI 一键安装配置工具 - 支持 Codex 和 Claude Code",
5
5
  "main": "main-config.js",
6
6
  "bin": {
7
- "pumpkinai": "./main-config.js",
8
- "codex-config": "./codex-setup.js",
9
- "codex-api": "./codex-api.js",
10
- "claude-config": "./claude-setup.js",
11
- "claude-api": "./claude-api.js",
12
- "claude-fix": "./claude-fix.js"
7
+ "pumpkinai": "./main-config.js"
13
8
  },
14
9
  "scripts": {
15
10
  "test": "node main-config.js",