coding-tool-x 3.3.6 → 3.3.8

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/web/assets/{Analytics-TtaduRqL.js → Analytics-DLpoDZ2M.js} +1 -1
  3. package/dist/web/assets/{ConfigTemplates-BP2lLBMN.js → ConfigTemplates-D_hRb55W.js} +1 -1
  4. package/dist/web/assets/Home-BMoFdAwy.css +1 -0
  5. package/dist/web/assets/Home-DNwp-0J-.js +1 -0
  6. package/dist/web/assets/{PluginManager-HmISlyMK.js → PluginManager-JXsyym1s.js} +1 -1
  7. package/dist/web/assets/{ProjectList-DoN8Hjbu.js → ProjectList-DZWSeb-q.js} +1 -1
  8. package/dist/web/assets/{SessionList-Da8BYzNi.js → SessionList-Cs624DR3.js} +1 -1
  9. package/dist/web/assets/{SkillManager-DqLAXh9o.js → SkillManager-bEliz7qz.js} +1 -1
  10. package/dist/web/assets/{WorkspaceManager-B_TxOgPW.js → WorkspaceManager-J3RecFGn.js} +1 -1
  11. package/dist/web/assets/{icons-B29onFfZ.js → icons-Cuc23WS7.js} +1 -1
  12. package/dist/web/assets/index-BXeSvAwU.js +2 -0
  13. package/dist/web/assets/index-DWAC3Tdv.css +1 -0
  14. package/dist/web/index.html +3 -3
  15. package/package.json +3 -2
  16. package/src/commands/daemon.js +44 -6
  17. package/src/commands/toggle-proxy.js +100 -5
  18. package/src/config/default.js +1 -1
  19. package/src/config/model-metadata.js +2 -2
  20. package/src/config/model-metadata.json +7 -2
  21. package/src/config/paths.js +102 -19
  22. package/src/server/api/channels.js +9 -0
  23. package/src/server/api/codex-channels.js +9 -0
  24. package/src/server/api/codex-proxy.js +22 -11
  25. package/src/server/api/gemini-proxy.js +22 -11
  26. package/src/server/api/mcp.js +26 -4
  27. package/src/server/api/oauth-credentials.js +163 -0
  28. package/src/server/api/opencode-proxy.js +22 -10
  29. package/src/server/api/plugins.js +3 -1
  30. package/src/server/api/proxy.js +39 -44
  31. package/src/server/api/skills.js +91 -13
  32. package/src/server/codex-proxy-server.js +1 -11
  33. package/src/server/index.js +26 -2
  34. package/src/server/services/channels.js +18 -22
  35. package/src/server/services/codex-channels.js +124 -175
  36. package/src/server/services/codex-config.js +2 -5
  37. package/src/server/services/codex-settings-manager.js +12 -348
  38. package/src/server/services/config-export-service.js +572 -117
  39. package/src/server/services/gemini-channels.js +11 -9
  40. package/src/server/services/mcp-client.js +70 -13
  41. package/src/server/services/mcp-service.js +74 -29
  42. package/src/server/services/model-detector.js +1 -0
  43. package/src/server/services/native-keychain.js +243 -0
  44. package/src/server/services/native-oauth-adapters.js +890 -0
  45. package/src/server/services/oauth-credentials-service.js +786 -0
  46. package/src/server/services/oauth-utils.js +49 -0
  47. package/src/server/services/opencode-channels.js +13 -9
  48. package/src/server/services/opencode-settings-manager.js +169 -16
  49. package/src/server/services/plugins-service.js +22 -1
  50. package/src/server/services/settings-manager.js +13 -0
  51. package/src/server/services/skill-service.js +712 -332
  52. package/src/utils/port-helper.js +87 -2
  53. package/dist/web/assets/Home-BsSioaaB.css +0 -1
  54. package/dist/web/assets/Home-CbbyopS-.js +0 -1
  55. package/dist/web/assets/index-By3mDEvx.js +0 -2
  56. package/dist/web/assets/index-CsWInMQV.css +0 -1
@@ -3,26 +3,27 @@ const path = require('path');
3
3
  const os = require('os');
4
4
  const toml = require('toml');
