pumpkinai-config 1.3.1 → 1.4.1

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.
@@ -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,20 +77,18 @@ 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';
102
84
  } else {
103
- command = 'sudo npm install -g @openai/codex --registry https://registry.npmmirror.com';
85
+ // 检查是否已经是 root 用户(通过 sudo 运行或直接 root)
86
+ const isRoot = process.getuid && process.getuid() === 0;
87
+ if (isRoot) {
88
+ command = 'npm install -g @openai/codex --registry https://registry.npmmirror.com';
89
+ } else {
90
+ command = 'sudo npm install -g @openai/codex --registry https://registry.npmmirror.com';
91
+ }
104
92
  }
105
93
 
106
94
  log(`[执行] ${command}`, 'yellow');
@@ -111,7 +99,28 @@ function installCodex() {
111
99
  return true;
112
100
  } catch (error) {
113
101
  log('[错误] @openai/codex 安装失败', 'red');
114
- log(`[错误] ${error.message}`, 'red');
102
+ log('\n[错误详情]', 'red');
103
+
104
+ // 显示错误信息
105
+ if (error.message) {
106
+ log(`错误信息: ${error.message}`, 'red');
107
+ }
108
+
109
+ // 显示退出码
110
+ if (error.status !== undefined) {
111
+ log(`退出码: ${error.status}`, 'red');
112
+ }
113
+
114
+ // 显示错误输出
115
+ if (error.stderr) {
116
+ log(`错误输出:\n${error.stderr.toString()}`, 'red');
117
+ }
118
+
119
+ // 显示标准输出(可能包含有用信息)
120
+ if (error.stdout) {
121
+ log(`标准输出:\n${error.stdout.toString()}`, 'yellow');
122
+ }
123
+
115
124
  log('\n[提示] 你可以手动安装:', 'yellow');
116
125
  log(` ${command}`, 'yellow');
117
126
  log(' 然后重新运行此工具', 'yellow');
@@ -163,7 +172,7 @@ function createConfigToml(codexDir) {
163
172
  const configPath = path.join(codexDir, 'config.toml');
164
173
 
165
174
  const configContent = `model_provider = "codex"
166
- model = "gpt-5" #可更改为model = "gpt-5-codex"
175
+ model = "gpt-5.2" #可更改为model = "gpt-5.1-codex或者gpt-5.1-codex-max"
167
176
  model_reasoning_effort = "high"
168
177
  disable_response_storage = true
169
178
 
@@ -285,7 +294,7 @@ function configureVSCodeSettings() {
285
294
  "chatgpt.apiBase": "https://code.ppchat.vip/v1",
286
295
  "chatgpt.config": {
287
296
  "preferred_auth_method": "apikey",
288
- "model": "gpt-5",
297
+ "model": "gpt-5.1",
289
298
  "model_reasoning_effort": "high",
290
299
  "wire_api": "responses"
291
300
  }
@@ -377,7 +386,7 @@ function configureCursorSettings() {
377
386
  "chatgpt.apiBase": "https://code.ppchat.vip/v1",
378
387
  "chatgpt.config": {
379
388
  "preferred_auth_method": "apikey",
380
- "model": "gpt-5",
389
+ "model": "gpt-5.1",
381
390
  "model_reasoning_effort": "high",
382
391
  "wire_api": "responses"
383
392
  }
@@ -451,36 +460,26 @@ function showCompletionInfo() {
451
460
  const authPath = path.join(codexPath, 'auth.json');
452
461
 
453
462
  log('\n', 'reset');
454
- log('' + ''.repeat(68) + '', 'cyan');
455
- log('║' + ' '.repeat(10) + '欢迎使用 Codex 开始您的 AI 编程之旅' + ' '.repeat(10) + '║', 'cyan');
456
- log('' + ''.repeat(68) + '', 'cyan');
457
-
458
- log('\n ┌─ 已完成操作', 'cyan');
459
- log(' ', 'cyan');
460
- log(' │ ✓ 安装 @openai/codex', 'green');
461
- log(` │ ✓ 创建目录 ${codexPath}`, 'green');
462
- log(' │ ✓ 创建配置文件 config.toml', 'green');
463
- log(' │ ✓ 创建认证文件 auth.json', 'green');
464
- log('', 'cyan');
465
- log(' └─────────────────────────────────────────────', 'cyan');
466
-
467
- log('\n ┌─ 配置文件路径', 'cyan');
468
- log(' ', 'cyan');
469
- log(` │ config.toml(可此处数修改域名url)`, 'reset');
470
- log(` │ ${configPath}`, 'yellow');
471
- log('', 'cyan');
472
- log(` │ auth.json(可在此处修改key)`, 'reset');
473
- log(` │ ${authPath}`, 'yellow');
474
- log(' │', 'cyan');
475
- log(' └─────────────────────────────────────────────', 'cyan');
476
-
477
- log('\n ┌─ 使用方式', 'cyan');
478
- log(' │', 'cyan');
479
- log(' │ 在终端输入以下命令启动 Codex:', 'reset');
480
- log(' │', 'cyan');
481
- log(' │ codex', 'yellow');
482
- log(' │', 'cyan');
483
- log(' └─────────────────────────────────────────────', 'cyan');
463
+ log('=' + '='.repeat(68) + '=', 'yellow');
464
+ log(' 欢迎使用 Codex 开始您的 AI 编程之旅', 'yellow');
465
+ log('=' + '='.repeat(68) + '=', 'yellow');
466
+
467
+ log('\n[已完成操作]', 'cyan');
468
+ log(' * 安装 @openai/codex', 'green');
469
+ log(` * 创建目录 ${codexPath}`, 'green');
470
+ log(' * 创建配置文件 config.toml', 'green');
471
+ log(' * 创建认证文件 auth.json', 'green');
472
+
473
+ log('\n[配置文件路径]', 'cyan');
474
+ log(' config.toml (可在此处修改域名url):', 'reset');
475
+ log(` ${configPath}`, 'yellow');
476
+ log('', 'reset');
477
+ log(' auth.json (可在此处修改key):', 'reset');
478
+ log(` ${authPath}`, 'yellow');
479
+
480
+ log('\n[使用方式]', 'cyan');
481
+ log(' 在终端输入以下命令启动 Codex:', 'reset');
482
+ log(' codex', 'yellow');
484
483
 
485
484
  const codexVersion = getCodexVersion();
486
485
  log('\n Codex 版本: v' + codexVersion, 'reset');
@@ -492,15 +491,15 @@ function showCompletionInfo() {
492
491
  async function main() {
493
492
  console.clear();
494
493
 
495
- // ASCII 艺术 Logo
494
+ // ASCII Logo - 水平渐变 (蓝→紫→粉)
496
495
  log('\n', 'reset');
497
- log(' ██████╗ ██████╗ ██████╗ ███████╗██╗ ██╗', 'cyan');
498
- log(' ██╔════╝██╔═══██╗██╔══██╗██╔════╝╚██╗██╔╝', 'cyan');
499
- log(' ██║ ██║ ██║██║ ██║█████╗ ╚███╔╝ ', 'cyan');
500
- log(' ██║ ██║ ██║██║ ██║██╔══╝ ██╔██╗ ', 'cyan');
501
- log(' ╚██████╗╚██████╔╝██████╔╝███████╗██╔╝ ██╗', 'cyan');
502
- log(' ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝', 'cyan');
503
- log('\n PPChat Codex 一键安装配置工具', 'magenta');
496
+ console.log(' \x1b[38;5;39m██████╗ \x1b[38;5;75m██████╗ \x1b[38;5;111m██████╗ \x1b[38;5;147m███████╗\x1b[38;5;183m██╗ \x1b[38;5;219m██╗\x1b[0m');
497
+ console.log(' \x1b[38;5;39m██╔════╝\x1b[38;5;75m██╔═══██╗\x1b[38;5;111m██╔══██╗\x1b[38;5;147m██╔════╝\x1b[38;5;183m╚██╗\x1b[38;5;219m██╔╝\x1b[0m');
498
+ console.log(' \x1b[38;5;45m██║ \x1b[38;5;81m██║ ██║\x1b[38;5;117m██║ ██║\x1b[38;5;153m█████╗ \x1b[38;5;189m ╚███╔╝ \x1b[0m');
499
+ console.log(' \x1b[38;5;75m██║ \x1b[38;5;111m██║ ██║\x1b[38;5;147m██║ ██║\x1b[38;5;183m██╔══╝ \x1b[38;5;219m ██╔██╗ \x1b[0m');
500
+ console.log(' \x1b[38;5;111m╚██████╗\x1b[38;5;147m╚██████╔╝\x1b[38;5;183m██████╔╝\x1b[38;5;219m███████╗\x1b[38;5;225m██╔╝ ██╗\x1b[0m');
501
+ console.log(' \x1b[38;5;147m╚═════╝ \x1b[38;5;183m╚═════╝ \x1b[38;5;219m╚═════╝ \x1b[38;5;225m╚══════╝\x1b[38;5;231m╚═╝ ╚═╝\x1b[0m');
502
+ log('\n pumpkinai Codex config Tool', 'magenta');
504
503
  log(' ' + '='.repeat(44), 'cyan');
505
504
  log('', 'reset');
506
505