coding-tool-x 3.5.6 → 3.5.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.
- package/README.md +17 -0
- package/bin/ctx.js +6 -1
- package/dist/web/assets/{Analytics-CRNCHeui.js → Analytics-BzoNzfbi.js} +2 -2
- package/dist/web/assets/Analytics-vQS5IWvs.css +1 -0
- package/dist/web/assets/{ConfigTemplates-C0erJdo2.js → ConfigTemplates-O4ikBt1o.js} +1 -1
- package/dist/web/assets/{Home-CL5z6Q4d.js → Home-BQjsnblU.js} +1 -1
- package/dist/web/assets/Home-qzk118Of.css +1 -0
- package/dist/web/assets/{PluginManager-hDx0XMO_.js → PluginManager-DS_DJnVc.js} +1 -1
- package/dist/web/assets/ProjectList-CqYDtsHx.js +1 -0
- package/dist/web/assets/ProjectList-GCC2QOmq.css +1 -0
- package/dist/web/assets/SessionList-CfPtcq6Y.css +1 -0
- package/dist/web/assets/SessionList-DMlLtMCz.js +1 -0
- package/dist/web/assets/{SkillManager-D6Vwpajh.js → SkillManager-DpNE02r0.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-C3TjeOPy.js → WorkspaceManager-DMY7_SHh.js} +1 -1
- package/dist/web/assets/icons-CEq2hYB-.js +1 -0
- package/dist/web/assets/index-Clf0l3wc.js +2 -0
- package/dist/web/assets/index-Dih_bOsv.css +1 -0
- package/dist/web/assets/{naive-ui-BaTCPPL5.js → naive-ui-Cg4_ZeoT.js} +1 -1
- package/dist/web/assets/{vendors-Fza9uSYn.js → vendors-Bsp-dq2d.js} +1 -1
- package/dist/web/assets/vue-vendor-BxIT0uQq.js +45 -0
- package/dist/web/index.html +7 -7
- package/docs/Caddyfile.example +19 -0
- package/docs/reverse-proxy-https.md +57 -0
- package/package.json +2 -1
- package/src/commands/daemon.js +33 -5
- package/src/commands/export-config.js +6 -6
- package/src/commands/ui.js +12 -3
- package/src/config/default.js +2 -6
- package/src/config/loader.js +2 -2
- package/src/config/paths.js +166 -33
- package/src/index.js +124 -34
- package/src/server/api/agents.js +52 -2
- package/src/server/api/commands.js +38 -2
- package/src/server/api/plugins.js +104 -1
- package/src/server/api/sessions.js +5 -5
- package/src/server/index.js +25 -5
- package/src/server/services/agents-service.js +269 -62
- package/src/server/services/commands-service.js +281 -81
- package/src/server/services/config-export-service.js +7 -7
- package/src/server/services/config-registry-service.js +4 -5
- package/src/server/services/config-sync-manager.js +61 -41
- package/src/server/services/config-sync-service.js +3 -3
- package/src/server/services/gemini-channels.js +5 -5
- package/src/server/services/gemini-config.js +3 -4
- package/src/server/services/gemini-sessions.js +23 -20
- package/src/server/services/gemini-settings-manager.js +2 -3
- package/src/server/services/https-cert.js +171 -0
- package/src/server/services/mcp-service.js +9 -14
- package/src/server/services/native-oauth-adapters.js +3 -3
- package/src/server/services/network-access.js +47 -2
- package/src/server/services/notification-hooks.js +11 -5
- package/src/server/services/opencode-sessions.js +4 -4
- package/src/server/services/opencode-settings-manager.js +3 -3
- package/src/server/services/plugins-service.js +499 -23
- package/src/server/services/prompts-service.js +5 -9
- package/src/server/services/sessions.js +2 -2
- package/src/server/services/skill-service.js +155 -18
- package/src/server/services/web-ui-runtime.js +54 -0
- package/src/server/websocket-server.js +11 -4
- package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
- package/dist/web/assets/Home-BQxQ1LhR.css +0 -1
- package/dist/web/assets/ProjectList-BNsz96av.js +0 -1
- package/dist/web/assets/ProjectList-DL4JK6ci.css +0 -1
- package/dist/web/assets/SessionList-B8dXVXfi.css +0 -1
- package/dist/web/assets/SessionList-CG1UhFo3.js +0 -1
- package/dist/web/assets/icons-CQuif85v.js +0 -1
- package/dist/web/assets/index-GuER-BmS.js +0 -2
- package/dist/web/assets/index-VGAxnLqi.css +0 -1
- package/dist/web/assets/vue-vendor-aWwwFAao.js +0 -45
|
@@ -6,29 +6,24 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const os = require('os');
|
|
10
9
|
const toml = require('@iarna/toml');
|
|
11
10
|
const { spawn } = require('child_process');
|
|
12
11
|
const http = require('http');
|
|
13
12
|
const https = require('https');
|
|
14
13
|
const { McpClient, buildMissingCommandMessage, createMissingCommandHint } = require('./mcp-client');
|
|
15
|
-
const { NATIVE_PATHS, PATHS } = require('../../config/paths');
|
|
16
|
-
const { resolvePreferredHomeDir } = require('../../utils/home-dir');
|
|
17
|
-
|
|
18
|
-
const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
|
|
14
|
+
const { NATIVE_PATHS, PATHS, HOME_DIR, getNativePathDir, joinNativeBasePath } = require('../../config/paths');
|
|
19
15
|
|
|
20
16
|
// MCP 配置文件路径
|
|
21
17
|
const MCP_SERVERS_FILE = PATHS.mcpServers;
|
|
22
18
|
|
|
23
19
|
// 各平台配置文件路径
|
|
24
|
-
const CLAUDE_CONFIG_PATH =
|
|
20
|
+
const CLAUDE_CONFIG_PATH = joinNativeBasePath(HOME_DIR, '.claude.json');
|
|
25
21
|
const CODEX_CONFIG_PATH = NATIVE_PATHS.codex.config;
|
|
26
|
-
const GEMINI_CONFIG_PATH =
|
|
27
|
-
const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
|
|
22
|
+
const GEMINI_CONFIG_PATH = NATIVE_PATHS.gemini.settings;
|
|
28
23
|
const OPENCODE_CONFIG_PATHS = {
|
|
29
|
-
jsonc:
|
|
30
|
-
json:
|
|
31
|
-
legacy:
|
|
24
|
+
jsonc: NATIVE_PATHS.opencode.configJsonc,
|
|
25
|
+
json: NATIVE_PATHS.opencode.configJson,
|
|
26
|
+
legacy: NATIVE_PATHS.opencode.configLegacy
|
|
32
27
|
};
|
|
33
28
|
|
|
34
29
|
// MCP 客户端连接池
|
|
@@ -279,7 +274,7 @@ function readJsonFile(filePath, defaultValue = {}) {
|
|
|
279
274
|
* 安全写入 JSON 文件(原子写入)
|
|
280
275
|
*/
|
|
281
276
|
function writeJsonFile(filePath, data) {
|
|
282
|
-
ensureDir(
|
|
277
|
+
ensureDir(getNativePathDir(filePath));
|
|
283
278
|
const tempPath = filePath + '.tmp';
|
|
284
279
|
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
285
280
|
fs.renameSync(tempPath, filePath);
|
|
@@ -305,7 +300,7 @@ function readTomlFile(filePath, defaultValue = {}) {
|
|
|
305
300
|
* 安全写入 TOML 文件(原子写入)
|
|
306
301
|
*/
|
|
307
302
|
function writeTomlFile(filePath, data) {
|
|
308
|
-
ensureDir(
|
|
303
|
+
ensureDir(getNativePathDir(filePath));
|
|
309
304
|
const tempPath = filePath + '.tmp';
|
|
310
305
|
fs.writeFileSync(tempPath, toml.stringify(data), 'utf-8');
|
|
311
306
|
fs.renameSync(tempPath, filePath);
|
|
@@ -412,7 +407,7 @@ function readOpenCodeConfig() {
|
|
|
412
407
|
* 写入 OpenCode 配置(保持 JSON 格式)
|
|
413
408
|
*/
|
|
414
409
|
function writeOpenCodeConfig(filePath, data) {
|
|
415
|
-
ensureDir(
|
|
410
|
+
ensureDir(getNativePathDir(filePath));
|
|
416
411
|
const tempPath = filePath + '.tmp';
|
|
417
412
|
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
418
413
|
fs.renameSync(tempPath, filePath);
|
|
@@ -4,7 +4,7 @@ const os = require('os');
|
|
|
4
4
|
const crypto = require('crypto');
|
|
5
5
|
const toml = require('toml');
|
|
6
6
|
const tomlStringify = require('@iarna/toml').stringify;
|
|
7
|
-
const { NATIVE_PATHS, PATHS } = require('../../config/paths');
|
|
7
|
+
const { NATIVE_PATHS, PATHS, getNativePathDir } = require('../../config/paths');
|
|
8
8
|
const claudeSettingsManager = require('./settings-manager');
|
|
9
9
|
const codexSettingsManager = require('./codex-settings-manager');
|
|
10
10
|
const geminiSettingsManager = require('./gemini-settings-manager');
|
|
@@ -35,7 +35,7 @@ function ensureFileMode(filePath, mode = 0o600) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function writeJsonFile(filePath, value) {
|
|
38
|
-
ensureDir(
|
|
38
|
+
ensureDir(getNativePathDir(filePath));
|
|
39
39
|
fs.writeFileSync(filePath, JSON.stringify(value, null, 2), 'utf8');
|
|
40
40
|
ensureFileMode(filePath);
|
|
41
41
|
}
|
|
@@ -562,7 +562,7 @@ function applyGeminiOAuth(credential) {
|
|
|
562
562
|
}
|
|
563
563
|
};
|
|
564
564
|
|
|
565
|
-
ensureDir(
|
|
565
|
+
ensureDir(getNativePathDir(NATIVE_PATHS.gemini.oauthCredentialsEncrypted));
|
|
566
566
|
fs.writeFileSync(
|
|
567
567
|
NATIVE_PATHS.gemini.oauthCredentialsEncrypted,
|
|
568
568
|
encryptGeminiPayload(JSON.stringify(payload, null, 2)),
|
|
@@ -16,6 +16,21 @@ function isLoopbackAddress(address) {
|
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function hasTrustedProxySocket(req) {
|
|
20
|
+
if (!req) return false;
|
|
21
|
+
return isLoopbackAddress(req.socket && req.socket.remoteAddress);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getForwardedHeaderValue(req, headerName) {
|
|
25
|
+
if (!hasTrustedProxySocket(req) || !req.headers) {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const rawValue = req.headers[headerName];
|
|
30
|
+
const value = Array.isArray(rawValue) ? rawValue[0] : rawValue;
|
|
31
|
+
return String(value || '').split(',')[0].trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
19
34
|
function isLoopbackRequest(req) {
|
|
20
35
|
if (!req) return false;
|
|
21
36
|
const socketAddress = req.socket && req.socket.remoteAddress;
|
|
@@ -30,6 +45,34 @@ function isLoopbackRequest(req) {
|
|
|
30
45
|
return true;
|
|
31
46
|
}
|
|
32
47
|
|
|
48
|
+
function getRequestProtocol(req) {
|
|
49
|
+
const forwardedProto = getForwardedHeaderValue(req, 'x-forwarded-proto').toLowerCase();
|
|
50
|
+
if (forwardedProto === 'https' || forwardedProto === 'https:') {
|
|
51
|
+
return 'https:';
|
|
52
|
+
}
|
|
53
|
+
if (forwardedProto === 'http' || forwardedProto === 'http:') {
|
|
54
|
+
return 'http:';
|
|
55
|
+
}
|
|
56
|
+
return req && req.socket && req.socket.encrypted ? 'https:' : 'http:';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getRequestHost(req) {
|
|
60
|
+
const forwardedHost = getForwardedHeaderValue(req, 'x-forwarded-host');
|
|
61
|
+
if (forwardedHost) {
|
|
62
|
+
return forwardedHost;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!req || !req.headers) {
|
|
66
|
+
return '';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const host = req.headers.host;
|
|
70
|
+
if (Array.isArray(host)) {
|
|
71
|
+
return String(host[0] || '').trim();
|
|
72
|
+
}
|
|
73
|
+
return String(host || '').trim();
|
|
74
|
+
}
|
|
75
|
+
|
|
33
76
|
function isSameOriginRequest(req) {
|
|
34
77
|
if (!req) return false;
|
|
35
78
|
const origin = req.headers && req.headers.origin;
|
|
@@ -37,14 +80,14 @@ function isSameOriginRequest(req) {
|
|
|
37
80
|
return true;
|
|
38
81
|
}
|
|
39
82
|
|
|
40
|
-
const host = req
|
|
83
|
+
const host = getRequestHost(req);
|
|
41
84
|
if (!host) {
|
|
42
85
|
return false;
|
|
43
86
|
}
|
|
44
87
|
|
|
45
88
|
try {
|
|
46
89
|
const originUrl = new URL(origin);
|
|
47
|
-
return originUrl.host === host;
|
|
90
|
+
return originUrl.host.toLowerCase() === host.toLowerCase();
|
|
48
91
|
} catch (error) {
|
|
49
92
|
return false;
|
|
50
93
|
}
|
|
@@ -124,6 +167,8 @@ module.exports = {
|
|
|
124
167
|
normalizeAddress,
|
|
125
168
|
isLoopbackAddress,
|
|
126
169
|
isLoopbackRequest,
|
|
170
|
+
getRequestProtocol,
|
|
171
|
+
getRequestHost,
|
|
127
172
|
isSameOriginRequest,
|
|
128
173
|
isRemoteMutationAllowed,
|
|
129
174
|
createRemoteMutationGuard,
|
|
@@ -4,11 +4,12 @@ const os = require('os');
|
|
|
4
4
|
const http = require('http');
|
|
5
5
|
const https = require('https');
|
|
6
6
|
const { execSync, execFileSync } = require('child_process');
|
|
7
|
-
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
7
|
+
const { PATHS, NATIVE_PATHS, getNativePathDir, joinNativeBasePath } = require('../../config/paths');
|
|
8
8
|
const { loadConfig } = require('../../config/loader');
|
|
9
9
|
const { loadUIConfig, saveUIConfig } = require('./ui-config');
|
|
10
10
|
const codexSettingsManager = require('./codex-settings-manager');
|
|
11
11
|
const geminiSettingsManager = require('./gemini-settings-manager');
|
|
12
|
+
const { getWebUiProtocol } = require('./web-ui-runtime');
|
|
12
13
|
|
|
13
14
|
const MANAGED_HOOK_NAME = 'coding-tool-notify';
|
|
14
15
|
const MANAGED_OPENCODE_PLUGIN_FILE = 'coding-tool-notify.js';
|
|
@@ -18,7 +19,7 @@ function normalizeType(type) {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
function ensureParentDir(filePath) {
|
|
21
|
-
const dir =
|
|
22
|
+
const dir = getNativePathDir(filePath);
|
|
22
23
|
if (!fs.existsSync(dir)) {
|
|
23
24
|
fs.mkdirSync(dir, { recursive: true });
|
|
24
25
|
}
|
|
@@ -169,9 +170,10 @@ function buildBrowserNotificationEndpoint() {
|
|
|
169
170
|
const config = loadConfig();
|
|
170
171
|
const port = Number(config?.ports?.webUI);
|
|
171
172
|
const resolvedPort = Number.isFinite(port) && port > 0 ? port : 19999;
|
|
172
|
-
|
|
173
|
+
const protocol = getWebUiProtocol();
|
|
174
|
+
return `${protocol}://127.0.0.1:${resolvedPort}/api/hooks/browser-event`;
|
|
173
175
|
} catch (error) {
|
|
174
|
-
return
|
|
176
|
+
return `${getWebUiProtocol()}://127.0.0.1:19999/api/hooks/browser-event`;
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
179
|
|
|
@@ -267,7 +269,7 @@ function buildGeminiCommand(type) {
|
|
|
267
269
|
}
|
|
268
270
|
|
|
269
271
|
function getOpenCodeManagedPluginPath() {
|
|
270
|
-
return
|
|
272
|
+
return joinNativeBasePath(NATIVE_PATHS.opencode.plugins, MANAGED_OPENCODE_PLUGIN_FILE);
|
|
271
273
|
}
|
|
272
274
|
|
|
273
275
|
function buildOpenCodePluginContent(type) {
|
|
@@ -746,6 +748,10 @@ function sendBrowserNotification(payload) {
|
|
|
746
748
|
timeout: 5000
|
|
747
749
|
}
|
|
748
750
|
|
|
751
|
+
if (urlObj.protocol === 'https:' && ['127.0.0.1', 'localhost', '::1'].includes(urlObj.hostname)) {
|
|
752
|
+
options.rejectUnauthorized = false
|
|
753
|
+
}
|
|
754
|
+
|
|
749
755
|
const requestModule = urlObj.protocol === 'https:' ? https : http
|
|
750
756
|
const request = requestModule.request(options, () => resolve())
|
|
751
757
|
request.on('error', () => resolve())
|
|
@@ -2,7 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { execFileSync } = require('child_process');
|
|
5
|
-
const { NATIVE_PATHS, PATHS } = require('../../config/paths');
|
|
5
|
+
const { NATIVE_PATHS, PATHS, joinNativeBasePath } = require('../../config/paths');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* OpenCode 会话服务
|
|
@@ -11,7 +11,7 @@ const { NATIVE_PATHS, PATHS } = require('../../config/paths');
|
|
|
11
11
|
|
|
12
12
|
const PROJECT_ORDER_FILE = PATHS.opencodeProjectOrder;
|
|
13
13
|
const SESSION_ORDER_FILE = PATHS.opencodeSessionOrder;
|
|
14
|
-
const OPENCODE_DB_PATH =
|
|
14
|
+
const OPENCODE_DB_PATH = joinNativeBasePath(NATIVE_PATHS.opencode.data, 'opencode.db');
|
|
15
15
|
const COUNTS_CACHE_TTL_MS = 30 * 1000;
|
|
16
16
|
const EMPTY_COUNTS = Object.freeze({ projectCount: 0, sessionCount: 0 });
|
|
17
17
|
|
|
@@ -234,12 +234,12 @@ function getOpenCodeDataDir() {
|
|
|
234
234
|
|
|
235
235
|
// 兼容导出:保留旧路径函数
|
|
236
236
|
function getSessionsDir() {
|
|
237
|
-
return
|
|
237
|
+
return NATIVE_PATHS?.opencode?.sessions || joinNativeBasePath(getOpenCodeDataDir(), 'storage', 'session');
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
// 兼容导出:保留旧路径函数
|
|
241
241
|
function getProjectsDir() {
|
|
242
|
-
return
|
|
242
|
+
return NATIVE_PATHS?.opencode?.projects || joinNativeBasePath(getOpenCodeDataDir(), 'storage', 'project');
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
function getProjectOrder() {
|
|
@@ -23,9 +23,9 @@ function resolveModelCost(modelId) {
|
|
|
23
23
|
|
|
24
24
|
const CONFIG_DIR = NATIVE_PATHS.opencode.config;
|
|
25
25
|
const CONFIG_PATHS = {
|
|
26
|
-
config:
|
|
27
|
-
opencode:
|
|
28
|
-
opencodec:
|
|
26
|
+
config: NATIVE_PATHS.opencode.configLegacy,
|
|
27
|
+
opencode: NATIVE_PATHS.opencode.configJson,
|
|
28
|
+
opencodec: NATIVE_PATHS.opencode.configJsonc
|
|
29
29
|
};
|
|
30
30
|
const BACKUP_SUFFIX = '.cc-tool-backup';
|
|
31
31
|
const EMPTY_SENTINEL = '__CC_TOOL_NO_FILE__';
|