coding-tool-x 3.4.8 → 3.4.10
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/package.json
CHANGED
|
@@ -103,7 +103,7 @@ function generateSystemNotificationCommand(type, platformOverride = platform) {
|
|
|
103
103
|
if (type === 'dialog') {
|
|
104
104
|
return `powershell -Command "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('Claude Code 任务已完成 | 等待交互', 'Coding Tool', 'OK', 'Information')" || ${buildWindowsPopupCommand()}`;
|
|
105
105
|
} else {
|
|
106
|
-
return
|
|
106
|
+
return buildWindowsPopupCommand();
|
|
107
107
|
}
|
|
108
108
|
} else {
|
|
109
109
|
// Linux
|
package/src/server/api/proxy.js
CHANGED
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
hasBackup,
|
|
12
12
|
readSettings
|
|
13
13
|
} = require('../services/settings-manager');
|
|
14
|
-
const { getAllChannels } = require('../services/channels');
|
|
14
|
+
const { getAllChannels, extractApiKeyFromHelper } = require('../services/channels');
|
|
15
15
|
const { clearNativeOAuth } = require('../services/native-oauth-adapters');
|
|
16
16
|
const { clearAllLogs } = require('../websocket-server');
|
|
17
17
|
const { PATHS, NATIVE_PATHS, ensureStorageDirMigrated } = require('../../config/paths');
|
|
@@ -101,10 +101,7 @@ function findActiveChannelFromSettings() {
|
|
|
101
101
|
|
|
102
102
|
// 如果 apiKey 仍为空,尝试从 apiKeyHelper 提取
|
|
103
103
|
if (!apiKey && settings?.apiKeyHelper) {
|
|
104
|
-
|
|
105
|
-
if (match && match[1]) {
|
|
106
|
-
apiKey = match[1];
|
|
107
|
-
}
|
|
104
|
+
apiKey = extractApiKeyFromHelper(settings.apiKeyHelper);
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
const channels = getAllChannels();
|
|
@@ -44,18 +44,22 @@ function extractApiKeyFromHelper(apiKeyHelper) {
|
|
|
44
44
|
return '';
|
|
45
45
|
}
|
|
46
46
|
const helper = apiKeyHelper.trim();
|
|
47
|
-
let match = helper.match(/^echo\s+["']([^"']+)["']$/);
|
|
47
|
+
let match = helper.match(/^echo\s+["']([^"']+)["']$/i);
|
|
48
48
|
if (match && match[1]) return match[1];
|
|
49
|
-
match = helper.match(/^
|
|
49
|
+
match = helper.match(/^echo\s+([^\s].*)$/i);
|
|
50
|
+
if (match && match[1]) return match[1].trim();
|
|
51
|
+
match = helper.match(/^cmd(?:\.exe)?\s*\/c\s+echo\s+([^\s].*)$/i);
|
|
52
|
+
if (match && match[1]) return match[1].trim();
|
|
53
|
+
match = helper.match(/^printf\s+["'][^"']*["']\s+["']([^"']+)["']$/i);
|
|
50
54
|
if (match && match[1]) return match[1];
|
|
51
55
|
return '';
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
function buildApiKeyHelperCommand() {
|
|
58
|
+
function buildApiKeyHelperCommand(value) {
|
|
55
59
|
if (isWindowsLikePlatform(process.platform, process.env)) {
|
|
56
|
-
return
|
|
60
|
+
return `cmd /c echo ${value}`;
|
|
57
61
|
}
|
|
58
|
-
return
|
|
62
|
+
return `echo '${value}'`;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
// ── Claude 原生设置写入 ──
|
|
@@ -114,7 +118,7 @@ function updateClaudeSettingsWithModelConfig(channel) {
|
|
|
114
118
|
delete settings.env;
|
|
115
119
|
}
|
|
116
120
|
|
|
117
|
-
settings.apiKeyHelper = buildApiKeyHelperCommand();
|
|
121
|
+
settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
|
|
118
122
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
119
123
|
}
|
|
120
124
|
|
|
@@ -147,7 +151,7 @@ function updateClaudeSettings(baseUrl, apiKey) {
|
|
|
147
151
|
delete settings.env;
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
settings.apiKeyHelper = buildApiKeyHelperCommand();
|
|
154
|
+
settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
|
|
151
155
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
152
156
|
}
|
|
153
157
|
|
|
@@ -326,4 +330,5 @@ module.exports = {
|
|
|
326
330
|
updateClaudeSettingsWithModelConfig,
|
|
327
331
|
getEffectiveApiKey,
|
|
328
332
|
disableAllChannels,
|
|
333
|
+
extractApiKeyFromHelper,
|
|
329
334
|
};
|
|
@@ -5,6 +5,9 @@ const { PATHS, HOME_DIR } = require('../../config/paths');
|
|
|
5
5
|
|
|
6
6
|
const PROFILE_MARKER_START = '# >>> coding-tool codex env >>>';
|
|
7
7
|
const PROFILE_MARKER_END = '# <<< coding-tool codex env <<<';
|
|
8
|
+
const WINDOWS_ENV_COMMAND_TIMEOUT_MS = 15000;
|
|
9
|
+
const WINDOWS_SETTING_CHANGE_TIMEOUT_MS = 1000;
|
|
10
|
+
const WINDOWS_SETTING_CHANGE_COMMAND_TIMEOUT_MS = 7000;
|
|
8
11
|
|
|
9
12
|
function defaultEnvFilePath(configDir) {
|
|
10
13
|
return path.join(configDir, 'codex-env.sh');
|
|
@@ -32,7 +35,7 @@ function powershellQuote(value) {
|
|
|
32
35
|
return `'${String(value).replace(/'/g, "''")}'`;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
function buildWindowsSettingChangeScript() {
|
|
38
|
+
function buildWindowsSettingChangeScript(timeoutMs = WINDOWS_SETTING_CHANGE_TIMEOUT_MS) {
|
|
36
39
|
return [
|
|
37
40
|
'Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"',
|
|
38
41
|
'[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]',
|
|
@@ -45,7 +48,7 @@ function buildWindowsSettingChangeScript() {
|
|
|
45
48
|
'$SMTO_ABORTIFHUNG = 0x0002',
|
|
46
49
|
'$result = [UIntPtr]::Zero',
|
|
47
50
|
'[Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE,',
|
|
48
|
-
|
|
51
|
+
` [UIntPtr]::Zero, "Environment", $SMTO_ABORTIFHUNG, ${timeoutMs}, [ref]$result) | Out-Null`
|
|
49
52
|
].join('\n');
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -375,7 +378,12 @@ function runLaunchctlCommand(args, execSync) {
|
|
|
375
378
|
}
|
|
376
379
|
|
|
377
380
|
function broadcastWindowsSettingChange(execSync) {
|
|
378
|
-
|
|
381
|
+
// WM_SETTINGCHANGE 只是帮助已运行的 GUI/终端尽快感知环境变化。
|
|
382
|
+
// 用户级环境变量已经写入注册表,即使这里超时,新开进程仍然能读到。
|
|
383
|
+
runWindowsEnvCommand(buildWindowsSettingChangeScript(), execSync, {
|
|
384
|
+
timeout: WINDOWS_SETTING_CHANGE_COMMAND_TIMEOUT_MS,
|
|
385
|
+
ignoreErrors: true
|
|
386
|
+
});
|
|
379
387
|
}
|
|
380
388
|
|
|
381
389
|
function syncWindowsEnvironment(nextValues, previousState, options) {
|
|
@@ -396,7 +404,12 @@ function syncWindowsEnvironment(nextValues, previousState, options) {
|
|
|
396
404
|
|
|
397
405
|
const changed = operations.length > 0;
|
|
398
406
|
if (changed) {
|
|
399
|
-
runWindowsEnvCommand(
|
|
407
|
+
runWindowsEnvCommand(
|
|
408
|
+
buildWindowsEnvBatchScript(operations, { includeSettingChangeBroadcast: false }),
|
|
409
|
+
execSync,
|
|
410
|
+
{ timeout: WINDOWS_ENV_COMMAND_TIMEOUT_MS }
|
|
411
|
+
);
|
|
412
|
+
broadcastWindowsSettingChange(execSync);
|
|
400
413
|
}
|
|
401
414
|
|
|
402
415
|
if (nextKeys.length > 0) {
|
|
@@ -421,21 +434,28 @@ function syncWindowsEnvironment(nextValues, previousState, options) {
|
|
|
421
434
|
};
|
|
422
435
|
}
|
|
423
436
|
|
|
424
|
-
function runWindowsEnvCommand(script, execSync) {
|
|
437
|
+
function runWindowsEnvCommand(script, execSync, options = {}) {
|
|
438
|
+
const timeout = Number(options.timeout) > 0
|
|
439
|
+
? Number(options.timeout)
|
|
440
|
+
: WINDOWS_ENV_COMMAND_TIMEOUT_MS;
|
|
441
|
+
const ignoreErrors = options.ignoreErrors === true;
|
|
425
442
|
const candidates = ['powershell', 'pwsh'];
|
|
426
443
|
let lastError = null;
|
|
427
444
|
for (const command of candidates) {
|
|
428
445
|
try {
|
|
429
446
|
execSync(command, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
430
447
|
stdio: ['ignore', 'ignore', 'ignore'],
|
|
431
|
-
timeout
|
|
448
|
+
timeout,
|
|
432
449
|
windowsHide: true
|
|
433
450
|
});
|
|
434
|
-
return;
|
|
451
|
+
return true;
|
|
435
452
|
} catch (error) {
|
|
436
453
|
lastError = error;
|
|
437
454
|
}
|
|
438
455
|
}
|
|
456
|
+
if (ignoreErrors) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
439
459
|
throw lastError || new Error('No PowerShell executable available');
|
|
440
460
|
}
|
|
441
461
|
|
|
@@ -348,20 +348,7 @@ function notify(mode, message) {
|
|
|
348
348
|
const command = 'powershell -NoProfile -Command ' + JSON.stringify(ps) + ' || ' + popupCommand
|
|
349
349
|
execSync(command, { stdio: 'ignore', windowsHide: true })
|
|
350
350
|
} else {
|
|
351
|
-
|
|
352
|
-
escapeForXml(title) + '</text><text>' + escapeForXml(message) +
|
|
353
|
-
'</text></binding></visual><audio src="ms-winsoundevent:Notification.Default"/></toast>'
|
|
354
|
-
const ps = 'try { ' +
|
|
355
|
-
'[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null; ' +
|
|
356
|
-
'[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null; ' +
|
|
357
|
-
'$xml = New-Object Windows.Data.Xml.Dom.XmlDocument; ' +
|
|
358
|
-
'$xml.LoadXml(\\'' + toastXml.replace(/'/g, "''") + '\\'); ' +
|
|
359
|
-
'$toast = [Windows.UI.Notifications.ToastNotification]::new($xml); ' +
|
|
360
|
-
"[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Coding Tool').Show($toast) " +
|
|
361
|
-
"} catch { $wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('" +
|
|
362
|
-
escapeForPowerShellSingleQuote(message) + "', 5, '" + escapeForPowerShellSingleQuote(title) + "', 0x40) }"
|
|
363
|
-
const command = 'powershell -NoProfile -Command ' + JSON.stringify(ps) + ' || ' + popupCommand
|
|
364
|
-
execSync(command, { stdio: 'ignore', windowsHide: true })
|
|
351
|
+
execSync(popupCommand, { stdio: 'ignore', windowsHide: true })
|
|
365
352
|
}
|
|
366
353
|
return
|
|
367
354
|
}
|
|
@@ -912,9 +899,7 @@ function generateSystemNotificationCommand(type, message, platformOverride = os.
|
|
|
912
899
|
if (normalizedType === 'dialog') {
|
|
913
900
|
return `powershell -NoProfile -Command "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('${escapeForPowerShellSingleQuote(message)}', '${escapeForPowerShellSingleQuote(title)}', 'OK', 'Information')" || ${popupCommand}`;
|
|
914
901
|
}
|
|
915
|
-
|
|
916
|
-
const toastXml = `<toast><visual><binding template="ToastGeneric"><text>${escapeForXml(title)}</text><text>${escapeForXml(message)}</text></binding></visual><audio src="ms-winsoundevent:Notification.Default"/></toast>`;
|
|
917
|
-
return `powershell -NoProfile -Command "try { [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null; [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null; $xml = New-Object Windows.Data.Xml.Dom.XmlDocument; $xml.LoadXml('${toastXml.replace(/'/g, "''")}'); $toast = [Windows.UI.Notifications.ToastNotification]::new($xml); [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Coding Tool').Show($toast) } catch { $wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('${escapeForPowerShellSingleQuote(message)}', 5, '${escapeForPowerShellSingleQuote(title)}', 0x40) }" || ${popupCommand}`;
|
|
902
|
+
return popupCommand;
|
|
918
903
|
}
|
|
919
904
|
|
|
920
905
|
if (normalizedType === 'dialog') {
|