coding-tool-x 3.5.8 → 3.5.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/CHANGELOG.md +11 -0
- package/README.md +4 -25
- package/bin/ctx.js +1 -6
- package/dist/web/assets/{Analytics-BzoNzfbi.js → Analytics-DPb1zErn.js} +2 -2
- package/dist/web/assets/Analytics-RNn1BUbG.css +1 -0
- package/dist/web/assets/{ConfigTemplates-O4ikBt1o.js → ConfigTemplates-Bki2k4Lo.js} +1 -1
- package/dist/web/assets/{Home-BQjsnblU.js → Home-BKYHKSax.js} +1 -1
- package/dist/web/assets/Home-BQxQ1LhR.css +1 -0
- package/dist/web/assets/{PluginManager-DS_DJnVc.js → PluginManager-jgeQOE24.js} +1 -1
- package/dist/web/assets/ProjectList-CF67RzP_.js +1 -0
- package/dist/web/assets/ProjectList-DL4JK6ci.css +1 -0
- package/dist/web/assets/SessionList-DhHsY5q9.js +1 -0
- package/dist/web/assets/SessionList-lz23U6ZJ.css +1 -0
- package/dist/web/assets/{SkillManager-DpNE02r0.js → SkillManager-CtEz5gGp.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-DMY7_SHh.js → WorkspaceManager-D3B0Y5XE.js} +1 -1
- package/dist/web/assets/icons-lKCTySl7.js +1 -0
- package/dist/web/assets/index-BS7aplYp.css +1 -0
- package/dist/web/assets/index-BnyNm74G.js +2 -0
- package/dist/web/assets/{markdown-DyTJGI4N.js → markdown-BNJfEgCA.js} +1 -1
- package/dist/web/assets/{naive-ui-Cg4_ZeoT.js → naive-ui-DM897mIP.js} +1 -1
- package/dist/web/assets/vendors-DQA4Hw9v.js +7 -0
- package/dist/web/assets/vue-vendor-B5RqjEjQ.js +45 -0
- package/dist/web/index.html +8 -8
- package/package.json +1 -2
- package/src/commands/daemon.js +5 -33
- package/src/commands/export-config.js +6 -6
- package/src/commands/stats.js +4 -41
- package/src/commands/ui.js +3 -12
- package/src/config/channel-preset-websites.js +114 -0
- package/src/config/default.js +9 -5
- package/src/config/loader.js +2 -2
- package/src/config/model-metadata.json +16 -20
- package/src/config/paths.js +33 -166
- package/src/index.js +34 -125
- package/src/server/api/agents.js +2 -52
- package/src/server/api/codex-proxy.js +4 -2
- package/src/server/api/codex-sessions.js +88 -9
- package/src/server/api/commands.js +2 -38
- package/src/server/api/dashboard.js +1 -25
- package/src/server/api/gemini-proxy.js +4 -2
- package/src/server/api/gemini-sessions.js +86 -9
- package/src/server/api/hooks.js +1 -17
- package/src/server/api/opencode-proxy.js +7 -2
- package/src/server/api/opencode-sessions.js +82 -7
- package/src/server/api/plugins.js +34 -128
- package/src/server/api/proxy.js +5 -3
- package/src/server/api/sessions.js +180 -24
- package/src/server/api/workspaces.js +0 -14
- package/src/server/index.js +9 -31
- package/src/server/services/agents-service.js +62 -269
- package/src/server/services/base/base-channel-service.js +3 -1
- package/src/server/services/channels.js +5 -0
- package/src/server/services/codex-channels.js +2 -0
- package/src/server/services/codex-sessions.js +78 -108
- package/src/server/services/commands-service.js +81 -281
- package/src/server/services/config-export-service.js +7 -7
- package/src/server/services/config-registry-service.js +9 -8
- package/src/server/services/config-sync-manager.js +41 -61
- package/src/server/services/config-sync-service.js +3 -3
- package/src/server/services/gemini-channels.js +17 -6
- package/src/server/services/gemini-config.js +4 -3
- package/src/server/services/gemini-sessions.js +51 -26
- package/src/server/services/gemini-settings-manager.js +3 -2
- package/src/server/services/mcp-service.js +14 -9
- package/src/server/services/native-oauth-adapters.js +3 -3
- package/src/server/services/network-access.js +2 -61
- package/src/server/services/notification-hooks.js +19 -184
- package/src/server/services/opencode-channels.js +9 -0
- 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 +62 -996
- package/src/server/services/prompts-service.js +9 -5
- package/src/server/services/sessions.js +128 -99
- package/src/server/services/skill-service.js +18 -155
- package/src/server/websocket-server.js +4 -35
- package/dist/web/assets/Analytics-vQS5IWvs.css +0 -1
- package/dist/web/assets/Home-qzk118Of.css +0 -1
- package/dist/web/assets/ProjectList-CqYDtsHx.js +0 -1
- package/dist/web/assets/ProjectList-GCC2QOmq.css +0 -1
- package/dist/web/assets/SessionList-CfPtcq6Y.css +0 -1
- package/dist/web/assets/SessionList-DMlLtMCz.js +0 -1
- package/dist/web/assets/icons-CEq2hYB-.js +0 -1
- package/dist/web/assets/index-Clf0l3wc.js +0 -2
- package/dist/web/assets/index-Dih_bOsv.css +0 -1
- package/dist/web/assets/vendors-Bsp-dq2d.js +0 -7
- package/dist/web/assets/vue-vendor-BxIT0uQq.js +0 -45
- package/docs/Caddyfile.example +0 -19
- package/docs/reverse-proxy-https.md +0 -57
- package/src/server/services/https-cert.js +0 -171
- package/src/server/services/session-launch-command.js +0 -81
- package/src/server/services/web-ui-runtime.js +0 -54
|
@@ -10,7 +10,6 @@ const ALL_SESSIONS_CACHE_TTL_MS = 20 * 1000;
|
|
|
10
10
|
const PROJECTS_CACHE_TTL_MS = 300 * 1000;
|
|
11
11
|
const PROJECT_SESSIONS_CACHE_TTL_MS = 120 * 1000;
|
|
12
12
|
const FAST_META_READ_BYTES = 64 * 1024;
|
|
13
|
-
const MAX_SESSION_META_SUMMARY_CACHE_ENTRIES = 5000;
|
|
14
13
|
const EMPTY_COUNTS = Object.freeze({ projectCount: 0, sessionCount: 0 });
|
|
15
14
|
|
|
16
15
|
let countsCache = {
|
|
@@ -33,8 +32,6 @@ let allSessionsCache = {
|
|
|
33
32
|
value: []
|
|
34
33
|
};
|
|
35
34
|
|
|
36
|
-
let sessionMetaSummaryCache = new Map();
|
|
37
|
-
|
|
38
35
|
const CODEX_PROJECTS_CACHE_KEY = `${CacheKeys.PROJECTS}codex`;
|
|
39
36
|
const codexSessionCacheKeys = new Set();
|
|
40
37
|
|
|
@@ -42,47 +39,6 @@ function getCodexSessionsCacheKey(projectName) {
|
|
|
42
39
|
return `${CacheKeys.SESSIONS}codex:${projectName}`;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
function getSessionMetaSummaryCacheKey(filePath, fileMeta = {}) {
|
|
46
|
-
return `${filePath}:${fileMeta.mtimeMs || 0}:${fileMeta.size || 0}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getCachedSessionMetaSummary(filePath, fileMeta) {
|
|
50
|
-
if (!fileMeta) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
return sessionMetaSummaryCache.get(getSessionMetaSummaryCacheKey(filePath, fileMeta)) || null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function setCachedSessionMetaSummary(filePath, fileMeta, summary) {
|
|
57
|
-
if (!fileMeta || !summary?.payload) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const cacheKey = getSessionMetaSummaryCacheKey(filePath, fileMeta);
|
|
62
|
-
sessionMetaSummaryCache.delete(cacheKey);
|
|
63
|
-
sessionMetaSummaryCache.set(cacheKey, summary);
|
|
64
|
-
|
|
65
|
-
while (sessionMetaSummaryCache.size > MAX_SESSION_META_SUMMARY_CACHE_ENTRIES) {
|
|
66
|
-
const oldestKey = sessionMetaSummaryCache.keys().next().value;
|
|
67
|
-
if (!oldestKey) {
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
sessionMetaSummaryCache.delete(oldestKey);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function pruneSessionMetaSummaryCache(files = []) {
|
|
75
|
-
const activeKeys = new Set(
|
|
76
|
-
files.map(file => getSessionMetaSummaryCacheKey(file.filePath, file))
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
for (const cacheKey of sessionMetaSummaryCache.keys()) {
|
|
80
|
-
if (!activeKeys.has(cacheKey)) {
|
|
81
|
-
sessionMetaSummaryCache.delete(cacheKey);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
42
|
/**
|
|
87
43
|
* 获取会话目录
|
|
88
44
|
*/
|
|
@@ -172,7 +128,6 @@ function scanSessionFiles() {
|
|
|
172
128
|
expiresAt,
|
|
173
129
|
value: new Map(parsed.map(file => [file.sessionId, file]))
|
|
174
130
|
};
|
|
175
|
-
pruneSessionMetaSummaryCache(parsed);
|
|
176
131
|
|
|
177
132
|
return parsed;
|
|
178
133
|
}
|
|
@@ -190,7 +145,7 @@ function getAllSessions() {
|
|
|
190
145
|
const files = scanSessionFiles();
|
|
191
146
|
|
|
192
147
|
const parsed = files.map(file => {
|
|
193
|
-
const fastSummary = readSessionMetaSummaryFast(file.filePath
|
|
148
|
+
const fastSummary = readSessionMetaSummaryFast(file.filePath);
|
|
194
149
|
let session = null;
|
|
195
150
|
|
|
196
151
|
if (fastSummary && fastSummary.payload) {
|
|
@@ -261,7 +216,7 @@ function normalizeSession(codexSession) {
|
|
|
261
216
|
filePath: filePath || '',
|
|
262
217
|
gitBranch: meta.git?.branch || null,
|
|
263
218
|
firstMessage: preview || null,
|
|
264
|
-
forkedFrom: null,
|
|
219
|
+
forkedFrom: null, // Codex 不支持 fork
|
|
265
220
|
|
|
266
221
|
// 额外的 Codex 特有字段(前端可能需要)
|
|
267
222
|
source: 'codex'
|
|
@@ -666,7 +621,7 @@ function deleteSession(sessionId) {
|
|
|
666
621
|
* @param {string} sessionId - 原会话 ID
|
|
667
622
|
* @returns {Object} Fork 结果 { newSessionId, forkedFrom }
|
|
668
623
|
*/
|
|
669
|
-
function forkSession(sessionId) {
|
|
624
|
+
function forkSession(sessionId, options = {}) {
|
|
670
625
|
const sourceFile = findSessionFileById(sessionId);
|
|
671
626
|
|
|
672
627
|
if (!sourceFile) {
|
|
@@ -674,7 +629,11 @@ function forkSession(sessionId) {
|
|
|
674
629
|
}
|
|
675
630
|
|
|
676
631
|
// 读取原会话文件内容
|
|
677
|
-
const
|
|
632
|
+
const originalContent = fs.readFileSync(sourceFile.filePath, 'utf8');
|
|
633
|
+
const content = sliceCodexContentByUserMessage(
|
|
634
|
+
originalContent,
|
|
635
|
+
options.afterUserMessageNumber
|
|
636
|
+
);
|
|
678
637
|
|
|
679
638
|
// 生成新的 session ID (使用 crypto.randomUUID 生成 v4 UUID)
|
|
680
639
|
const crypto = require('crypto');
|
|
@@ -700,70 +659,31 @@ function forkSession(sessionId) {
|
|
|
700
659
|
|
|
701
660
|
const newFileName = `rollout-${timestamp}-${newSessionId}.jsonl`;
|
|
702
661
|
const newFilePath = path.join(targetDir, newFileName);
|
|
703
|
-
const rewrittenContent = rewriteForkedCodexSessionContent(content, newSessionId, now.toISOString());
|
|
704
662
|
|
|
705
663
|
// 写入新文件
|
|
706
|
-
fs.writeFileSync(newFilePath,
|
|
664
|
+
fs.writeFileSync(newFilePath, content, 'utf8');
|
|
707
665
|
|
|
708
666
|
// 保存 fork 关系(复用 Claude Code 的 fork 关系存储)
|
|
709
667
|
const { getForkRelations, saveForkRelations } = require('./sessions');
|
|
710
668
|
const forkRelations = getForkRelations();
|
|
711
669
|
forkRelations[newSessionId] = sessionId;
|
|
712
670
|
saveForkRelations(forkRelations);
|
|
671
|
+
if (options.alias) {
|
|
672
|
+
const { setAlias } = require('./alias');
|
|
673
|
+
setAlias(newSessionId, options.alias);
|
|
674
|
+
}
|
|
713
675
|
|
|
714
676
|
invalidateProjectAndSessionCountsCache();
|
|
715
677
|
invalidateCodexSessionCaches();
|
|
716
678
|
return {
|
|
717
679
|
newSessionId,
|
|
718
680
|
forkedFrom: sessionId,
|
|
719
|
-
newFilePath
|
|
681
|
+
newFilePath,
|
|
682
|
+
alias: options.alias || null,
|
|
683
|
+
afterUserMessageNumber: options.afterUserMessageNumber || null
|
|
720
684
|
};
|
|
721
685
|
}
|
|
722
686
|
|
|
723
|
-
function rewriteForkedCodexSessionContent(content, newSessionId, nowIsoTimestamp) {
|
|
724
|
-
const lines = String(content || '').split('\n');
|
|
725
|
-
|
|
726
|
-
return lines.map((line) => {
|
|
727
|
-
if (!line.trim()) {
|
|
728
|
-
return line;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
let parsed;
|
|
732
|
-
try {
|
|
733
|
-
parsed = JSON.parse(line);
|
|
734
|
-
} catch (err) {
|
|
735
|
-
return line;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
if (parsed.type === 'session_meta' && parsed.payload && typeof parsed.payload === 'object') {
|
|
739
|
-
return JSON.stringify({
|
|
740
|
-
...parsed,
|
|
741
|
-
timestamp: nowIsoTimestamp,
|
|
742
|
-
payload: {
|
|
743
|
-
...parsed.payload,
|
|
744
|
-
id: newSessionId,
|
|
745
|
-
timestamp: nowIsoTimestamp
|
|
746
|
-
}
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
if (parsed.type === 'event' && parsed.event && typeof parsed.event === 'object' && parsed.event.type === 'session_start') {
|
|
751
|
-
const nextEvent = { ...parsed.event };
|
|
752
|
-
if (typeof nextEvent.session_id === 'string' && nextEvent.session_id.trim()) {
|
|
753
|
-
nextEvent.session_id = newSessionId;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
return JSON.stringify({
|
|
757
|
-
...parsed,
|
|
758
|
-
timestamp: nowIsoTimestamp,
|
|
759
|
-
event: nextEvent
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
return line;
|
|
764
|
-
}).join('\n');
|
|
765
|
-
}
|
|
766
|
-
|
|
767
687
|
/**
|
|
768
688
|
* 获取会话排序(按项目)
|
|
769
689
|
* @param {string} projectName - 项目名称
|
|
@@ -893,12 +813,65 @@ function extractCodexPreviewFromResponseItem(payload = {}) {
|
|
|
893
813
|
return text.substring(0, 100);
|
|
894
814
|
}
|
|
895
815
|
|
|
896
|
-
function
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
816
|
+
function splitTextPreserveEol(content) {
|
|
817
|
+
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
818
|
+
const hasTrailingEol = content.endsWith('\r\n') || content.endsWith('\n');
|
|
819
|
+
return {
|
|
820
|
+
lines: content.split(/\r?\n/),
|
|
821
|
+
eol,
|
|
822
|
+
hasTrailingEol
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function joinTextPreserveEol(lines, eol, hasTrailingEol) {
|
|
827
|
+
const text = lines.join(eol);
|
|
828
|
+
return hasTrailingEol ? `${text}${eol}` : text;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function sliceCodexContentByUserMessage(content, afterUserMessageNumber) {
|
|
832
|
+
if (!Number.isInteger(afterUserMessageNumber) || afterUserMessageNumber <= 0) {
|
|
833
|
+
return content;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const { lines, eol, hasTrailingEol } = splitTextPreserveEol(content);
|
|
837
|
+
const keptLines = [];
|
|
838
|
+
let matchedUserMessages = 0;
|
|
839
|
+
|
|
840
|
+
for (const line of lines) {
|
|
841
|
+
if (!line.trim()) {
|
|
842
|
+
keptLines.push(line);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
let parsed;
|
|
847
|
+
try {
|
|
848
|
+
parsed = JSON.parse(line);
|
|
849
|
+
} catch (err) {
|
|
850
|
+
keptLines.push(line);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
keptLines.push(line);
|
|
855
|
+
|
|
856
|
+
if (parsed?.type !== 'response_item' || !parsed?.payload) {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const preview = extractCodexPreviewFromResponseItem(parsed.payload);
|
|
861
|
+
if (!preview) {
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
matchedUserMessages += 1;
|
|
866
|
+
if (matchedUserMessages >= afterUserMessageNumber) {
|
|
867
|
+
return joinTextPreserveEol(keptLines, eol, hasTrailingEol);
|
|
868
|
+
}
|
|
900
869
|
}
|
|
901
870
|
|
|
871
|
+
throw new Error(`afterUserMessageNumber ${afterUserMessageNumber} exceeds available user messages (${matchedUserMessages})`);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function readSessionMetaSummaryFast(filePath) {
|
|
902
875
|
let fd;
|
|
903
876
|
try {
|
|
904
877
|
fd = fs.openSync(filePath, 'r');
|
|
@@ -936,9 +909,7 @@ function readSessionMetaSummaryFast(filePath, fileMeta = null) {
|
|
|
936
909
|
}
|
|
937
910
|
|
|
938
911
|
if (!payload) return null;
|
|
939
|
-
|
|
940
|
-
setCachedSessionMetaSummary(filePath, fileMeta, summary);
|
|
941
|
-
return summary;
|
|
912
|
+
return { payload, preview };
|
|
942
913
|
} catch (err) {
|
|
943
914
|
return null;
|
|
944
915
|
} finally {
|
|
@@ -952,8 +923,8 @@ function readSessionMetaSummaryFast(filePath, fileMeta = null) {
|
|
|
952
923
|
}
|
|
953
924
|
}
|
|
954
925
|
|
|
955
|
-
function readSessionMetaPayloadFast(filePath
|
|
956
|
-
const summary = readSessionMetaSummaryFast(filePath
|
|
926
|
+
function readSessionMetaPayloadFast(filePath) {
|
|
927
|
+
const summary = readSessionMetaSummaryFast(filePath);
|
|
957
928
|
return summary?.payload || null;
|
|
958
929
|
}
|
|
959
930
|
|
|
@@ -978,7 +949,7 @@ function calculateProjectAndSessionCounts() {
|
|
|
978
949
|
|
|
979
950
|
const projectNames = new Set();
|
|
980
951
|
sessions.forEach((session) => {
|
|
981
|
-
const payload = readSessionMetaPayloadFast(session.filePath
|
|
952
|
+
const payload = readSessionMetaPayloadFast(session.filePath);
|
|
982
953
|
const projectName = extractCodexProjectNameFromMeta(payload || {});
|
|
983
954
|
if (projectName) {
|
|
984
955
|
projectNames.add(projectName);
|
|
@@ -1029,6 +1000,5 @@ module.exports = {
|
|
|
1029
1000
|
saveSessionOrder,
|
|
1030
1001
|
getProjectOrder,
|
|
1031
1002
|
saveProjectOrder,
|
|
1032
|
-
getProjectAndSessionCounts
|
|
1033
|
-
rewriteForkedCodexSessionContent
|
|
1003
|
+
getProjectAndSessionCounts
|
|
1034
1004
|
};
|