coding-tool-x 3.5.5 → 3.5.6
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/README.md +8 -4
- package/dist/web/assets/{Analytics-gvYu5sCM.js → Analytics-CRNCHeui.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-CPlH8Ehd.js → ConfigTemplates-C0erJdo2.js} +1 -1
- package/dist/web/assets/{Home-B-qbu3uk.js → Home-CL5z6Q4d.js} +1 -1
- package/dist/web/assets/{PluginManager-B2tQ_YUq.js → PluginManager-hDx0XMO_.js} +1 -1
- package/dist/web/assets/{ProjectList-kDadoXXs.js → ProjectList-BNsz96av.js} +1 -1
- package/dist/web/assets/{SessionList-eLgITwTV.js → SessionList-CG1UhFo3.js} +1 -1
- package/dist/web/assets/{SkillManager-B7zEB5Op.js → SkillManager-D6Vwpajh.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-C-RzB3ud.js → WorkspaceManager-C3TjeOPy.js} +1 -1
- package/dist/web/assets/{icons-DlxD2wZJ.js → icons-CQuif85v.js} +1 -1
- package/dist/web/assets/index-GuER-BmS.js +2 -0
- package/dist/web/assets/{index-BHeh2z0i.css → index-VGAxnLqi.css} +1 -1
- package/dist/web/index.html +3 -3
- package/package.json +1 -1
- package/src/commands/stats.js +41 -4
- package/src/index.js +1 -0
- package/src/server/api/codex-sessions.js +6 -3
- package/src/server/api/dashboard.js +25 -1
- package/src/server/api/gemini-sessions.js +6 -3
- package/src/server/api/hooks.js +17 -1
- package/src/server/api/opencode-sessions.js +6 -3
- package/src/server/api/plugins.js +24 -33
- package/src/server/api/sessions.js +6 -3
- package/src/server/index.js +6 -4
- package/src/server/services/codex-sessions.js +107 -9
- package/src/server/services/network-access.js +14 -0
- package/src/server/services/notification-hooks.js +175 -16
- package/src/server/services/plugins-service.js +502 -44
- package/src/server/services/session-launch-command.js +81 -0
- package/src/server/services/sessions.js +103 -33
- package/src/server/websocket-server.js +25 -1
- package/dist/web/assets/index-DG00t-zy.js +0 -2
|
@@ -5,6 +5,7 @@ const http = require('http');
|
|
|
5
5
|
const https = require('https');
|
|
6
6
|
const { execSync, execFileSync } = require('child_process');
|
|
7
7
|
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
8
|
+
const { loadConfig } = require('../../config/loader');
|
|
8
9
|
const { loadUIConfig, saveUIConfig } = require('./ui-config');
|
|
9
10
|
const codexSettingsManager = require('./codex-settings-manager');
|
|
10
11
|
const geminiSettingsManager = require('./gemini-settings-manager');
|
|
@@ -57,6 +58,13 @@ function getFeishuConfig() {
|
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
function getBrowserNotificationConfig() {
|
|
62
|
+
const uiConfig = loadUIConfig();
|
|
63
|
+
return {
|
|
64
|
+
enabled: uiConfig.browserNotification?.enabled === true
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
60
68
|
function applyClaudeDisablePreference(uiConfig = {}, claudeEnabled) {
|
|
61
69
|
const nextConfig = (uiConfig && typeof uiConfig === 'object') ? { ...uiConfig } : {};
|
|
62
70
|
if (claudeEnabled) {
|
|
@@ -67,7 +75,7 @@ function applyClaudeDisablePreference(uiConfig = {}, claudeEnabled) {
|
|
|
67
75
|
return nextConfig;
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
function saveNotificationUiConfig(feishu = {}, claudeEnabled) {
|
|
78
|
+
function saveNotificationUiConfig(feishu = {}, browser = {}, claudeEnabled) {
|
|
71
79
|
let uiConfig = loadUIConfig();
|
|
72
80
|
if (typeof claudeEnabled === 'boolean') {
|
|
73
81
|
uiConfig = applyClaudeDisablePreference(uiConfig, claudeEnabled);
|
|
@@ -76,6 +84,9 @@ function saveNotificationUiConfig(feishu = {}, claudeEnabled) {
|
|
|
76
84
|
enabled: feishu.enabled === true,
|
|
77
85
|
webhookUrl: feishu.webhookUrl || ''
|
|
78
86
|
};
|
|
87
|
+
uiConfig.browserNotification = {
|
|
88
|
+
enabled: browser.enabled === true
|
|
89
|
+
};
|
|
79
90
|
saveUIConfig(uiConfig);
|
|
80
91
|
}
|
|
81
92
|
|
|
@@ -115,6 +126,84 @@ function removeNotifyScript() {
|
|
|
115
126
|
}
|
|
116
127
|
}
|
|
117
128
|
|
|
129
|
+
function getNotificationSourceLabel(source = 'claude') {
|
|
130
|
+
switch (String(source || '').trim().toLowerCase()) {
|
|
131
|
+
case 'codex':
|
|
132
|
+
return 'Codex CLI';
|
|
133
|
+
case 'gemini':
|
|
134
|
+
return 'Gemini CLI';
|
|
135
|
+
case 'opencode':
|
|
136
|
+
return 'OpenCode';
|
|
137
|
+
default:
|
|
138
|
+
return 'Claude Code';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function resolveFallbackNotificationMessage(source = 'claude', eventType = '') {
|
|
143
|
+
const normalizedSource = String(source || '').trim().toLowerCase();
|
|
144
|
+
const normalizedEventType = String(eventType || '').trim();
|
|
145
|
+
|
|
146
|
+
if (normalizedSource === 'codex') {
|
|
147
|
+
if (normalizedEventType === 'agent-turn-complete') {
|
|
148
|
+
return 'Codex CLI 回合已完成 | 等待交互';
|
|
149
|
+
}
|
|
150
|
+
return 'Codex CLI 已返回结果 | 等待交互';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (normalizedSource === 'gemini') {
|
|
154
|
+
return 'Gemini CLI 回合已完成 | 等待交互';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (normalizedSource === 'opencode') {
|
|
158
|
+
if (normalizedEventType === 'session.error') {
|
|
159
|
+
return 'OpenCode 会话异常,请检查日志';
|
|
160
|
+
}
|
|
161
|
+
return 'OpenCode 响应已完成 | 等待交互';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return 'Claude Code 任务已完成 | 等待交互';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function buildBrowserNotificationEndpoint() {
|
|
168
|
+
try {
|
|
169
|
+
const config = loadConfig();
|
|
170
|
+
const port = Number(config?.ports?.webUI);
|
|
171
|
+
const resolvedPort = Number.isFinite(port) && port > 0 ? port : 19999;
|
|
172
|
+
return `http://127.0.0.1:${resolvedPort}/api/hooks/browser-event`;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
return 'http://127.0.0.1:19999/api/hooks/browser-event';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildBrowserNotificationPayload(input = {}) {
|
|
179
|
+
const rawSource = String(input?.source || '').trim().toLowerCase();
|
|
180
|
+
const source = ['claude', 'codex', 'gemini', 'opencode'].includes(rawSource) ? rawSource : 'claude';
|
|
181
|
+
const eventType = String(input?.eventType || input?.event_type || '').trim();
|
|
182
|
+
const rawMessage = String(input?.message || '').trim();
|
|
183
|
+
const timestamp = Number(input?.timestamp);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
title: String(input?.title || '').trim() || 'Coding Tool',
|
|
187
|
+
source,
|
|
188
|
+
sourceLabel: getNotificationSourceLabel(source),
|
|
189
|
+
mode: normalizeType(input?.mode),
|
|
190
|
+
eventType,
|
|
191
|
+
message: rawMessage || resolveFallbackNotificationMessage(source, eventType),
|
|
192
|
+
timestamp: Number.isFinite(timestamp) ? timestamp : Date.now()
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function emitBrowserNotificationEvent(input = {}) {
|
|
197
|
+
const browser = getBrowserNotificationConfig();
|
|
198
|
+
if (!browser.enabled) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const { broadcastBrowserNotification } = require('../websocket-server');
|
|
203
|
+
broadcastBrowserNotification(buildBrowserNotificationPayload(input));
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
118
207
|
function parseManagedType(input) {
|
|
119
208
|
const value = String(input || '');
|
|
120
209
|
const matches = [
|
|
@@ -433,8 +522,10 @@ function runWindowsPowerShellCommand(command) {
|
|
|
433
522
|
});
|
|
434
523
|
}
|
|
435
524
|
|
|
436
|
-
function generateNotifyScript(feishu = {}) {
|
|
525
|
+
function generateNotifyScript(feishu = {}, browser = {}) {
|
|
437
526
|
const feishuEnabled = feishu.enabled === true && !!feishu.webhookUrl;
|
|
527
|
+
const browserEnabled = browser.enabled === true;
|
|
528
|
+
const browserEndpoint = browserEnabled ? buildBrowserNotificationEndpoint() : '';
|
|
438
529
|
|
|
439
530
|
return `#!/usr/bin/env node
|
|
440
531
|
// Coding Tool 通知脚本 - 自动生成,请勿手动修改
|
|
@@ -446,6 +537,8 @@ const { execSync, execFileSync } = require('child_process')
|
|
|
446
537
|
|
|
447
538
|
const FEISHU_ENABLED = ${feishuEnabled ? 'true' : 'false'}
|
|
448
539
|
const FEISHU_WEBHOOK_URL = ${JSON.stringify(feishuEnabled ? feishu.webhookUrl : '')}
|
|
540
|
+
const BROWSER_ENABLED = ${browserEnabled ? 'true' : 'false'}
|
|
541
|
+
const BROWSER_ENDPOINT = ${JSON.stringify(browserEndpoint)}
|
|
449
542
|
|
|
450
543
|
function readArg(name) {
|
|
451
544
|
const prefix = \`\${name}=\`
|
|
@@ -483,21 +576,25 @@ function readOptionalPayload() {
|
|
|
483
576
|
return null
|
|
484
577
|
}
|
|
485
578
|
|
|
486
|
-
function
|
|
487
|
-
|
|
579
|
+
function resolveEventType(eventType, payload) {
|
|
580
|
+
return eventType || payload?.type || payload?.hook_event?.event_type || ''
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function resolveMessage(source, effectiveEventType) {
|
|
584
|
+
const normalizedSource = String(source || '').trim().toLowerCase()
|
|
488
585
|
|
|
489
|
-
if (
|
|
586
|
+
if (normalizedSource === 'codex') {
|
|
490
587
|
if (effectiveEventType === 'agent-turn-complete') {
|
|
491
588
|
return 'Codex CLI 回合已完成 | 等待交互'
|
|
492
589
|
}
|
|
493
590
|
return 'Codex CLI 已返回结果 | 等待交互'
|
|
494
591
|
}
|
|
495
592
|
|
|
496
|
-
if (
|
|
593
|
+
if (normalizedSource === 'gemini') {
|
|
497
594
|
return 'Gemini CLI 回合已完成 | 等待交互'
|
|
498
595
|
}
|
|
499
596
|
|
|
500
|
-
if (
|
|
597
|
+
if (normalizedSource === 'opencode') {
|
|
501
598
|
if (effectiveEventType === 'session.error') {
|
|
502
599
|
return 'OpenCode 会话异常,请检查日志'
|
|
503
600
|
}
|
|
@@ -628,15 +725,63 @@ function sendFeishu(message, source) {
|
|
|
628
725
|
})
|
|
629
726
|
}
|
|
630
727
|
|
|
728
|
+
function sendBrowserNotification(payload) {
|
|
729
|
+
if (!BROWSER_ENABLED || !BROWSER_ENDPOINT) {
|
|
730
|
+
return Promise.resolve()
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return new Promise((resolve) => {
|
|
734
|
+
try {
|
|
735
|
+
const urlObj = new URL(BROWSER_ENDPOINT)
|
|
736
|
+
const data = JSON.stringify(payload)
|
|
737
|
+
const options = {
|
|
738
|
+
hostname: urlObj.hostname,
|
|
739
|
+
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
740
|
+
path: urlObj.pathname + urlObj.search,
|
|
741
|
+
method: 'POST',
|
|
742
|
+
headers: {
|
|
743
|
+
'Content-Type': 'application/json',
|
|
744
|
+
'Content-Length': Buffer.byteLength(data)
|
|
745
|
+
},
|
|
746
|
+
timeout: 5000
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const requestModule = urlObj.protocol === 'https:' ? https : http
|
|
750
|
+
const request = requestModule.request(options, () => resolve())
|
|
751
|
+
request.on('error', () => resolve())
|
|
752
|
+
request.on('timeout', () => {
|
|
753
|
+
request.destroy()
|
|
754
|
+
resolve()
|
|
755
|
+
})
|
|
756
|
+
request.write(data)
|
|
757
|
+
request.end()
|
|
758
|
+
} catch (error) {
|
|
759
|
+
resolve()
|
|
760
|
+
}
|
|
761
|
+
})
|
|
762
|
+
}
|
|
763
|
+
|
|
631
764
|
(async () => {
|
|
632
765
|
const source = readArg('--source') || 'claude'
|
|
633
766
|
const mode = readArg('--mode') || readArg('--cc-notify-type') || 'notification'
|
|
634
767
|
const eventType = readArg('--event-type') || ''
|
|
635
768
|
const payload = readOptionalPayload()
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
769
|
+
const effectiveEventType = resolveEventType(eventType, payload)
|
|
770
|
+
const normalizedMode = mode === 'dialog' ? 'dialog' : 'notification'
|
|
771
|
+
const message = resolveMessage(source, effectiveEventType)
|
|
772
|
+
const timestamp = Date.now()
|
|
773
|
+
|
|
774
|
+
notify(normalizedMode, message)
|
|
775
|
+
await Promise.all([
|
|
776
|
+
sendFeishu(message, source),
|
|
777
|
+
sendBrowserNotification({
|
|
778
|
+
source,
|
|
779
|
+
mode: normalizedMode,
|
|
780
|
+
eventType: effectiveEventType,
|
|
781
|
+
message,
|
|
782
|
+
timestamp
|
|
783
|
+
})
|
|
784
|
+
])
|
|
640
785
|
})().catch(() => {
|
|
641
786
|
process.exit(0)
|
|
642
787
|
})
|
|
@@ -856,9 +1001,9 @@ function runWindowsPowerShellCommand(command) {
|
|
|
856
1001
|
`;
|
|
857
1002
|
}
|
|
858
1003
|
|
|
859
|
-
function writeNotifyScript(feishu = {}) {
|
|
1004
|
+
function writeNotifyScript(feishu = {}, browser = getBrowserNotificationConfig()) {
|
|
860
1005
|
ensureParentDir(PATHS.notifyHook);
|
|
861
|
-
fs.writeFileSync(PATHS.notifyHook, generateNotifyScript(feishu), { mode: 0o755 });
|
|
1006
|
+
fs.writeFileSync(PATHS.notifyHook, generateNotifyScript(feishu, browser), { mode: 0o755 });
|
|
862
1007
|
}
|
|
863
1008
|
|
|
864
1009
|
function getClaudeHookStatus() {
|
|
@@ -1167,6 +1312,7 @@ function getNotificationSettings() {
|
|
|
1167
1312
|
success: true,
|
|
1168
1313
|
platform: os.platform(),
|
|
1169
1314
|
feishu: getFeishuConfig(),
|
|
1315
|
+
browser: getBrowserNotificationConfig(),
|
|
1170
1316
|
platforms: {
|
|
1171
1317
|
claude: getClaudeHookStatus(),
|
|
1172
1318
|
codex: getCodexHookStatus(),
|
|
@@ -1185,6 +1331,7 @@ function normalizePlatformInput(platform = {}) {
|
|
|
1185
1331
|
|
|
1186
1332
|
function saveNotificationSettings(input = {}) {
|
|
1187
1333
|
const existingFeishu = getFeishuConfig();
|
|
1334
|
+
const existingBrowser = getBrowserNotificationConfig();
|
|
1188
1335
|
const requestedWebhookUrl = String(input?.feishu?.webhookUrl || '').trim();
|
|
1189
1336
|
const feishu = {
|
|
1190
1337
|
enabled: input?.feishu?.enabled === true,
|
|
@@ -1193,6 +1340,11 @@ function saveNotificationSettings(input = {}) {
|
|
|
1193
1340
|
if (feishu.enabled && feishu.webhookUrl) {
|
|
1194
1341
|
validateFeishuWebhookUrl(feishu.webhookUrl);
|
|
1195
1342
|
}
|
|
1343
|
+
const browser = {
|
|
1344
|
+
enabled: input?.browser !== undefined
|
|
1345
|
+
? input?.browser?.enabled === true
|
|
1346
|
+
: existingBrowser.enabled === true
|
|
1347
|
+
};
|
|
1196
1348
|
const platforms = {
|
|
1197
1349
|
claude: normalizePlatformInput(input?.platforms?.claude),
|
|
1198
1350
|
codex: normalizePlatformInput(input?.platforms?.codex),
|
|
@@ -1200,11 +1352,11 @@ function saveNotificationSettings(input = {}) {
|
|
|
1200
1352
|
opencode: normalizePlatformInput(input?.platforms?.opencode)
|
|
1201
1353
|
};
|
|
1202
1354
|
|
|
1203
|
-
saveNotificationUiConfig(feishu, platforms.claude.enabled);
|
|
1355
|
+
saveNotificationUiConfig(feishu, browser, platforms.claude.enabled);
|
|
1204
1356
|
|
|
1205
1357
|
const hasManagedPlatform = Object.values(platforms).some((platform) => platform.enabled);
|
|
1206
1358
|
if (hasManagedPlatform) {
|
|
1207
|
-
writeNotifyScript(feishu);
|
|
1359
|
+
writeNotifyScript(feishu, browser);
|
|
1208
1360
|
}
|
|
1209
1361
|
|
|
1210
1362
|
saveClaudeHook(platforms.claude.enabled, platforms.claude.type);
|
|
@@ -1347,6 +1499,9 @@ function buildLegacyClaudeSaveInput(input = {}, currentSettings = getNotificatio
|
|
|
1347
1499
|
: {
|
|
1348
1500
|
enabled: currentSettings?.feishu?.enabled === true,
|
|
1349
1501
|
webhookUrl: currentSettings?.feishu?.webhookUrl || ''
|
|
1502
|
+
},
|
|
1503
|
+
browser: {
|
|
1504
|
+
enabled: currentSettings?.browser?.enabled === true
|
|
1350
1505
|
}
|
|
1351
1506
|
};
|
|
1352
1507
|
}
|
|
@@ -1356,6 +1511,7 @@ function getLegacyClaudeHookSettings() {
|
|
|
1356
1511
|
success: true,
|
|
1357
1512
|
stopHook: parseStopHookStatus(readClaudeSettings()),
|
|
1358
1513
|
feishu: getFeishuConfig(),
|
|
1514
|
+
browser: getBrowserNotificationConfig(),
|
|
1359
1515
|
platform: os.platform()
|
|
1360
1516
|
};
|
|
1361
1517
|
}
|
|
@@ -1553,7 +1709,7 @@ function syncManagedNotificationAssets() {
|
|
|
1553
1709
|
const hasManagedPlatform = Object.values(settings?.platforms || {}).some((platform) => platform?.enabled === true);
|
|
1554
1710
|
|
|
1555
1711
|
if (hasManagedPlatform) {
|
|
1556
|
-
writeNotifyScript(settings.feishu || {});
|
|
1712
|
+
writeNotifyScript(settings.feishu || {}, settings.browser || {});
|
|
1557
1713
|
} else {
|
|
1558
1714
|
removeNotifyScript();
|
|
1559
1715
|
}
|
|
@@ -1575,6 +1731,7 @@ module.exports = {
|
|
|
1575
1731
|
getLegacyClaudeHookSettings,
|
|
1576
1732
|
saveNotificationSettings,
|
|
1577
1733
|
saveLegacyClaudeHookSettings,
|
|
1734
|
+
emitBrowserNotificationEvent,
|
|
1578
1735
|
testNotification,
|
|
1579
1736
|
initDefaultHooks,
|
|
1580
1737
|
syncManagedNotificationAssets,
|
|
@@ -1600,6 +1757,8 @@ module.exports = {
|
|
|
1600
1757
|
buildGeminiCommand,
|
|
1601
1758
|
buildStopHookCommand,
|
|
1602
1759
|
buildClaudeCommand,
|
|
1760
|
+
buildBrowserNotificationEndpoint,
|
|
1761
|
+
buildBrowserNotificationPayload,
|
|
1603
1762
|
buildOpenCodePluginContent,
|
|
1604
1763
|
getOpenCodeManagedPluginPath,
|
|
1605
1764
|
generateNotifyScript,
|