5
5
  const tomlStringify = require('@iarna/toml').stringify;
6
- const { resolvePreferredHomeDir, isWindowsLikePlatform } = require('../../utils/home-dir');
6
+ const { resolvePreferredHomeDir } = require('../../utils/home-dir');
7
+ const { NATIVE_PATHS } = require('../../config/paths');
7
8
 
8
9
  const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
9
10
 
10
11
  // Codex 配置文件路径
11
12
  function getConfigPath() {
12
- return path.join(HOME_DIR, '.codex', 'config.toml');
13
+ return NATIVE_PATHS.codex.config;
13
14
  }
14
15
 
15
16
  function getAuthPath() {
16
- return path.join(HOME_DIR, '.codex', 'auth.json');
17
+ return NATIVE_PATHS.codex.auth;
17
18
  }
18
19
 
19
20
  // 备份文件路径
20
21
  function getConfigBackupPath() {
21
- return path.join(HOME_DIR, '.codex', 'config.toml.cc-tool-backup');
22
+ return NATIVE_PATHS.codex.configBackup;
22
23
  }
23
24
 
24
25
  function getAuthBackupPath() {
25
- return path.join(HOME_DIR, '.codex', 'auth.json.cc-tool-backup');
26
+ return NATIVE_PATHS.codex.authBackup;
26
27
  }
27
28
 
28
29
  // 检查配置文件是否存在
@@ -39,172 +40,6 @@ function hasBackup() {
39
40
  return fs.existsSync(getConfigBackupPath()) || fs.existsSync(getAuthBackupPath());
40
41
  }
41
42
 
