coding-tool-x 3.4.4 → 3.4.5

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 (26) hide show
  1. package/dist/web/assets/{Analytics-_Byi9M6y.js → Analytics-DFWyPf5C.js} +1 -1
  2. package/dist/web/assets/{ConfigTemplates-DIwosdtG.js → ConfigTemplates-BFE7hmKd.js} +1 -1
  3. package/dist/web/assets/{Home-DdNMuQ9c.js → Home-DZUuCrxk.js} +1 -1
  4. package/dist/web/assets/{PluginManager-iuY24cnW.js → PluginManager-WyGY2BQN.js} +1 -1
  5. package/dist/web/assets/{ProjectList-DSkMulzL.js → ProjectList-CBc0QawN.js} +1 -1
  6. package/dist/web/assets/{SessionList-B6pGquIr.js → SessionList-CdPR7QLq.js} +1 -1
  7. package/dist/web/assets/{SkillManager-CHtQX5r8.js → SkillManager-B5-DxQOS.js} +1 -1
  8. package/dist/web/assets/{WorkspaceManager-gNPs-VaI.js → WorkspaceManager-C7yqFjpi.js} +1 -1
  9. package/dist/web/assets/index-BDsmoSfO.js +2 -0
  10. package/dist/web/assets/{index-pMqqe9ei.css → index-C1pzEgmj.css} +1 -1
  11. package/dist/web/index.html +2 -2
  12. package/package.json +2 -2
  13. package/src/server/api/claude-hooks.js +1 -0
  14. package/src/server/api/plugins.js +161 -14
  15. package/src/server/api/skills.js +62 -7
  16. package/src/server/codex-proxy-server.js +10 -2
  17. package/src/server/gemini-proxy-server.js +10 -2
  18. package/src/server/opencode-proxy-server.js +10 -2
  19. package/src/server/proxy-server.js +10 -2
  20. package/src/server/services/codex-channels.js +64 -21
  21. package/src/server/services/codex-env-manager.js +44 -28
  22. package/src/server/services/plugins-service.js +1060 -235
  23. package/src/server/services/proxy-runtime.js +129 -5
  24. package/src/server/services/server-shutdown.js +79 -0
  25. package/src/server/services/skill-service.js +142 -17
  26. package/dist/web/assets/index-DGjGCo37.js +0 -2
@@ -5,27 +5,46 @@ const toml = require('toml');
5
5
  const tomlStringify = require('@iarna/toml').stringify;
6
6
  const { PATHS } = require('../../config/paths');
7
7
  const { getCodexDir } = require('./codex-config');
8
- const { isProxyConfig } = require('./codex-settings-manager');
8
+ const { isProxyConfig, readConfig } = require('./codex-settings-manager');
9
9
  const { clearNativeOAuth } = require('./native-oauth-adapters');
10
10
  const { syncCodexUserEnvironment } = require('./codex-env-manager');
11
11
  const BaseChannelService = require('./base/base-channel-service');
12
12
 
13
- const CODEX_PROXY_ENV_KEY = 'CC_PROXY_KEY';
13
+ const CODEX_MANAGED_ENV_KEY = 'CC_PROXY_KEY';
14
14
  const CODEX_PROXY_ENV_VALUE = 'PROXY_KEY';
15
15
 
16
16
  // ── Codex 特有工具函数 ──
17
17
 
