coding-tool-x 3.5.7 → 3.5.9
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-C6DEmD3D.js → Analytics-C5W3axXs.js} +2 -2
- package/dist/web/assets/Analytics-vQS5IWvs.css +1 -0
- package/dist/web/assets/{ConfigTemplates-Cf_iTpC4.js → ConfigTemplates-DzyVFDx9.js} +1 -1
- package/dist/web/assets/{Home-BtBmYLJ1.js → Home-C9TQNB6f.js} +1 -1
- package/dist/web/assets/Home-qzk118Of.css +1 -0
- package/dist/web/assets/{PluginManager-DEk8vSw5.js → PluginManager-9B_brLWT.js} +1 -1
- package/dist/web/assets/ProjectList-Bjt6mrsV.js +1 -0
- package/dist/web/assets/ProjectList-GCC2QOmq.css +1 -0
- package/dist/web/assets/SessionList-BsHPgmUR.css +1 -0
- package/dist/web/assets/SessionList-DcBH13uA.js +1 -0
- package/dist/web/assets/{SkillManager-DcZOiiSf.js → SkillManager-vST8DRRg.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-BHqI8aGV.js → WorkspaceManager-ov1KgRXR.js} +1 -1
- package/dist/web/assets/icons-CEq2hYB-.js +1 -0
- package/dist/web/assets/index-Dih_bOsv.css +1 -0
- package/dist/web/assets/index-Duc7QP4e.js +2 -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/package.json +1 -1
- package/src/commands/export-config.js +6 -6
- package/src/config/default.js +2 -6
- package/src/config/loader.js +2 -2
- package/src/config/paths.js +160 -33
- package/src/server/api/agents.js +52 -2
- package/src/server/api/codex-sessions.js +4 -2
- package/src/server/api/commands.js +38 -2
- package/src/server/api/opencode-sessions.js +4 -2
- package/src/server/api/plugins.js +104 -1
- package/src/server/api/sessions.js +9 -7
- 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/mcp-service.js +9 -14
- package/src/server/services/native-oauth-adapters.js +3 -3
- package/src/server/services/notification-hooks.js +3 -3
- package/src/server/services/opencode-sessions.js +16 -6
- 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/session-launch-command.js +1 -24
- package/src/server/services/sessions.js +91 -40
- package/src/server/services/skill-service.js +155 -18
- package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
- package/dist/web/assets/Home-BQxQ1LhR.css +0 -1
- package/dist/web/assets/ProjectList-BMVhA_Kh.js +0 -1
- package/dist/web/assets/ProjectList-DL4JK6ci.css +0 -1
- package/dist/web/assets/SessionList-B5ioAXxg.js +0 -1
- package/dist/web/assets/SessionList-B8dXVXfi.css +0 -1
- package/dist/web/assets/icons-CQuif85v.js +0 -1
- package/dist/web/assets/index-CtByKdkA.js +0 -2
- package/dist/web/assets/index-VGAxnLqi.css +0 -1
- package/dist/web/assets/vue-vendor-aWwwFAao.js +0 -45
|
@@ -16,21 +16,43 @@
|
|
|
16
16
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
|
-
const os = require('os');
|
|
20
19
|
const toml = require('toml');
|
|
21
20
|
const tomlStringify = require('@iarna/toml').stringify;
|
|
22
21
|
const { convertSkillToCodex, convertCommandToCodex } = require('./format-converter');
|
|
23
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
PATHS,
|
|
24
|
+
NATIVE_PATHS,
|
|
25
|
+
HOME_DIR,
|
|
26
|
+
ensureStorageDirMigrated,
|
|
27
|
+
getNativePlatformDir,
|
|
28
|
+
isWindowsAbsolutePath,
|
|
29
|
+
isNativeAbsolutePath,
|
|
30
|
+
joinNativeBasePath,
|
|
31
|
+
resolveNativeBasePath,
|
|
32
|
+
getNativePathDir
|
|
33
|
+
} = require('../../config/paths');
|
|
24
34
|
|
|
25
|
-
// Paths
|
|
26
|
-
const HOME = HOME_DIR || os.homedir();
|
|
27
35
|
const CC_TOOL_CONFIGS = PATHS.configs;
|
|
28
|
-
const CLAUDE_CODE_DIR =
|
|
29
|
-
const CODEX_DIR =
|
|
30
|
-
const GEMINI_DIR =
|
|
36
|
+
const CLAUDE_CODE_DIR = getNativePlatformDir('claude');
|
|
37
|
+
const CODEX_DIR = getNativePlatformDir('codex');
|
|
38
|
+
const GEMINI_DIR = getNativePlatformDir('gemini');
|
|
31
39
|
const OPENCODE_DIR = NATIVE_PATHS.opencode.config;
|
|
32
40
|
const CODEX_CONFIG_PATH = NATIVE_PATHS.codex.config;
|
|
33
41
|
|
|
42
|
+
function isPathInsideNativeDir(targetPath, baseDir) {
|
|
43
|
+
const shouldFoldCase = isWindowsAbsolutePath(baseDir);
|
|
44
|
+
const normalize = (value) => {
|
|
45
|
+
const normalized = String(value || '')
|
|
46
|
+
.replace(/[\\/]+/g, '/')
|
|
47
|
+
.replace(/\/+$/, '');
|
|
48
|
+
return shouldFoldCase ? normalized.toLowerCase() : normalized;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const normalizedTarget = normalize(targetPath);
|
|
52
|
+
const normalizedBase = normalize(baseDir);
|
|
53
|
+
return normalizedTarget === normalizedBase || normalizedTarget.startsWith(`${normalizedBase}/`);
|
|
54
|
+
}
|
|
55
|
+
|
|
34
56
|
// Config type definitions
|
|
35
57
|
const CONFIG_TYPES = {
|
|
36
58
|
skills: {
|
|
@@ -108,7 +130,7 @@ class ConfigSyncManager {
|
|
|
108
130
|
return { success: false, error: 'Invalid config item name' };
|
|
109
131
|
}
|
|
110
132
|
const sourcePath = path.join(this.ccToolConfigs, type, safeName);
|
|
111
|
-
const targetPath =
|
|
133
|
+
const targetPath = joinNativeBasePath(this.claudeDir, config.claudeTarget, safeName);
|
|
112
134
|
|
|
113
135
|
// Check if source exists
|
|
114
136
|
if (!fs.existsSync(sourcePath)) {
|
|
@@ -119,12 +141,12 @@ class ConfigSyncManager {
|
|
|
119
141
|
try {
|
|
120
142
|
if (config.isDirectory) {
|
|
121
143
|
// Copy entire directory recursively
|
|
122
|
-
this._ensureDir(
|
|
144
|
+
this._ensureDir(getNativePathDir(targetPath));
|
|
123
145
|
this._copyDirRecursive(sourcePath, targetPath);
|
|
124
146
|
console.log(`[ConfigSyncManager] Synced ${type}/${name} to Claude Code (directory)`);
|
|
125
147
|
} else {
|
|
126
148
|
// Copy single file, preserving subdirectory structure
|
|
127
|
-
this._ensureDir(
|
|
149
|
+
this._ensureDir(getNativePathDir(targetPath));
|
|
128
150
|
this._copyFile(sourcePath, targetPath);
|
|
129
151
|
console.log(`[ConfigSyncManager] Synced ${type}/${name} to Claude Code (file)`);
|
|
130
152
|
}
|
|
@@ -152,7 +174,7 @@ class ConfigSyncManager {
|
|
|
152
174
|
if (!safeName) {
|
|
153
175
|
return { success: false, error: 'Invalid config item name' };
|
|
154
176
|
}
|
|
155
|
-
const targetPath =
|
|
177
|
+
const targetPath = joinNativeBasePath(this.claudeDir, config.claudeTarget, safeName);
|
|
156
178
|
|
|
157
179
|
if (!fs.existsSync(targetPath)) {
|
|
158
180
|
console.log(`[ConfigSyncManager] Target not found (already removed): ${targetPath}`);
|
|
@@ -170,7 +192,7 @@ class ConfigSyncManager {
|
|
|
170
192
|
console.log(`[ConfigSyncManager] Removed ${type}/${name} from Claude Code (file)`);
|
|
171
193
|
|
|
172
194
|
// Clean up empty parent directories for file-based configs
|
|
173
|
-
this._cleanupEmptyParents(
|
|
195
|
+
this._cleanupEmptyParents(getNativePathDir(targetPath), joinNativeBasePath(this.claudeDir, config.claudeTarget));
|
|
174
196
|
}
|
|
175
197
|
|
|
176
198
|
return { success: true };
|
|
@@ -260,7 +282,7 @@ class ConfigSyncManager {
|
|
|
260
282
|
|
|
261
283
|
if (type === 'skills') {
|
|
262
284
|
// Skills: copy directory, convert SKILL.md content
|
|
263
|
-
const targetPath =
|
|
285
|
+
const targetPath = joinNativeBasePath(this.codexDir, config.codexTarget, safeName);
|
|
264
286
|
this._ensureDir(targetPath);
|
|
265
287
|
|
|
266
288
|
// Copy all files, converting SKILL.md
|
|
@@ -288,8 +310,8 @@ class ConfigSyncManager {
|
|
|
288
310
|
}
|
|
289
311
|
|
|
290
312
|
// Target path in codex prompts (same relative path structure)
|
|
291
|
-
const targetPath =
|
|
292
|
-
this._ensureDir(
|
|
313
|
+
const targetPath = joinNativeBasePath(this.codexDir, config.codexTarget, safeName);
|
|
314
|
+
this._ensureDir(getNativePathDir(targetPath));
|
|
293
315
|
fs.writeFileSync(targetPath, result.content, 'utf-8');
|
|
294
316
|
|
|
295
317
|
console.log(`[ConfigSyncManager] Synced ${type}/${name} to Codex (prompt)`);
|
|
@@ -354,7 +376,7 @@ class ConfigSyncManager {
|
|
|
354
376
|
}
|
|
355
377
|
}
|
|
356
378
|
|
|
357
|
-
const targetPath =
|
|
379
|
+
const targetPath = joinNativeBasePath(this.codexDir, config.codexTarget, safeName);
|
|
358
380
|
|
|
359
381
|
if (!fs.existsSync(targetPath)) {
|
|
360
382
|
console.log(`[ConfigSyncManager] Target not found (already removed): ${targetPath}`);
|
|
@@ -372,7 +394,7 @@ class ConfigSyncManager {
|
|
|
372
394
|
console.log(`[ConfigSyncManager] Removed ${type}/${name} from Codex (prompt)`);
|
|
373
395
|
|
|
374
396
|
// Clean up empty parent directories
|
|
375
|
-
this._cleanupEmptyParents(
|
|
397
|
+
this._cleanupEmptyParents(getNativePathDir(targetPath), joinNativeBasePath(this.codexDir, config.codexTarget));
|
|
376
398
|
}
|
|
377
399
|
|
|
378
400
|
return { success: true };
|
|
@@ -411,8 +433,8 @@ class ConfigSyncManager {
|
|
|
411
433
|
}
|
|
412
434
|
|
|
413
435
|
try {
|
|
414
|
-
const targetPath =
|
|
415
|
-
this._ensureDir(
|
|
436
|
+
const targetPath = joinNativeBasePath(this.geminiDir, config.geminiTarget, safeName);
|
|
437
|
+
this._ensureDir(getNativePathDir(targetPath));
|
|
416
438
|
this._copyDirRecursive(sourcePath, targetPath);
|
|
417
439
|
console.log(`[ConfigSyncManager] Synced ${type}/${name} to Gemini (directory)`);
|
|
418
440
|
return { success: true, target: targetPath };
|
|
@@ -443,7 +465,7 @@ class ConfigSyncManager {
|
|
|
443
465
|
return { success: true, skipped: true, reason: 'Not supported by Gemini' };
|
|
444
466
|
}
|
|
445
467
|
|
|
446
|
-
const targetPath =
|
|
468
|
+
const targetPath = joinNativeBasePath(this.geminiDir, config.geminiTarget, safeName);
|
|
447
469
|
if (!fs.existsSync(targetPath)) {
|
|
448
470
|
console.log(`[ConfigSyncManager] Target not found (already removed): ${targetPath}`);
|
|
449
471
|
return { success: true, message: 'Already removed' };
|
|
@@ -489,14 +511,14 @@ class ConfigSyncManager {
|
|
|
489
511
|
|
|
490
512
|
try {
|
|
491
513
|
const targetBaseDir = this._getOpenCodeTypeBaseDir(config);
|
|
492
|
-
const targetPath =
|
|
514
|
+
const targetPath = joinNativeBasePath(targetBaseDir, safeName);
|
|
493
515
|
|
|
494
516
|
if (config.isDirectory) {
|
|
495
|
-
this._ensureDir(
|
|
517
|
+
this._ensureDir(getNativePathDir(targetPath));
|
|
496
518
|
this._copyDirRecursive(sourcePath, targetPath);
|
|
497
519
|
console.log(`[ConfigSyncManager] Synced ${type}/${name} to OpenCode (directory)`);
|
|
498
520
|
} else {
|
|
499
|
-
this._ensureDir(
|
|
521
|
+
this._ensureDir(getNativePathDir(targetPath));
|
|
500
522
|
this._copyFile(sourcePath, targetPath);
|
|
501
523
|
console.log(`[ConfigSyncManager] Synced ${type}/${name} to OpenCode (file)`);
|
|
502
524
|
}
|
|
@@ -544,7 +566,7 @@ class ConfigSyncManager {
|
|
|
544
566
|
} else {
|
|
545
567
|
fs.unlinkSync(targetPath);
|
|
546
568
|
console.log(`[ConfigSyncManager] Removed ${type}/${name} from OpenCode (file)`);
|
|
547
|
-
this._cleanupEmptyParents(
|
|
569
|
+
this._cleanupEmptyParents(getNativePathDir(targetPath), targetBaseDir);
|
|
548
570
|
}
|
|
549
571
|
|
|
550
572
|
return { success: true };
|
|
@@ -744,7 +766,7 @@ class ConfigSyncManager {
|
|
|
744
766
|
* OpenCode supports both plural (new) and singular (legacy) folder names.
|
|
745
767
|
*/
|
|
746
768
|
_getOpenCodeTypeBaseDir(config) {
|
|
747
|
-
const modernDir =
|
|
769
|
+
const modernDir = joinNativeBasePath(this.opencodeDir, config.opencodeTarget);
|
|
748
770
|
// 技能目录强制使用 modern/plural 形式,避免 legacy 目录带来的跨平台历史污染
|
|
749
771
|
if (config === this.configTypes.skills) {
|
|
750
772
|
return modernDir;
|
|
@@ -754,7 +776,7 @@ class ConfigSyncManager {
|
|
|
754
776
|
return modernDir;
|
|
755
777
|
}
|
|
756
778
|
|
|
757
|
-
const legacyDir =
|
|
779
|
+
const legacyDir = joinNativeBasePath(this.opencodeDir, config.opencodeLegacyTarget);
|
|
758
780
|
if (fs.existsSync(legacyDir) && !fs.existsSync(modernDir)) {
|
|
759
781
|
return legacyDir;
|
|
760
782
|
}
|
|
@@ -777,12 +799,11 @@ class ConfigSyncManager {
|
|
|
777
799
|
* Clean up empty parent directories up to the base directory
|
|
778
800
|
*/
|
|
779
801
|
_cleanupEmptyParents(dir, baseDir) {
|
|
780
|
-
|
|
781
|
-
const
|
|
782
|
-
const normalizedBase = path.resolve(baseDir);
|
|
802
|
+
const normalizedDir = resolveNativeBasePath(dir);
|
|
803
|
+
const normalizedBase = resolveNativeBasePath(baseDir);
|
|
783
804
|
|
|
784
805
|
// Don't go above base directory
|
|
785
|
-
if (!normalizedDir
|
|
806
|
+
if (!isPathInsideNativeDir(normalizedDir, normalizedBase) || normalizedDir === normalizedBase) {
|
|
786
807
|
return;
|
|
787
808
|
}
|
|
788
809
|
|
|
@@ -792,7 +813,7 @@ class ConfigSyncManager {
|
|
|
792
813
|
fs.rmdirSync(dir);
|
|
793
814
|
console.log(`[ConfigSyncManager] Removed empty directory: ${dir}`);
|
|
794
815
|
// Recurse to parent
|
|
795
|
-
this._cleanupEmptyParents(
|
|
816
|
+
this._cleanupEmptyParents(getNativePathDir(dir), baseDir);
|
|
796
817
|
}
|
|
797
818
|
} catch (err) {
|
|
798
819
|
// Ignore errors (directory might not exist or permission issues)
|
|
@@ -810,7 +831,7 @@ class ConfigSyncManager {
|
|
|
810
831
|
return null;
|
|
811
832
|
}
|
|
812
833
|
|
|
813
|
-
if (
|
|
834
|
+
if (isNativeAbsolutePath(raw) || raw.startsWith('/')) {
|
|
814
835
|
return null;
|
|
815
836
|
}
|
|
816
837
|
|
|
@@ -860,35 +881,34 @@ class ConfigSyncManager {
|
|
|
860
881
|
if (!normalized) return '';
|
|
861
882
|
|
|
862
883
|
if (normalized.startsWith('~/')) {
|
|
863
|
-
return
|
|
884
|
+
return joinNativeBasePath(HOME_DIR, normalized.slice(2));
|
|
864
885
|
}
|
|
865
886
|
|
|
866
|
-
if (
|
|
887
|
+
if (isNativeAbsolutePath(normalized)) {
|
|
867
888
|
return normalized;
|
|
868
889
|
}
|
|
869
890
|
|
|
870
|
-
return
|
|
891
|
+
return resolveNativeBasePath(CODEX_DIR, normalized);
|
|
871
892
|
}
|
|
872
893
|
|
|
873
894
|
_isManagedCodexAgentConfigPath(configPath) {
|
|
874
895
|
const resolved = this._resolveCodexPath(configPath);
|
|
875
896
|
if (!resolved) return false;
|
|
876
897
|
|
|
877
|
-
|
|
878
|
-
return resolved.startsWith(managedRoot) || resolved === path.resolve(this._getCodexManagedAgentConfigDir());
|
|
898
|
+
return isPathInsideNativeDir(resolved, this._getCodexManagedAgentConfigDir());
|
|
879
899
|
}
|
|
880
900
|
|
|
881
901
|
_getCodexManagedAgentConfigDir() {
|
|
882
|
-
return
|
|
902
|
+
return NATIVE_PATHS.codex.agents;
|
|
883
903
|
}
|
|
884
904
|
|
|
885
905
|
_getCodexManagedAgentConfigPath(fileName) {
|
|
886
|
-
return
|
|
906
|
+
return joinNativeBasePath(this._getCodexManagedAgentConfigDir(), `${fileName}.toml`);
|
|
887
907
|
}
|
|
888
908
|
|
|
889
909
|
_writeCodexAgentConfigFile(configPath, data) {
|
|
890
910
|
const resolved = this._resolveCodexPath(configPath);
|
|
891
|
-
this._ensureDir(
|
|
911
|
+
this._ensureDir(getNativePathDir(resolved));
|
|
892
912
|
const tempPath = `${resolved}.tmp-${process.pid}-${Date.now()}`;
|
|
893
913
|
fs.writeFileSync(tempPath, tomlStringify(data), 'utf-8');
|
|
894
914
|
fs.renameSync(tempPath, resolved);
|
|
@@ -923,7 +943,7 @@ class ConfigSyncManager {
|
|
|
923
943
|
}
|
|
924
944
|
|
|
925
945
|
_writeCodexConfigToml(config) {
|
|
926
|
-
this._ensureDir(
|
|
946
|
+
this._ensureDir(getNativePathDir(CODEX_CONFIG_PATH));
|
|
927
947
|
const tempPath = `${CODEX_CONFIG_PATH}.tmp-${process.pid}-${Date.now()}`;
|
|
928
948
|
fs.writeFileSync(tempPath, tomlStringify(config), 'utf-8');
|
|
929
949
|
fs.renameSync(tempPath, CODEX_CONFIG_PATH);
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
|
-
const {
|
|
18
|
+
const { NATIVE_PATHS, joinNativeBasePath } = require('../../config/paths');
|
|
19
19
|
|
|
20
20
|
// 全局配置目录
|
|
21
|
-
const GLOBAL_CONFIG_DIR =
|
|
21
|
+
const GLOBAL_CONFIG_DIR = NATIVE_PATHS.claude.dir;
|
|
22
22
|
|
|
23
23
|
// 配置类型定义
|
|
24
24
|
const CONFIG_TYPES = {
|
|
@@ -96,7 +96,7 @@ class ConfigSyncService {
|
|
|
96
96
|
let dir;
|
|
97
97
|
|
|
98
98
|
if (source === 'global') {
|
|
99
|
-
dir =
|
|
99
|
+
dir = joinNativeBasePath(this.globalConfigDir, config.globalDir);
|
|
100
100
|
} else if (source === 'workspace' && projectPath) {
|
|
101
101
|
if (!config.projectDir) {
|
|
102
102
|
// skills 不支持项目级
|
|
@@ -22,7 +22,7 @@ const { normalizeGatewaySourceType } = require('./base/proxy-utils');
|
|
|
22
22
|
|
|
23
23
|
// 获取 Gemini 配置目录
|
|
24
24
|
function getGeminiDir() {
|
|
25
|
-
return
|
|
25
|
+
return NATIVE_PATHS.gemini.dir;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// 获取渠道存储文件路径
|
|
@@ -35,7 +35,7 @@ function getChannelsFilePath() {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function readExistingGeminiEnv() {
|
|
38
|
-
const envPath =
|
|
38
|
+
const envPath = NATIVE_PATHS.gemini.env;
|
|
39
39
|
if (!fs.existsSync(envPath)) {
|
|
40
40
|
return {};
|
|
41
41
|
}
|
|
@@ -60,7 +60,7 @@ function readExistingGeminiEnv() {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function writeGeminiEnv(env = {}) {
|
|
63
|
-
const envPath =
|
|
63
|
+
const envPath = NATIVE_PATHS.gemini.env;
|
|
64
64
|
const content = Object.entries(env)
|
|
65
65
|
.map(([key, value]) => `${key}=${value}`)
|
|
66
66
|
.join('\n');
|
|
@@ -73,7 +73,7 @@ function writeGeminiEnv(env = {}) {
|
|
|
73
73
|
|
|
74
74
|
// 检查是否在代理模式
|
|
75
75
|
function isProxyConfig() {
|
|
76
|
-
const envPath =
|
|
76
|
+
const envPath = NATIVE_PATHS.gemini.env;
|
|
77
77
|
if (!fs.existsSync(envPath)) {
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
@@ -128,7 +128,7 @@ function loadChannels() {
|
|
|
128
128
|
|
|
129
129
|
// 从现有 .env 初始化渠道
|
|
130
130
|
function initializeFromEnv() {
|
|
131
|
-
const envPath =
|
|
131
|
+
const envPath = NATIVE_PATHS.gemini.env;
|
|
132
132
|
|
|
133
133
|
const defaultData = { channels: [] };
|
|
134
134
|
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
2
|
const { NATIVE_PATHS } = require('../../config/paths');
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* 获取 Gemini 配置目录
|
|
7
6
|
*/
|
|
8
7
|
function getGeminiDir() {
|
|
9
|
-
return
|
|
8
|
+
return NATIVE_PATHS.gemini.dir;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* 读取 .env 文件
|
|
14
13
|
*/
|
|
15
14
|
function loadEnv() {
|
|
16
|
-
const envPath =
|
|
15
|
+
const envPath = NATIVE_PATHS.gemini.env;
|
|
17
16
|
|
|
18
17
|
if (!fs.existsSync(envPath)) {
|
|
19
18
|
return {};
|
|
@@ -44,7 +43,7 @@ function loadEnv() {
|
|
|
44
43
|
* 读取 settings.json
|
|
45
44
|
*/
|
|
46
45
|
function loadSettings() {
|
|
47
|
-
const settingsPath =
|
|
46
|
+
const settingsPath = NATIVE_PATHS.gemini.settings;
|
|
48
47
|
|
|
49
48
|
if (!fs.existsSync(settingsPath)) {
|
|
50
49
|
return {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
-
const { HOME_DIR } = require('../../config/paths');
|
|
4
|
+
const { HOME_DIR, NATIVE_PATHS, joinNativeBasePath } = require('../../config/paths');
|
|
5
5
|
const { getGeminiDir } = require('./gemini-config');
|
|
6
6
|
|
|
7
7
|
// 路径映射缓存
|
|
@@ -13,7 +13,7 @@ const PATH_MAPPING_CACHE_TTL = 60000; // 1分钟缓存
|
|
|
13
13
|
* 获取 Gemini tmp 目录(包含所有项目)
|
|
14
14
|
*/
|
|
15
15
|
function getTmpDir() {
|
|
16
|
-
return
|
|
16
|
+
return NATIVE_PATHS?.gemini?.tmp || joinNativeBasePath(getGeminiDir(), 'tmp');
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -63,7 +63,7 @@ function scanDirForHashes(dir, targetHashes, maxDepth, results, currentDepth = 0
|
|
|
63
63
|
|
|
64
64
|
if (item.isDirectory()) {
|
|
65
65
|
scanDirForHashes(
|
|
66
|
-
|
|
66
|
+
joinNativeBasePath(dir, item.name),
|
|
67
67
|
targetHashes,
|
|
68
68
|
maxDepth,
|
|
69
69
|
results,
|
|
@@ -108,17 +108,17 @@ function buildPathMapping() {
|
|
|
108
108
|
// 深度说明:depth=3 表示可以扫描到 Desktop/a/b/c 这样的 4 层目录
|
|
109
109
|
const searchPaths = [
|
|
110
110
|
{ dir: homeDir, depth: 0 }, // 只检查 home 目录本身
|
|
111
|
-
{ dir:
|
|
112
|
-
{ dir:
|
|
113
|
-
{ dir:
|
|
114
|
-
{ dir:
|
|
115
|
-
{ dir:
|
|
116
|
-
{ dir:
|
|
117
|
-
{ dir:
|
|
118
|
-
{ dir:
|
|
119
|
-
{ dir:
|
|
120
|
-
{ dir:
|
|
121
|
-
{ dir:
|
|
111
|
+
{ dir: joinNativeBasePath(homeDir, 'Desktop'), depth: 4 }, // Desktop 及 4 层子目录
|
|
112
|
+
{ dir: joinNativeBasePath(homeDir, 'Documents'), depth: 4 }, // Documents 及 4 层子目录
|
|
113
|
+
{ dir: joinNativeBasePath(homeDir, 'Downloads'), depth: 3 }, // Downloads 及 3 层子目录
|
|
114
|
+
{ dir: joinNativeBasePath(homeDir, 'Projects'), depth: 4 }, // Projects 及 4 层子目录
|
|
115
|
+
{ dir: joinNativeBasePath(homeDir, 'Code'), depth: 4 }, // Code 及 4 层子目录
|
|
116
|
+
{ dir: joinNativeBasePath(homeDir, 'workspace'), depth: 4 }, // workspace 及 4 层子目录
|
|
117
|
+
{ dir: joinNativeBasePath(homeDir, 'dev'), depth: 4 }, // dev 及 4 层子目录
|
|
118
|
+
{ dir: joinNativeBasePath(homeDir, 'src'), depth: 4 }, // src 及 4 层子目录
|
|
119
|
+
{ dir: joinNativeBasePath(homeDir, 'work'), depth: 4 }, // work 及 4 层子目录
|
|
120
|
+
{ dir: joinNativeBasePath(homeDir, 'repos'), depth: 4 }, // repos 及 4 层子目录
|
|
121
|
+
{ dir: joinNativeBasePath(homeDir, 'github'), depth: 4 }, // github 及 4 层子目录
|
|
122
122
|
];
|
|
123
123
|
|
|
124
124
|
for (const { dir, depth } of searchPaths) {
|
|
@@ -164,7 +164,7 @@ function scanProjects() {
|
|
|
164
164
|
* @returns {Array} 会话文件路径数组
|
|
165
165
|
*/
|
|
166
166
|
function scanProjectSessions(projectHash) {
|
|
167
|
-
const chatsDir =
|
|
167
|
+
const chatsDir = joinNativeBasePath(getTmpDir(), projectHash, 'chats');
|
|
168
168
|
|
|
169
169
|
if (!fs.existsSync(chatsDir)) {
|
|
170
170
|
return [];
|
|
@@ -175,7 +175,7 @@ function scanProjectSessions(projectHash) {
|
|
|
175
175
|
return entries
|
|
176
176
|
.filter(entry => entry.isFile() && entry.name.match(/^session-.*\.json$/))
|
|
177
177
|
.map(entry => {
|
|
178
|
-
const filePath =
|
|
178
|
+
const filePath = joinNativeBasePath(chatsDir, entry.name);
|
|
179
179
|
// 文件名格式:session-2025-11-23T02-09-87570eb4.json
|
|
180
180
|
// session-{timestamp}-{shortId}.json
|
|
181
181
|
const match = entry.name.match(/^session-(.*)-([a-f0-9]+)\.json$/);
|
|
@@ -451,7 +451,7 @@ function deleteSession(sessionId) {
|
|
|
451
451
|
* @returns {Object} 删除结果
|
|
452
452
|
*/
|
|
453
453
|
function deleteProject(projectHash) {
|
|
454
|
-
const projectDir =
|
|
454
|
+
const projectDir = joinNativeBasePath(getTmpDir(), projectHash);
|
|
455
455
|
|
|
456
456
|
if (!fs.existsSync(projectDir)) {
|
|
457
457
|
throw new Error('Project not found');
|
|
@@ -634,8 +634,8 @@ function forkSession(sessionId) {
|
|
|
634
634
|
};
|
|
635
635
|
|
|
636
636
|
// 写入新文件
|
|
637
|
-
const chatsDir =
|
|
638
|
-
const newFilePath =
|
|
637
|
+
const chatsDir = joinNativeBasePath(getTmpDir(), sourceSession.projectHash, 'chats');
|
|
638
|
+
const newFilePath = joinNativeBasePath(chatsDir, newFileName);
|
|
639
639
|
|
|
640
640
|
try {
|
|
641
641
|
fs.writeFileSync(newFilePath, JSON.stringify(newSession, null, 2), 'utf8');
|
|
@@ -685,5 +685,8 @@ module.exports = {
|
|
|
685
685
|
saveSessionOrder,
|
|
686
686
|
forkSession,
|
|
687
687
|
getProjectPath,
|
|
688
|
-
getProjectAndSessionCounts
|
|
688
|
+
getProjectAndSessionCounts,
|
|
689
|
+
_test: {
|
|
690
|
+
getTmpDir
|
|
691
|
+
}
|
|
689
692
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
2
|
const { NATIVE_PATHS } = require('../../config/paths');
|
|
4
3
|
|
|
5
4
|
// Gemini 配置文件路径
|
|
@@ -8,7 +7,7 @@ function getEnvPath() {
|
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
function getSettingsPath() {
|
|
11
|
-
return
|
|
10
|
+
return NATIVE_PATHS.gemini.settings;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
// 备份文件路径
|
|
@@ -17,7 +16,7 @@ function getEnvBackupPath() {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
function getSettingsBackupPath() {
|
|
20
|
-
return
|
|
19
|
+
return NATIVE_PATHS.gemini.settingsBackup;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
// 检查配置文件是否存在
|
|
@@ -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)),
|
|
@@ -4,7 +4,7 @@ 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');
|
|
@@ -19,7 +19,7 @@ function normalizeType(type) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function ensureParentDir(filePath) {
|
|
22
|
-
const dir =
|
|
22
|
+
const dir = getNativePathDir(filePath);
|
|
23
23
|
if (!fs.existsSync(dir)) {
|
|
24
24
|
fs.mkdirSync(dir, { recursive: true });
|
|
25
25
|
}
|
|
@@ -269,7 +269,7 @@ function buildGeminiCommand(type) {
|
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
function getOpenCodeManagedPluginPath() {
|
|
272
|
-
return
|
|
272
|
+
return joinNativeBasePath(NATIVE_PATHS.opencode.plugins, MANAGED_OPENCODE_PLUGIN_FILE);
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
function buildOpenCodePluginContent(type) {
|