coding-tool-x 3.3.3 → 3.3.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.
- package/dist/web/assets/{Analytics-DtR00OYP.js → Analytics-B6CWdkhx.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-DWiSFOp5.js → ConfigTemplates-BW6LEgd8.js} +1 -1
- package/dist/web/assets/{Home-DUu2mGb6.js → Home-B2B2gS2-.js} +1 -1
- package/dist/web/assets/{PluginManager-DsJ1KtNr.js → PluginManager-Bqc7ldY-.js} +1 -1
- package/dist/web/assets/{ProjectList-CzTJaBJb.js → ProjectList-BFdZZm_8.js} +1 -1
- package/dist/web/assets/{SessionList-D1ovPZ0I.js → SessionList-B_Tp37kM.js} +1 -1
- package/dist/web/assets/{SkillManager-DqpDTc2c.js → SkillManager-ul2rcS3o.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Dj28-3G5.js → WorkspaceManager-Dp5Jvdtu.js} +1 -1
- package/dist/web/assets/index-CSBDZxYn.js +2 -0
- package/dist/web/assets/index-DxRneGyu.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +2 -1
- package/src/commands/doctor.js +3 -3
- package/src/commands/export-config.js +2 -2
- package/src/commands/logs.js +42 -9
- package/src/commands/port-config.js +2 -2
- package/src/commands/switch.js +2 -2
- package/src/config/default.js +4 -1
- package/src/config/loader.js +5 -2
- package/src/config/paths.js +25 -21
- package/src/reset-config.js +3 -5
- package/src/server/api/agents.js +2 -3
- package/src/server/api/claude-hooks.js +92 -12
- package/src/server/api/codex-sessions.js +6 -5
- package/src/server/api/opencode-sessions.js +2 -2
- package/src/server/api/pm2-autostart.js +20 -8
- package/src/server/api/proxy.js +2 -3
- package/src/server/api/sessions.js +42 -12
- package/src/server/index.js +5 -9
- package/src/server/opencode-proxy-server.js +11 -1
- package/src/server/services/agents-service.js +6 -3
- package/src/server/services/channels.js +6 -7
- package/src/server/services/codex-channels.js +4 -1
- package/src/server/services/codex-config.js +4 -1
- package/src/server/services/codex-parser.js +31 -4
- package/src/server/services/codex-settings-manager.js +18 -9
- package/src/server/services/commands-service.js +2 -2
- package/src/server/services/config-export-service.js +7 -6
- package/src/server/services/config-registry-service.js +7 -6
- package/src/server/services/config-sync-manager.js +2 -2
- package/src/server/services/config-sync-service.js +2 -2
- package/src/server/services/env-checker.js +2 -2
- package/src/server/services/favorites.js +3 -4
- package/src/server/services/gemini-channels.js +4 -4
- package/src/server/services/gemini-config.js +2 -2
- package/src/server/services/gemini-sessions.js +3 -3
- package/src/server/services/gemini-settings-manager.js +5 -5
- package/src/server/services/mcp-service.js +7 -4
- package/src/server/services/model-detector.js +2 -2
- package/src/server/services/opencode-channels.js +5 -5
- package/src/server/services/opencode-sessions.js +28 -3
- package/src/server/services/plugins-service.js +3 -4
- package/src/server/services/prompts-service.js +7 -4
- package/src/server/services/proxy-runtime.js +2 -2
- package/src/server/services/repo-scanner-base.js +2 -2
- package/src/server/services/request-logger.js +2 -2
- package/src/server/services/security-config.js +2 -2
- package/src/server/services/session-cache.js +2 -2
- package/src/server/services/session-converter.js +9 -4
- package/src/server/services/sessions.js +8 -5
- package/src/server/services/settings-manager.js +3 -4
- package/src/server/services/skill-service.js +5 -5
- package/src/server/services/statistics-service.js +2 -2
- package/src/server/services/ui-config.js +3 -4
- package/src/server/websocket-server.js +2 -2
- package/src/utils/home-dir.js +82 -0
- package/src/utils/port-helper.js +34 -12
- package/dist/web/assets/index-CaKktouI.js +0 -2
- package/dist/web/assets/index-DZjDFGqR.css +0 -1
|
@@ -359,7 +359,19 @@ function getSessionRowsByProjectId(projectId) {
|
|
|
359
359
|
s.time_created,
|
|
360
360
|
s.time_updated,
|
|
361
361
|
s.time_compacting,
|
|
362
|
-
s.time_archived
|
|
362
|
+
s.time_archived,
|
|
363
|
+
(
|
|
364
|
+
COALESCE((
|
|
365
|
+
SELECT SUM(length(CAST(COALESCE(m.data, '') AS BLOB)))
|
|
366
|
+
FROM message m
|
|
367
|
+
WHERE m.session_id = s.id
|
|
368
|
+
), 0) +
|
|
369
|
+
COALESCE((
|
|
370
|
+
SELECT SUM(length(CAST(COALESCE(p.data, '') AS BLOB)))
|
|
371
|
+
FROM part p
|
|
372
|
+
WHERE p.session_id = s.id
|
|
373
|
+
), 0)
|
|
374
|
+
) AS size
|
|
363
375
|
FROM session s
|
|
364
376
|
WHERE s.project_id = ${sqlQuote(projectId)}
|
|
365
377
|
AND s.time_archived IS NULL
|
|
@@ -387,7 +399,19 @@ function getSessionRowById(sessionId) {
|
|
|
387
399
|
s.time_created,
|
|
388
400
|
s.time_updated,
|
|
389
401
|
s.time_compacting,
|
|
390
|
-
s.time_archived
|
|
402
|
+
s.time_archived,
|
|
403
|
+
(
|
|
404
|
+
COALESCE((
|
|
405
|
+
SELECT SUM(length(CAST(COALESCE(m.data, '') AS BLOB)))
|
|
406
|
+
FROM message m
|
|
407
|
+
WHERE m.session_id = s.id
|
|
408
|
+
), 0) +
|
|
409
|
+
COALESCE((
|
|
410
|
+
SELECT SUM(length(CAST(COALESCE(p.data, '') AS BLOB)))
|
|
411
|
+
FROM part p
|
|
412
|
+
WHERE p.session_id = s.id
|
|
413
|
+
), 0)
|
|
414
|
+
) AS size
|
|
391
415
|
FROM session s
|
|
392
416
|
WHERE s.id = ${sqlQuote(sessionId)}
|
|
393
417
|
LIMIT 1
|
|
@@ -426,11 +450,12 @@ function getPartRowsBySessionId(sessionId) {
|
|
|
426
450
|
}
|
|
427
451
|
|
|
428
452
|
function normalizeSession(session, projectId = null) {
|
|
453
|
+
const size = Number(session?.size);
|
|
429
454
|
return {
|
|
430
455
|
sessionId: session.id,
|
|
431
456
|
projectName: projectId || session.project_id,
|
|
432
457
|
mtime: toIsoTime(session.time_updated) || new Date().toISOString(),
|
|
433
|
-
size: 0,
|
|
458
|
+
size: Number.isFinite(size) && size > 0 ? size : 0,
|
|
434
459
|
filePath: '',
|
|
435
460
|
gitBranch: null,
|
|
436
461
|
firstMessage: session.title || session.slug || null,
|
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const os = require('os');
|
|
10
9
|
const { listPlugins, getPlugin, updatePlugin: updatePluginRegistry } = require('../../plugins/registry');
|
|
11
10
|
const { installPlugin: installPluginCore, uninstallPlugin: uninstallPluginCore } = require('../../plugins/plugin-installer');
|
|
12
11
|
const { initializePlugins, shutdownPlugins } = require('../../plugins/plugin-manager');
|
|
13
12
|
const { INSTALLED_DIR, CONFIG_DIR } = require('../../plugins/constants');
|
|
14
|
-
const { NATIVE_PATHS } = require('../../config/paths');
|
|
13
|
+
const { NATIVE_PATHS, HOME_DIR } = require('../../config/paths');
|
|
15
14
|
|
|
16
|
-
const CLAUDE_PLUGINS_DIR = path.join(
|
|
15
|
+
const CLAUDE_PLUGINS_DIR = path.join(path.dirname(NATIVE_PATHS.claude.settings), 'plugins');
|
|
17
16
|
const CLAUDE_INSTALLED_FILE = path.join(CLAUDE_PLUGINS_DIR, 'installed_plugins.json');
|
|
18
17
|
const CLAUDE_MARKETPLACES_FILE = path.join(CLAUDE_PLUGINS_DIR, 'known_marketplaces.json');
|
|
19
18
|
const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
|
|
@@ -107,7 +106,7 @@ function stripJsonComments(input = '') {
|
|
|
107
106
|
class PluginsService {
|
|
108
107
|
constructor(platform = 'claude') {
|
|
109
108
|
this.platform = ['claude', 'opencode'].includes(platform) ? platform : 'claude';
|
|
110
|
-
this.ccToolConfigDir = path.join(
|
|
109
|
+
this.ccToolConfigDir = path.join(HOME_DIR, '.cc-tool');
|
|
111
110
|
this.opencodePluginsDir = path.join(OPENCODE_CONFIG_DIR, 'plugins');
|
|
112
111
|
this.opencodeLegacyPluginsDir = path.join(OPENCODE_CONFIG_DIR, 'plugin');
|
|
113
112
|
}
|
|
@@ -8,15 +8,18 @@ const fs = require('fs');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const os = require('os');
|
|
10
10
|
const { NATIVE_PATHS } = require('../../config/paths');
|
|
11
|
+
const { resolvePreferredHomeDir } = require('../../utils/home-dir');
|
|
12
|
+
|
|
13
|
+
const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
|
|
11
14
|
|
|
12
15
|
// Prompts 配置文件路径
|
|
13
|
-
const CC_TOOL_DIR = path.join(
|
|
16
|
+
const CC_TOOL_DIR = path.join(HOME_DIR, '.cc-tool');
|
|
14
17
|
const PROMPTS_FILE = path.join(CC_TOOL_DIR, 'prompts.json');
|
|
15
18
|
|
|
16
19
|
// 各平台提示词文件路径
|
|
17
|
-
const CLAUDE_PROMPT_PATH = path.join(
|
|
18
|
-
const CODEX_PROMPT_PATH = path.join(
|
|
19
|
-
const GEMINI_PROMPT_PATH = path.join(
|
|
20
|
+
const CLAUDE_PROMPT_PATH = path.join(HOME_DIR, '.claude', 'CLAUDE.md');
|
|
21
|
+
const CODEX_PROMPT_PATH = path.join(HOME_DIR, '.codex', 'AGENTS.md');
|
|
22
|
+
const GEMINI_PROMPT_PATH = path.join(HOME_DIR, '.gemini', 'GEMINI.md');
|
|
20
23
|
const OPENCODE_PROMPT_PATH = path.join(NATIVE_PATHS.opencode.config, 'AGENTS.md');
|
|
21
24
|
|
|
22
25
|
function normalizeApps(apps = {}, defaults = { claude: true, codex: true, gemini: true, opencode: false }) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const
|
|
3
|
+
const { PATHS } = require('../../config/paths');
|
|
4
4
|
|
|
5
5
|
function getRuntimeFilePath(proxyType) {
|
|
6
|
-
const ccToolDir =
|
|
6
|
+
const ccToolDir = PATHS.base;
|
|
7
7
|
if (!fs.existsSync(ccToolDir)) {
|
|
8
8
|
fs.mkdirSync(ccToolDir, { recursive: true });
|
|
9
9
|
}
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const os = require('os');
|
|
11
10
|
const https = require('https');
|
|
12
11
|
const http = require('http');
|
|
13
12
|
const { createWriteStream } = require('fs');
|
|
14
13
|
const AdmZip = require('adm-zip');
|
|
14
|
+
const { HOME_DIR } = require('../../config/paths');
|
|
15
15
|
|
|
16
16
|
// 缓存有效期(5分钟)
|
|
17
17
|
const CACHE_TTL = 5 * 60 * 1000;
|
|
@@ -70,7 +70,7 @@ class RepoScannerBase {
|
|
|
70
70
|
this.fileExtension = options.fileExtension || '.md';
|
|
71
71
|
this.defaultRepos = options.defaultRepos || [];
|
|
72
72
|
|
|
73
|
-
this.configDir = path.join(
|
|
73
|
+
this.configDir = path.join(HOME_DIR, '.cc-tool');
|
|
74
74
|
this.reposConfigPath = path.join(this.configDir, `${this.type}-repos.json`);
|
|
75
75
|
this.cachePath = path.join(this.configDir, `${this.type}-cache.json`);
|
|
76
76
|
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
|
-
const
|
|
19
|
+
const { PATHS } = require('../../config/paths');
|
|
20
20
|
|
|
21
|
-
const CC_TOOL_DIR =
|
|
21
|
+
const CC_TOOL_DIR = PATHS.base;
|
|
22
22
|
|
|
23
23
|
function ensureDir(dir) {
|
|
24
24
|
if (!fs.existsSync(dir)) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
3
|
const crypto = require('crypto');
|
|
4
|
+
const { PATHS } = require('../../config/paths');
|
|
5
5
|
|
|
6
|
-
const SECURITY_DIR =
|
|
6
|
+
const SECURITY_DIR = PATHS.base;
|
|
7
7
|
const SECURITY_FILE = path.join(SECURITY_DIR, 'security.json');
|
|
8
8
|
|
|
9
9
|
const DEFAULT_SECURITY_CONFIG = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const
|
|
3
|
+
const { PATHS } = require('../../config/paths');
|
|
4
4
|
|
|
5
5
|
const PROJECTS_CACHE_TTL = 30 * 1000; // 30s
|
|
6
6
|
const projectsCache = new Map();
|
|
@@ -11,7 +11,7 @@ let hasMessagesPersisted = {};
|
|
|
11
11
|
let hasMessagesPersistTimer = null;
|
|
12
12
|
|
|
13
13
|
function getCcToolDir() {
|
|
14
|
-
return
|
|
14
|
+
return PATHS.base;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function ensureDirExists(dir) {
|
|
@@ -6,6 +6,11 @@ const { getSessionById: getClaudeSessionById } = require('./sessions');
|
|
|
6
6
|
const { getSessionById: getCodexSessionById } = require('./codex-sessions');
|
|
7
7
|
const { getSessionById: getGeminiSessionById, getProjectPath } = require('./gemini-sessions');
|
|
8
8
|
const { readJSONL, parseSession: parseCodexFull } = require('./codex-parser');
|
|
9
|
+
const { NATIVE_PATHS, HOME_DIR } = require('../../config/paths');
|
|
10
|
+
|
|
11
|
+
const CLAUDE_PROJECTS_DIR = NATIVE_PATHS.claude.projects;
|
|
12
|
+
const CODEX_SESSIONS_DIR = NATIVE_PATHS.codex.sessions;
|
|
13
|
+
const GEMINI_TMP_DIR = NATIVE_PATHS.gemini.tmp;
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
16
|
* 统一中间格式
|
|
@@ -192,7 +197,7 @@ function parseGeminiToUnified(filePath) {
|
|
|
192
197
|
|
|
193
198
|
return {
|
|
194
199
|
sessionId: session.sessionId || crypto.randomUUID(),
|
|
195
|
-
cwd: projectPath ||
|
|
200
|
+
cwd: projectPath || HOME_DIR,
|
|
196
201
|
gitBranch: null, // Gemini 不记录 git branch
|
|
197
202
|
startTime: session.startTime || new Date().toISOString(),
|
|
198
203
|
messages,
|
|
@@ -380,7 +385,7 @@ function generateTargetPath(targetType, unified, options = {}) {
|
|
|
380
385
|
|
|
381
386
|
if (targetType === 'claude') {
|
|
382
387
|
// Claude: ~/.claude/projects/{encoded-path}/{uuid}.jsonl
|
|
383
|
-
const projectsDir =
|
|
388
|
+
const projectsDir = CLAUDE_PROJECTS_DIR;
|
|
384
389
|
const encodedPath = options.targetProject
|
|
385
390
|
? encodePath(options.targetProject)
|
|
386
391
|
: encodePath(unified.cwd);
|
|
@@ -388,7 +393,7 @@ function generateTargetPath(targetType, unified, options = {}) {
|
|
|
388
393
|
return path.join(projectsDir, encodedPath, `${newSessionId}.jsonl`);
|
|
389
394
|
} else if (targetType === 'codex') {
|
|
390
395
|
// Codex: ~/.codex/sessions/{YYYY}/{MM}/{DD}/rollout-{timestamp}-{uuid}.jsonl
|
|
391
|
-
const sessionsDir =
|
|
396
|
+
const sessionsDir = CODEX_SESSIONS_DIR;
|
|
392
397
|
const now = new Date();
|
|
393
398
|
const year = now.getFullYear();
|
|
394
399
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
@@ -401,7 +406,7 @@ function generateTargetPath(targetType, unified, options = {}) {
|
|
|
401
406
|
`rollout-${timestamp}-${newSessionId}.jsonl`);
|
|
402
407
|
} else if (targetType === 'gemini') {
|
|
403
408
|
// Gemini: ~/.gemini/tmp/{project_hash}/chats/session-{timestamp}-{shortId}.json
|
|
404
|
-
const geminiDir =
|
|
409
|
+
const geminiDir = GEMINI_TMP_DIR;
|
|
405
410
|
const projectHash = unified.metadata.projectHash ||
|
|
406
411
|
crypto.createHash('sha256').update(unified.cwd).digest('hex');
|
|
407
412
|
const timestamp = new Date().toISOString().replace(/:/g, '-').replace(/\..+/, '');
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
3
|
const crypto = require('crypto');
|
|
5
4
|
const { getAllSessions, parseSessionInfoFast } = require('../../utils/session');
|
|
6
5
|
const { loadAliases } = require('./alias');
|
|
@@ -12,7 +11,11 @@ const {
|
|
|
12
11
|
rememberHasMessages
|
|
13
12
|
} = require('./session-cache');
|
|
14
13
|
const { globalCache, CacheKeys } = require('./enhanced-cache');
|
|
15
|
-
const { PATHS } = require('../../config/paths');
|
|
14
|
+
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
15
|
+
|
|
16
|
+
const CLAUDE_PROJECTS_DIR = NATIVE_PATHS.claude.projects;
|
|
17
|
+
const CODEX_PROJECTS_DIR = path.join(path.dirname(NATIVE_PATHS.codex.config), 'projects');
|
|
18
|
+
const GEMINI_PROJECTS_DIR = path.join(path.dirname(NATIVE_PATHS.gemini.env), 'projects');
|
|
16
19
|
|
|
17
20
|
// Base directory for cc-tool data
|
|
18
21
|
function getCcToolDir() {
|
|
@@ -238,7 +241,7 @@ function validateProjectPath(candidatePath) {
|
|
|
238
241
|
|
|
239
242
|
function tryResolvePathFromSessions(encodedName) {
|
|
240
243
|
try {
|
|
241
|
-
const projectDir = path.join(
|
|
244
|
+
const projectDir = path.join(CLAUDE_PROJECTS_DIR, encodedName);
|
|
242
245
|
if (!fs.existsSync(projectDir)) {
|
|
243
246
|
return null;
|
|
244
247
|
}
|
|
@@ -824,7 +827,7 @@ async function searchSessionsAcrossProjects(config, keyword, contextLength = 35)
|
|
|
824
827
|
|
|
825
828
|
try {
|
|
826
829
|
// Search in Codex projects
|
|
827
|
-
const codexProjectsDir =
|
|
830
|
+
const codexProjectsDir = CODEX_PROJECTS_DIR;
|
|
828
831
|
if (fs.existsSync(codexProjectsDir)) {
|
|
829
832
|
const codexConfig = { ...config, projectsDir: codexProjectsDir };
|
|
830
833
|
const codexProjects = await getProjects(codexConfig);
|
|
@@ -849,7 +852,7 @@ async function searchSessionsAcrossProjects(config, keyword, contextLength = 35)
|
|
|
849
852
|
|
|
850
853
|
try {
|
|
851
854
|
// Search in Gemini projects
|
|
852
|
-
const geminiProjectsDir =
|
|
855
|
+
const geminiProjectsDir = GEMINI_PROJECTS_DIR;
|
|
853
856
|
if (fs.existsSync(geminiProjectsDir)) {
|
|
854
857
|
const geminiConfig = { ...config, projectsDir: geminiProjectsDir };
|
|
855
858
|
const geminiProjects = await getProjects(geminiConfig);
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
const
|
|
3
|
-
const os = require('os');
|
|
2
|
+
const { NATIVE_PATHS } = require('../../config/paths');
|
|
4
3
|
|
|
5
4
|
// Claude Code 配置文件路径
|
|
6
5
|
function getSettingsPath() {
|
|
7
|
-
return
|
|
6
|
+
return NATIVE_PATHS.claude.settings;
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
// 备份文件路径
|
|
11
10
|
function getBackupPath() {
|
|
12
|
-
return
|
|
11
|
+
return NATIVE_PATHS.claude.settingsBackup;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
// 检查配置文件是否存在
|
|
@@ -16,7 +16,7 @@ const AdmZip = require('adm-zip');
|
|
|
16
16
|
const {
|
|
17
17
|
parseSkillContent,
|
|
18
18
|
} = require('./format-converter');
|
|
19
|
-
const { NATIVE_PATHS } = require('../../config/paths');
|
|
19
|
+
const { NATIVE_PATHS, HOME_DIR } = require('../../config/paths');
|
|
20
20
|
|
|
21
21
|
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
22
22
|
const OPENCODE_SKILL_NAME_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
@@ -46,17 +46,17 @@ const DEFAULT_REPOS_BY_PLATFORM = {
|
|
|
46
46
|
|
|
47
47
|
const PLATFORM_CONFIG = {
|
|
48
48
|
claude: {
|
|
49
|
-
installDir: path.join(
|
|
49
|
+
installDir: path.join(HOME_DIR, '.claude', 'skills'),
|
|
50
50
|
reposFile: 'skill-repos.json',
|
|
51
51
|
cacheFile: 'skills-cache.json'
|
|
52
52
|
},
|
|
53
53
|
codex: {
|
|
54
|
-
installDir: path.join(
|
|
54
|
+
installDir: path.join(HOME_DIR, '.codex', 'skills'),
|
|
55
55
|
reposFile: 'codex-skill-repos.json',
|
|
56
56
|
cacheFile: 'codex-skills-cache.json'
|
|
57
57
|
},
|
|
58
58
|
gemini: {
|
|
59
|
-
installDir: path.join(
|
|
59
|
+
installDir: path.join(HOME_DIR, '.gemini', 'skills'),
|
|
60
60
|
reposFile: 'gemini-skill-repos.json',
|
|
61
61
|
cacheFile: 'gemini-skills-cache.json'
|
|
62
62
|
},
|
|
@@ -73,7 +73,7 @@ const CACHE_TTL = 5 * 60 * 1000;
|
|
|
73
73
|
class SkillService {
|
|
74
74
|
constructor(platform = 'claude') {
|
|
75
75
|
this.platform = normalizePlatform(platform);
|
|
76
|
-
this.configDir = path.join(
|
|
76
|
+
this.configDir = path.join(HOME_DIR, '.cc-tool');
|
|
77
77
|
|
|
78
78
|
const platformConfig = PLATFORM_CONFIG[this.platform];
|
|
79
79
|
this.installDir = platformConfig.installDir;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const
|
|
3
|
+
const { PATHS } = require('../../config/paths');
|
|
4
4
|
|
|
5
5
|
// 北京时间辅助(UTC+8),统一所有时间计算
|
|
6
6
|
const CST_OFFSET_MS = 8 * 60 * 60 * 1000;
|
|
@@ -39,7 +39,7 @@ function getCSTHour(ts) {
|
|
|
39
39
|
|
|
40
40
|
// 获取基础目录
|
|
41
41
|
function getBaseDir() {
|
|
42
|
-
const dir =
|
|
42
|
+
const dir = PATHS.base;
|
|
43
43
|
if (!fs.existsSync(dir)) {
|
|
44
44
|
fs.mkdirSync(dir, { recursive: true });
|
|
45
45
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
const
|
|
3
|
-
const os = require('os');
|
|
2
|
+
const { PATHS } = require('../../config/paths');
|
|
4
3
|
|
|
5
|
-
const UI_CONFIG_DIR =
|
|
6
|
-
const UI_CONFIG_FILE =
|
|
4
|
+
const UI_CONFIG_DIR = PATHS.base;
|
|
5
|
+
const UI_CONFIG_FILE = PATHS.uiConfig;
|
|
7
6
|
|
|
8
7
|
// Default UI config
|
|
9
8
|
const DEFAULT_UI_CONFIG = {
|
|
@@ -2,8 +2,8 @@ const WebSocket = require('ws');
|
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const os = require('os');
|
|
6
5
|
const { loadConfig } = require('../config/loader');
|
|
6
|
+
const { PATHS } = require('../config/paths');
|
|
7
7
|
const {
|
|
8
8
|
normalizeAddress,
|
|
9
9
|
isLoopbackAddress,
|
|
@@ -159,7 +159,7 @@ function installOriginGuard(server) {
|
|
|
159
159
|
|
|
160
160
|
// 日志持久化文件路径
|
|
161
161
|
function getLogsFilePath() {
|
|
162
|
-
const ccToolDir =
|
|
162
|
+
const ccToolDir = PATHS.base;
|
|
163
163
|
if (!fs.existsSync(ccToolDir)) {
|
|
164
164
|
fs.mkdirSync(ccToolDir, { recursive: true });
|
|
165
165
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
function isWindowsLikePlatform(platform = process.platform, env = process.env) {
|
|
4
|
+
if (platform === 'win32' || platform === 'cygwin' || platform === 'msys') {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const hasWindowsEnv = Boolean(env.SYSTEMROOT || env.WINDIR || env.COMSPEC || env.ComSpec);
|
|
9
|
+
const hasWindowsHome = /^[a-zA-Z]:[\\/]/.test(env.USERPROFILE || '') ||
|
|
10
|
+
(Boolean(env.HOMEDRIVE) && Boolean(env.HOMEPATH));
|
|
11
|
+
return hasWindowsEnv && hasWindowsHome;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeWindowsHomePath(rawPath, env = process.env) {
|
|
15
|
+
if (!rawPath || typeof rawPath !== 'string') {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let value = rawPath.trim();
|
|
20
|
+
if (!value) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const msysDriveMatch = value.match(/^\/([a-zA-Z])\/(.+)$/);
|
|
25
|
+
if (msysDriveMatch) {
|
|
26
|
+
const drive = msysDriveMatch[1].toUpperCase();
|
|
27
|
+
const rest = msysDriveMatch[2].replace(/\//g, '\\');
|
|
28
|
+
value = `${drive}:\\${rest}`;
|
|
29
|
+
} else if (/^\/Users\//i.test(value)) {
|
|
30
|
+
const drive = (env.SYSTEMDRIVE || env.HOMEDRIVE || 'C:').replace(/\\+$/, '');
|
|
31
|
+
const rest = value.replace(/^\/+/, '').replace(/\//g, '\\');
|
|
32
|
+
value = `${drive}\\${rest}`;
|
|
33
|
+
} else {
|
|
34
|
+
value = value.replace(/\//g, '\\');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (/^[a-zA-Z]:[^\\]/.test(value)) {
|
|
38
|
+
value = `${value.slice(0, 2)}\\${value.slice(2)}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!path.win32.isAbsolute(value)) {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return path.win32.normalize(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isGitInstallHomePath(homePath) {
|
|
49
|
+
return /[\\/]Program Files[\\/]Git[\\/]Users[\\/]/i.test(homePath || '');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolvePreferredHomeDir(platform = process.platform, env = process.env, fallbackHome = '') {
|
|
53
|
+
if (!isWindowsLikePlatform(platform, env)) {
|
|
54
|
+
return fallbackHome;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const candidates = [];
|
|
58
|
+
if (env.USERPROFILE) {
|
|
59
|
+
candidates.push(env.USERPROFILE);
|
|
60
|
+
}
|
|
61
|
+
if (env.HOMEDRIVE && env.HOMEPATH) {
|
|
62
|
+
candidates.push(`${env.HOMEDRIVE}${env.HOMEPATH}`);
|
|
63
|
+
}
|
|
64
|
+
if (env.HOME) {
|
|
65
|
+
candidates.push(env.HOME);
|
|
66
|
+
}
|
|
67
|
+
candidates.push(fallbackHome);
|
|
68
|
+
|
|
69
|
+
const normalizedCandidates = candidates
|
|
70
|
+
.map(candidate => normalizeWindowsHomePath(candidate, env))
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
|
|
73
|
+
const preferredHome = normalizedCandidates.find(candidate => !isGitInstallHomePath(candidate));
|
|
74
|
+
return preferredHome || normalizedCandidates[0] || fallbackHome;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
isWindowsLikePlatform,
|
|
79
|
+
normalizeWindowsHomePath,
|
|
80
|
+
resolvePreferredHomeDir,
|
|
81
|
+
isGitInstallHomePath
|
|
82
|
+
};
|
package/src/utils/port-helper.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
const { execSync } = require('child_process');
|
|
2
2
|
const net = require('net');
|
|
3
|
+
const { isWindowsLikePlatform } = require('./home-dir');
|
|
4
|
+
|
|
5
|
+
function isWindowsLikeRuntime(platform = process.platform, env = process.env) {
|
|
6
|
+
return isWindowsLikePlatform(platform, env);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function parsePidsFromNetstatOutput(output, port) {
|
|
10
|
+
const target = `:${port}`;
|
|
11
|
+
const pids = new Set();
|
|
12
|
+
|
|
13
|
+
String(output || '').split(/\r?\n/).forEach((line) => {
|
|
14
|
+
const row = line.trim();
|
|
15
|
+
if (!row || !row.includes(target)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (row.startsWith('UDP') && !row.includes(`${target} `) && !row.endsWith(target)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const match = row.match(/\s+(\d+)\s*$/);
|
|
22
|
+
if (match && match[1] !== '0') {
|
|
23
|
+
pids.add(match[1]);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return Array.from(pids);
|
|
28
|
+
}
|
|
3
29
|
|
|
4
30
|
/**
|
|
5
31
|
* 检查端口是否被占用
|
|
@@ -43,18 +69,12 @@ function isPortInUse(port, host = '127.0.0.1') {
|
|
|
43
69
|
* 查找占用端口的进程PID(跨平台)
|
|
44
70
|
*/
|
|
45
71
|
function findProcessByPort(port) {
|
|
46
|
-
const isWindows =
|
|
72
|
+
const isWindows = isWindowsLikeRuntime();
|
|
47
73
|
if (isWindows) {
|
|
48
74
|
try {
|
|
49
|
-
// Windows: netstat
|
|
50
|
-
const result = execSync(
|
|
51
|
-
|
|
52
|
-
result.split('\n').forEach(line => {
|
|
53
|
-
// 格式: " TCP 0.0.0.0:9999 0.0.0.0:0 LISTENING 1234"
|
|
54
|
-
const match = line.trim().match(/\s+(\d+)\s*$/);
|
|
55
|
-
if (match) pids.add(match[1]);
|
|
56
|
-
});
|
|
57
|
-
return Array.from(pids).filter(pid => pid && pid !== '0');
|
|
75
|
+
// Windows: 直接解析 netstat 输出,避免依赖 findstr/lsof
|
|
76
|
+
const result = execSync('netstat -ano', { encoding: 'utf-8' });
|
|
77
|
+
return parsePidsFromNetstatOutput(result, port);
|
|
58
78
|
} catch (e) {
|
|
59
79
|
return [];
|
|
60
80
|
}
|
|
@@ -85,7 +105,7 @@ function killProcessByPort(port) {
|
|
|
85
105
|
return false;
|
|
86
106
|
}
|
|
87
107
|
|
|
88
|
-
const isWindows =
|
|
108
|
+
const isWindows = isWindowsLikeRuntime();
|
|
89
109
|
pids.forEach(pid => {
|
|
90
110
|
try {
|
|
91
111
|
if (isWindows) {
|
|
@@ -125,5 +145,7 @@ module.exports = {
|
|
|
125
145
|
isPortInUse,
|
|
126
146
|
findProcessByPort,
|
|
127
147
|
killProcessByPort,
|
|
128
|
-
waitForPortRelease
|
|
148
|
+
waitForPortRelease,
|
|
149
|
+
isWindowsLikeRuntime,
|
|
150
|
+
parsePidsFromNetstatOutput
|
|
129
151
|
};
|