42
- const INVALID_ENV_NAME_PATTERN = /[\r\n]/;
43
- const SHELL_MARKER_PREFIX = '# Added by Coding-Tool for Codex';
44
-
45
- function normalizeEnvName(envName) {
46
- const normalized = String(envName || '').trim();
47
- if (!normalized || INVALID_ENV_NAME_PATTERN.test(normalized)) {
48
- return null;
49
- }
50
- return normalized;
51
- }
52
-
53
- function ensureParentDir(filePath) {
54
- const dirPath = path.dirname(filePath);
55
- if (!fs.existsSync(dirPath)) {
56
- fs.mkdirSync(dirPath, { recursive: true });
57
- }
58
- }
59
-
60
- function writeFileAtomic(filePath, content) {
61
- ensureParentDir(filePath);
62
- const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
63
-
64
- try {
65
- fs.writeFileSync(tempPath, content, 'utf8');
66
- fs.renameSync(tempPath, filePath);
67
- } finally {
68
- if (fs.existsSync(tempPath)) {
69
- try {
70
- fs.unlinkSync(tempPath);
71
- } catch (cleanupErr) {
72
- // ignore cleanup errors
73
- }
74
- }
75
- }
76
- }
77
-
78
- function normalizeHomePath(filePath) {
79
- const normalizedPath = String(filePath || '').replace(/\\/g, '/');
80
- const normalizedHome = HOME_DIR.replace(/\\/g, '/');
81
- if (normalizedPath.startsWith(normalizedHome)) {
82
- return `~${normalizedPath.slice(normalizedHome.length)}`;
83
- }
84
- return filePath;
85
- }
86
-
87
- function compactBlankLines(lines) {
88
- const compacted = [];
89
- let previousIsBlank = false;
90
-
91
- for (const line of lines) {
92
- const isBlank = line.trim() === '';
93
- if (isBlank) {
94
- if (!previousIsBlank) {
95
- compacted.push('');
96
- }
97
- previousIsBlank = true;
98
- continue;
99
- }
100
-
101
- compacted.push(line);
102
- previousIsBlank = false;
103
- }
104
-
105
- while (compacted.length > 0 && compacted[compacted.length - 1].trim() === '') {
106
- compacted.pop();
107
- }
108
-
109
- return compacted;
110
- }
111
-
112
- function isPowerShellProfile(filePath) {
113
- return String(filePath || '').toLowerCase().endsWith('.ps1');
114
- }
115
-
116
- function getShellConfigCandidates() {
117
- const homeDir = HOME_DIR;
118
- const shell = String(process.env.SHELL || '').toLowerCase();
119
- const candidates = [];
120
-
121
- if (isWindowsLikePlatform(process.platform, process.env)) {
122
- const oneDriveDir = process.env.OneDrive || process.env.ONEDRIVE || '';
123
-
124
- if (shell.includes('zsh')) {
125
- candidates.push(path.join(homeDir, '.zshrc'));
126
- }
127
-
128
- if (shell.includes('bash')) {
129
- candidates.push(path.join(homeDir, '.bashrc'));
130
- candidates.push(path.join(homeDir, '.bash_profile'));
131
- }
132
-
133
- candidates.push(path.join(homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'));
134
- candidates.push(path.join(homeDir, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'));
135
- if (oneDriveDir) {
136
- candidates.push(path.join(oneDriveDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'));
137
- candidates.push(path.join(oneDriveDir, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'));
138
- }
139
- candidates.push(path.join(homeDir, '.bashrc'));
140
- candidates.push(path.join(homeDir, '.profile'));
141
- } else if (shell.includes('zsh')) {
142
- candidates.push(path.join(homeDir, '.zshrc'));
143
- candidates.push(path.join(homeDir, '.zprofile'));
144
- candidates.push(path.join(homeDir, '.profile'));
145
- } else if (shell.includes('bash')) {
146
- if (process.platform === 'darwin') {
147
- candidates.push(path.join(homeDir, '.bash_profile'));
148
- candidates.push(path.join(homeDir, '.bashrc'));
149
- } else {
150
- candidates.push(path.join(homeDir, '.bashrc'));
151
- candidates.push(path.join(homeDir, '.bash_profile'));
152
- }
153
- candidates.push(path.join(homeDir, '.profile'));
154
- } else {
155
- candidates.push(path.join(homeDir, '.zshrc'));
156
- candidates.push(path.join(homeDir, '.bashrc'));
157
- candidates.push(path.join(homeDir, '.bash_profile'));
158
- candidates.push(path.join(homeDir, '.profile'));
159
- }
160
-
161
- return [...new Set(candidates)];
162
- }
163
-
164
- function getShellReloadCommand(configPath) {
165
- if (!configPath) {
166
- return isWindowsLikePlatform(process.platform, process.env) ? '重启终端' : 'source ~/.zshrc';
167
- }
168
-
169
- const displayPath = normalizeHomePath(configPath);
170
- const normalized = String(displayPath || '').replace(/\\/g, '/').toLowerCase();
171
-
172
- if (normalized.endsWith('microsoft.powershell_profile.ps1')) {
173
- return '. $PROFILE';
174
- }
175
- if (normalized.endsWith('/.zshrc')) {
176
- return 'source ~/.zshrc';
177
- }
178
- if (normalized.endsWith('/.bash_profile')) {
179
- return 'source ~/.bash_profile';
180
- }
181
- if (normalized.endsWith('/.bashrc')) {
182
- return 'source ~/.bashrc';
183
- }
184
- if (normalized.endsWith('/.profile')) {
185
- return 'source ~/.profile';
186
- }
187
-
188
- if (isWindowsLikePlatform(process.platform, process.env)) {
189
- return '. $PROFILE';
190
- }
191
-
192
- return `source ${displayPath}`;
193
- }
194
-
195
- function escapeShellValue(value) {
196
- return String(value ?? '')
197
- .replace(/\\/g, '\\\\')
198
- .replace(/"/g, '\\"')
199
- .replace(/\$/g, '\\$')
200
- .replace(/`/g, '\\`');
201
- }
202
-
203
- function escapePowerShellValue(value) {
204
- return String(value ?? '')
205
- .replace(/`/g, '``')
206
- .replace(/"/g, '`"');
207
- }
208
43
 
209
44
  // 读取 config.toml
210
45
  function readConfig() {
@@ -359,10 +194,7 @@ function restoreSettings() {
359
194
  fs.unlinkSync(getAuthBackupPath());
360
195
  }
361
196
 
362
- // 清理 shell 配置文件中的环境变量(可选,不影响恢复结果)
363
- removeEnvFromShell('CC_PROXY_KEY');
364
-
365
- // 同步删除当前进程的环境变量,使恢复立即生效(无需新开终端)
197
+ // auth.json 已恢复,同步删除当前进程的环境变量
366
198
  delete process.env.CC_PROXY_KEY;
367
199
 
368
200
  console.log('Codex settings restored from backup');
@@ -372,161 +204,6 @@ function restoreSettings() {
372
204
  }
373
205
  }
374
206
 
375
- // 获取用户的 shell 配置文件路径
376
- function getShellConfigPath() {
377
- const candidates = getShellConfigCandidates();
378
- const existing = candidates.find(filePath => fs.existsSync(filePath));
379
- return existing || candidates[0];
380
- }
381
-
382
- // 注入环境变量到 shell 配置文件
383
- function injectEnvToShell(envName, envValue) {
384
- const normalizedEnvName = normalizeEnvName(envName);
385
- if (!normalizedEnvName) {
386
- return {
387
- success: false,
388
- error: `Invalid environment variable name: ${envName}`,
389
- isFirstTime: false
390
- };
391
- }
392
-
393
- const configPath = getShellConfigPath();
394
- const marker = `${SHELL_MARKER_PREFIX} [${normalizedEnvName}]`;
395
- const usePowerShell = isPowerShellProfile(configPath);
396
- const exportLine = usePowerShell
397
- ? `$env:${normalizedEnvName} = "${escapePowerShellValue(envValue)}"`
398
- : `export ${normalizedEnvName}="${escapeShellValue(envValue)}"`;
399
-
400
- try {
401
- let content = '';
402
- if (fs.existsSync(configPath)) {
403
- content = fs.readFileSync(configPath, 'utf8');
404
- }
405
-
406
- const envKeyEscaped = String(normalizedEnvName).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
407
- const envLineRegex = usePowerShell
408
- ? new RegExp(`^\\s*\\$env:${envKeyEscaped}\\s*=`, 'i')
409
- : new RegExp(`^\\s*(?:export\\s+)?${envKeyEscaped}=`);
410
-
411
- const originalLines = content ? content.split(/\r?\n/) : [];
412
- const cleanedLines = [];
413
- let existed = false;
414
-
415
- for (let i = 0; i < originalLines.length; i++) {
416
- const currentLine = originalLines[i];
417
- const trimmedLine = currentLine.trim();
418
-
419
- if (trimmedLine === marker) {
420
- const nextLine = originalLines[i + 1] || '';
421
- if (envLineRegex.test(nextLine.trim())) {
422
- i += 1;
423
- }
424
- existed = true;
425
- continue;
426
- }
427
-
428
- if (envLineRegex.test(trimmedLine)) {
429
- existed = true;
430
- continue;
431
- }
432
-
433
- cleanedLines.push(currentLine);
434
- }
435
-
436
- while (cleanedLines.length > 0 && cleanedLines[cleanedLines.length - 1].trim() === '') {
437
- cleanedLines.pop();
438
- }
439
-
440
- if (cleanedLines.length > 0) {
441
- cleanedLines.push('');
442
- }
443
-
444
- cleanedLines.push(marker, exportLine);
445
-
446
- const nextContent = `${cleanedLines.join('\n')}\n`;
447
- if (nextContent !== content) {
448
- writeFileAtomic(configPath, nextContent);
449
- }
450
-
451
- // 同步更新当前进程的环境变量,使变更立即生效(无需新开终端)
452
- process.env[normalizedEnvName] = String(envValue ?? '');
453
-
454
- return { success: true, path: configPath, isFirstTime: !existed };
455
- } catch (err) {
456
- // 不抛出错误,只是警告,因为这不是致命问题
457
- console.warn(`[Codex] Failed to inject env to shell config: ${err.message}`);
458
- return { success: false, error: err.message, isFirstTime: false };
459
- }
460
- }
461
-
462
- // 从 shell 配置文件移除环境变量
463
- function removeEnvFromShell(envName) {
464
- const normalizedEnvName = normalizeEnvName(envName);
465
- if (!normalizedEnvName) {
466
- return {
467
- success: false,
468
- error: `Invalid environment variable name: ${envName}`
469
- };
470
- }
471
-
472
- const configPath = getShellConfigPath();
473
-
474
- try {
475
- if (!fs.existsSync(configPath)) {
476
- return { success: true };
477
- }
478
-
479
- const content = fs.readFileSync(configPath, 'utf8');
480
- const usePowerShell = isPowerShellProfile(configPath);
481
- const marker = `${SHELL_MARKER_PREFIX} [${normalizedEnvName}]`;
482
- const envKeyEscaped = String(normalizedEnvName).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
483
- const envLineRegex = usePowerShell
484
- ? new RegExp(`^\\s*\\$env:${envKeyEscaped}\\s*=`, 'i')
485
- : new RegExp(`^\\s*(?:export\\s+)?${envKeyEscaped}=`);
486
-
487
- const originalLines = content ? content.split(/\r?\n/) : [];
488
- const cleanedLines = [];
489
- let changed = false;
490
-
491
- for (let i = 0; i < originalLines.length; i++) {
492
- const currentLine = originalLines[i];
493
- const trimmedLine = currentLine.trim();
494
-
495
- if (trimmedLine === marker) {
496
- const nextLine = originalLines[i + 1] || '';
497
- if (envLineRegex.test(nextLine.trim())) {
498
- i += 1;
499
- }
500
- changed = true;
501
- continue;
502
- }
503
-
504
- if (envLineRegex.test(trimmedLine)) {
505
- changed = true;
506
- continue;
507
- }
508
-
509
- cleanedLines.push(currentLine);
510
- }
511
-
512
- if (!changed) {
513
- return { success: true };
514
- }
515
-
516
- const normalized = compactBlankLines(cleanedLines);
517
- const nextContent = normalized.length > 0 ? `${normalized.join('\n')}\n` : '';
518
- writeFileAtomic(configPath, nextContent);
519
-
520
- // 同步删除当前进程的环境变量,使变更立即生效(无需新开终端)
521
- delete process.env[normalizedEnvName];
522
-
523
- return { success: true };
524
- } catch (err) {
525
- console.warn(`[Codex] Failed to remove env from shell config: ${err.message}`);
526
- return { success: false, error: err.message };
527
- }
528
- }
529
-
530
207
  // 设置代理配置
531
208
  function setProxyConfig(proxyPort) {
532
209
  try {
@@ -560,24 +237,15 @@ function setProxyConfig(proxyPort) {
560
237
  auth.CC_PROXY_KEY = 'PROXY_KEY';
561
238
  writeAuth(auth);
562
239
 
563
- // 注入环境变量到 shell 配置文件(解决某些系统环境变量优先级问题)
564
- const shellInjectResult = injectEnvToShell('CC_PROXY_KEY', 'PROXY_KEY');
565
-
566
- // 同步更新当前进程的环境变量,使代理立即生效(无需新开终端)
567
- process.env.CC_PROXY_KEY = 'PROXY_KEY';
568
-
569
- // 获取 shell 配置文件路径用于提示信息
570
- const shellConfigPath = shellInjectResult.path || getShellConfigPath();
571
- const sourceCommand = getShellReloadCommand(shellConfigPath);
572
-
240
+ // auth.json 已写入 CC_PROXY_KEY,Codex 优先读取 auth.json,无需注入 shell 配置文件
573
241
  console.log(`Codex settings updated to use proxy on port ${proxyPort}`);
574
242
  return {
575
243
  success: true,
576
244
  port: proxyPort,
577
- envInjected: shellInjectResult.success,
578
- isFirstTime: shellInjectResult.isFirstTime,
579
- shellConfigPath: shellConfigPath,
580
- sourceCommand: sourceCommand
245
+ envInjected: true,
246
+ isFirstTime: false,
247
+ shellConfigPath: null,
248
+ sourceCommand: null
581
249
  };
582
250
  } catch (err) {
583
251
  throw new Error('Failed to set proxy config: ' + err.message);
@@ -652,8 +320,4 @@ module.exports = {
652
320
  setProxyConfig,
653
321
  isProxyConfig,
654
322
  getCurrentProxyPort,
655
- // 导出环境变量注入函数供其他模块使用
656
- getShellConfigPath,
657
- injectEnvToShell,
658
- removeEnvFromShell
659
323
  };