18
- function buildManagedCodexEnvMap(channels = [], { includeProxyKey = false } = {}) {
19
- if (includeProxyKey) {
20
- return { [CODEX_PROXY_ENV_KEY]: CODEX_PROXY_ENV_VALUE };
18
+ function resolveCurrentManagedChannel(channels = []) {
19
+ const allChannels = Array.isArray(channels) ? channels : [];
20
+ let currentProvider = '';
21
+
22
+ try {
23
+ currentProvider = String(readConfig()?.model_provider || '').trim();
24
+ } catch (err) {
25
+ currentProvider = '';
21
26
  }
22
- const envMap = {};
23
- for (const ch of channels) {
24
- if (ch.enabled !== false && ch.envKey && ch.apiKey) {
25
- envMap[ch.envKey] = ch.apiKey;
27
+
28
+ if (currentProvider && currentProvider !== 'cc-proxy') {
29
+ const matched = allChannels.find(ch => ch.providerKey === currentProvider);
30
+ if (matched) {
31
+ return matched;
26
32
  }
27
33
  }
28
- return envMap;
34
+
35
+ return allChannels.find(ch => ch.enabled !== false) || null;
36
+ }
37
+
38
+ function buildManagedCodexEnvMap(channels = [], { includeProxyKey = false, activeChannel = null } = {}) {
39
+ if (includeProxyKey) {
40
+ return { [CODEX_MANAGED_ENV_KEY]: CODEX_PROXY_ENV_VALUE };
41
+ }
42
+
43
+ const targetChannel = activeChannel || resolveCurrentManagedChannel(channels);
44
+ if (targetChannel?.apiKey) {
45
+ return { [CODEX_MANAGED_ENV_KEY]: targetChannel.apiKey };
46
+ }
47
+ return {};
29
48
  }
30
49
 
31
50
  function syncAllChannelEnvVars() {
@@ -34,7 +53,8 @@ function syncAllChannelEnvVars() {
34
53
  const data = svc.loadChannels();
35
54
  const proxyRunning = isProxyConfig();
36
55
  const envMap = buildManagedCodexEnvMap(data.channels, {
37
- includeProxyKey: proxyRunning
56
+ includeProxyKey: proxyRunning,
57
+ activeChannel: proxyRunning ? null : resolveCurrentManagedChannel(data.channels)
38
58
  });
39
59
  syncCodexUserEnvironment(envMap, { replace: true });
40
60
  } catch (err) {
@@ -87,7 +107,7 @@ function writeCodexConfigForMultiChannel(channels) {
87
107
  name: ch.name,
88
108
  base_url: ch.baseUrl,
89
109
  wire_api: ch.wireApi || 'responses',
90
- env_key: ch.envKey,
110
+ env_key: CODEX_MANAGED_ENV_KEY,
91
111
  requires_openai_auth: ch.requiresOpenaiAuth !== false
92
112
  };
93
113
  if (ch.queryParams && Object.keys(ch.queryParams).length > 0) {
@@ -121,7 +141,7 @@ class CodexChannelService extends BaseChannelService {
121
141
  _applyDefaults(channel) {
122
142
  const ch = super._applyDefaults(channel);
123
143
  ch.providerKey = ch.providerKey || '';
124
- ch.envKey = ch.envKey || '';
144
+ ch.envKey = CODEX_MANAGED_ENV_KEY;
125
145
  ch.wireApi = ch.wireApi || 'responses';
126
146
  ch.model = ch.model || '';
127
147
  ch.speedTestModel = ch.speedTestModel || null;
@@ -143,19 +163,38 @@ class CodexChannelService extends BaseChannelService {
143
163
  }
144
164
 
145
165
  _onAfterCreate(_channel, _allChannels) {
166
+ if (_channel.enabled !== false && !isProxyConfig()) {
167
+ this._applyToNativeSettings(_channel);
168
+ return;
169
+ }
146
170
  syncAllChannelEnvVars();
147
- // 注意:不再自动写入 config.toml,只在开启代理控制时才同步
148
171
  }
149
172
 
150
- _onAfterUpdate(_old, _next, _allChannels) {
173
+ _onAfterUpdate(_old, _next, allChannels) {
174
+ if (!isProxyConfig()) {
175
+ if (_old.enabled === false && _next.enabled !== false) {
176
+ this._applyToNativeSettings(_next);
177
+ return;
178
+ }
179
+ const activeChannel = resolveCurrentManagedChannel(allChannels);
180
+ if (_next.enabled !== false && activeChannel?.id === _next.id) {
181
+ this._applyToNativeSettings(_next);
182
+ return;
183
+ }
184
+ }
151
185
  syncAllChannelEnvVars();
152
- // 注意:不再自动写入 config.toml,只在开启代理控制时才同步
153
186
  }
154
187
 
155
- _onAfterDelete(_channel, _allChannels) {
188
+ _onAfterDelete(_channel, allChannels) {
189
+ if (!isProxyConfig()) {
190
+ const activeChannel = resolveCurrentManagedChannel(allChannels);
191
+ if (activeChannel && activeChannel.enabled !== false) {
192
+ this._applyToNativeSettings(activeChannel);
193
+ return;
194
+ }
195
+ }
156
196
  clearNativeOAuth('codex');
157
197
  syncAllChannelEnvVars();
158
- // 注意:不再自动写入 config.toml,只在开启代理控制时才同步
159
198
  }
160
199
 
161
200
  _applyToNativeSettings(channel) {
@@ -184,7 +223,7 @@ class CodexChannelService extends BaseChannelService {
184
223
  name: channel.name,
185
224
  base_url: channel.baseUrl,
186
225
  wire_api: channel.wireApi || 'responses',
187
- env_key: channel.envKey,
226
+ env_key: CODEX_MANAGED_ENV_KEY,
188
227
  requires_openai_auth: channel.requiresOpenaiAuth !== false
189
228
  };
190
229
 
@@ -215,10 +254,9 @@ const service = getServiceInstance();
215
254
  function getChannels() { return service.getChannels(); }
216
255
  function getEnabledChannels() { return service.getEnabledChannels(); }
217
256
  function createChannel(name, providerKey, baseUrl, apiKey, wireApi, extraConfig = {}) {
218
- const envKey = extraConfig.envKey || `${providerKey.toUpperCase()}_API_KEY`;
219
257
  return service.createChannel({
220
258
  name, providerKey, baseUrl, apiKey, wireApi,
221
- envKey,
259
+ envKey: CODEX_MANAGED_ENV_KEY,
222
260
  ...extraConfig,
223
261
  });
224
262
  }
@@ -251,4 +289,9 @@ module.exports = {
251
289
  applyChannelToSettings,
252
290
  getEffectiveApiKey,
253
291
  disableAllChannels,
292
+ _test: {
293
+ buildManagedCodexEnvMap,
294
+ CODEX_MANAGED_ENV_KEY,
295
+ resolveCurrentManagedChannel
296
+ }
254
297
  };
@@ -32,6 +32,40 @@ function powershellQuote(value) {
32
32
  return `'${String(value).replace(/'/g, "''")}'`;
33
33
  }
34
34
 
35
+ function buildWindowsSettingChangeScript() {
36
+ return [
37
+ 'Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"',
38
+ '[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]',
39
+ 'public static extern IntPtr SendMessageTimeout(',
40
+ ' IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,',
41
+ ' uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);',
42
+ '"@',
43
+ '$HWND_BROADCAST = [IntPtr]0xffff',
44
+ '$WM_SETTINGCHANGE = 0x1a',
45
+ '$SMTO_ABORTIFHUNG = 0x0002',
46
+ '$result = [UIntPtr]::Zero',
47
+ '[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE,',
48
+ ' [UIntPtr]::Zero, "Environment", $SMTO_ABORTIFHUNG, 5000, [ref]$result) | Out-Null'
49
+ ].join('\n');
50
+ }
51
+
52
+ function buildWindowsEnvBatchScript(operations = [], { includeSettingChangeBroadcast = true } = {}) {
53
+ const normalizedOperations = Array.isArray(operations) ? operations.filter(Boolean) : [];
54
+ const lines = normalizedOperations.map((operation) => {
55
+ const key = powershellQuote(operation.key || '');
56
+ if (operation.remove) {
57
+ return `[Environment]::SetEnvironmentVariable(${key}, $null, 'User')`;
58
+ }
59
+ return `[Environment]::SetEnvironmentVariable(${key}, ${powershellQuote(operation.value || '')}, 'User')`;
60
+ });
61
+
62
+ if (includeSettingChangeBroadcast && lines.length > 0) {
63
+ lines.push(buildWindowsSettingChangeScript());
64
+ }
65
+
66
+ return lines.join('\n');
67
+ }
68
+
35
69
  function buildHomeRelativeShellPath(filePath, homeDir) {
36
70
  const normalizedHome = path.resolve(homeDir);
37
71
  const normalizedFilePath = path.resolve(filePath);
@@ -341,48 +375,28 @@ function runLaunchctlCommand(args, execSync) {
341
375
  }
342
376
 
343
377
  function broadcastWindowsSettingChange(execSync) {
344
- const script = [
345
- 'Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"',
346
- '[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]',
347
- 'public static extern IntPtr SendMessageTimeout(',
348
- ' IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,',
349
- ' uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);',
350
- '"@',
351
- '$HWND_BROADCAST = [IntPtr]0xffff',
352
- '$WM_SETTINGCHANGE = 0x1a',
353
- '$SMTO_ABORTIFHUNG = 0x0002',
354
- '$result = [UIntPtr]::Zero',
355
- '[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE,',
356
- ' [UIntPtr]::Zero, "Environment", $SMTO_ABORTIFHUNG, 5000, [ref]$result) | Out-Null'
357
- ].join('\n');
358
- runWindowsEnvCommand(script, execSync);
378
+ runWindowsEnvCommand(buildWindowsSettingChangeScript(), execSync);
359
379
  }
360
380
 
361
381
  function syncWindowsEnvironment(nextValues, previousState, options) {
362
382
  const { stateFilePath, execSync } = options;
363
383
  const nextKeys = Object.keys(nextValues).sort();
364
384
  const previousValues = previousState.values || {};
365
- let changed = false;
385
+ const operations = [];
366
386
 
367
387
  for (const [key, value] of Object.entries(nextValues)) {
368
388
  if (previousValues[key] === value) continue;
369
- setWindowsUserEnv(key, value, execSync);
370
- changed = true;
389
+ operations.push({ key, value });
371
390
  }
372
391
 
373
392
  for (const key of Object.keys(previousValues)) {
374
393
  if (Object.prototype.hasOwnProperty.call(nextValues, key)) continue;
375
- removeWindowsUserEnv(key, execSync);
376
- changed = true;
394
+ operations.push({ key, remove: true });
377
395
  }
378
396
 
379
- // 广播 WM_SETTINGCHANGE,通知已打开的应用(如 VSCode)刷新环境变量
397
+ const changed = operations.length > 0;
380
398
  if (changed) {
381
- try {
382
- broadcastWindowsSettingChange(execSync);
383
- } catch {
384
- // 广播失败不影响主流程,环境变量已写入注册表
385
- }
399
+ runWindowsEnvCommand(buildWindowsEnvBatchScript(operations), execSync);
386
400
  }
387
401
 
388
402
  if (nextKeys.length > 0) {
@@ -427,14 +441,14 @@ function runWindowsEnvCommand(script, execSync) {
427
441
 
428
442
  function setWindowsUserEnv(key, value, execSync) {
429
443
  runWindowsEnvCommand(
430
- `[Environment]::SetEnvironmentVariable(${powershellQuote(key)}, ${powershellQuote(value)}, 'User')`,
444
+ buildWindowsEnvBatchScript([{ key, value }], { includeSettingChangeBroadcast: false }),
431
445
  execSync
432
446
  );
433
447
  }
434
448
 
435
449
  function removeWindowsUserEnv(key, execSync) {
436
450
  runWindowsEnvCommand(
437
- `[Environment]::SetEnvironmentVariable(${powershellQuote(key)}, $null, 'User')`,
451
+ buildWindowsEnvBatchScript([{ key, remove: true }], { includeSettingChangeBroadcast: false }),
438
452
  execSync
439
453
  );
440
454
  }
@@ -473,8 +487,10 @@ module.exports = {
473
487
  syncCodexUserEnvironment,
474
488
  _test: {
475
489
  broadcastWindowsSettingChange,
490
+ buildWindowsEnvBatchScript,
476
491
  buildHomeRelativeShellPath,
477
492
  buildNextEnvValues,
493
+ buildWindowsSettingChangeScript,
478
494
  buildSourceSnippet,
479
495
  getPosixProfileCandidates,
480
496
  readState,