coding-tool-x 3.5.9 → 3.5.11
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-C5W3axXs.js → Analytics-Bw-0r4Ys.js} +2 -2
- package/dist/web/assets/Analytics-RNn1BUbG.css +1 -0
- package/dist/web/assets/{ConfigTemplates-DzyVFDx9.js → ConfigTemplates-C6QRIeDj.js} +1 -1
- package/dist/web/assets/Home-BQxQ1LhR.css +1 -0
- package/dist/web/assets/{Home-C9TQNB6f.js → Home-B_Z9ythw.js} +1 -1
- package/dist/web/assets/{PluginManager-9B_brLWT.js → PluginManager-Ce-mws6Q.js} +1 -1
- package/dist/web/assets/ProjectList-9Z7FFCIT.js +1 -0
- package/dist/web/assets/ProjectList-DL4JK6ci.css +1 -0
- package/dist/web/assets/SessionList-CyETQcs8.js +1 -0
- package/dist/web/assets/SessionList-D12muXvx.css +1 -0
- package/dist/web/assets/{SkillManager-vST8DRRg.js → SkillManager-DSUcP1se.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-ov1KgRXR.js → WorkspaceManager-Cvbc11bJ.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-CTeg1xG-.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-channels.js +4 -0
- package/src/server/api/codex-proxy.js +45 -2
- package/src/server/api/codex-sessions.js +89 -12
- 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 +83 -10
- package/src/server/api/plugins.js +34 -128
- package/src/server/api/proxy.js +5 -3
- package/src/server/api/sessions.js +181 -27
- 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 +88 -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 +59 -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 +6 -16
- 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 +171 -183
- 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-Bjt6mrsV.js +0 -1
- package/dist/web/assets/ProjectList-GCC2QOmq.css +0 -1
- package/dist/web/assets/SessionList-BsHPgmUR.css +0 -1
- package/dist/web/assets/SessionList-DcBH13uA.js +0 -1
- package/dist/web/assets/icons-CEq2hYB-.js +0 -1
- package/dist/web/assets/index-Dih_bOsv.css +0 -1
- package/dist/web/assets/index-Duc7QP4e.js +0 -2
- 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 -58
- package/src/server/services/web-ui-runtime.js +0 -54
|
@@ -11,6 +11,7 @@ const fs = require('fs');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const crypto = require('crypto');
|
|
13
13
|
const { normalizeGatewaySourceType, normalizeNumber } = require('./proxy-utils');
|
|
14
|
+
const { resolveChannelWebsiteUrl } = require('../../../config/channel-preset-websites');
|
|
14
15
|
|
|
15
16
|
class BaseChannelService {
|
|
16
17
|
/**
|
|
@@ -225,6 +226,7 @@ class BaseChannelService {
|
|
|
225
226
|
normalized.gatewaySourceType,
|
|
226
227
|
this.defaultGatewaySource
|
|
227
228
|
);
|
|
229
|
+
normalized.websiteUrl = resolveChannelWebsiteUrl(this.platform, normalized);
|
|
228
230
|
return normalized;
|
|
229
231
|
}
|
|
230
232
|
|
|
@@ -248,4 +250,4 @@ class BaseChannelService {
|
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
252
|
|
|
251
|
-
module.exports = BaseChannelService;
|
|
253
|
+
module.exports = BaseChannelService;
|
|
@@ -301,6 +301,10 @@ function updateChannel(id, updates) {
|
|
|
301
301
|
return service.updateChannel(id, updates);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
function markChannelAsRecentlyUsed(id) {
|
|
305
|
+
return service.updateChannel(id, {});
|
|
306
|
+
}
|
|
307
|
+
|
|
304
308
|
function deleteChannel(id) {
|
|
305
309
|
return service.deleteChannel(id);
|
|
306
310
|
}
|
|
@@ -323,6 +327,7 @@ module.exports = {
|
|
|
323
327
|
getCurrentSettings,
|
|
324
328
|
createChannel,
|
|
325
329
|
updateChannel,
|
|
330
|
+
markChannelAsRecentlyUsed,
|
|
326
331
|
deleteChannel,
|
|
327
332
|
applyChannelToSettings,
|
|
328
333
|
getBestChannelForRestore,
|
|
@@ -261,6 +261,7 @@ function createChannel(name, providerKey, baseUrl, apiKey, wireApi, extraConfig
|
|
|
261
261
|
});
|
|
262
262
|
}
|
|
263
263
|
function updateChannel(id, updates) { return service.updateChannel(id, updates); }
|
|
264
|
+
function markChannelAsRecentlyUsed(id) { return service.updateChannel(id, {}); }
|
|
264
265
|
function deleteChannel(id) { return service.deleteChannel(id); }
|
|
265
266
|
function saveChannelOrder(order) { return service.saveChannelOrder(order); }
|
|
266
267
|
function applyChannelToSettings(id) { return service.applyChannelToSettings(id); }
|
|
@@ -281,6 +282,7 @@ module.exports = {
|
|
|
281
282
|
getChannels,
|
|
282
283
|
createChannel,
|
|
283
284
|
updateChannel,
|
|
285
|
+
markChannelAsRecentlyUsed,
|
|
284
286
|
deleteChannel,
|
|
285
287
|
getEnabledChannels,
|
|
286
288
|
saveChannelOrder,
|
|
@@ -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,75 @@ 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;
|
|
900
834
|
}
|
|
901
835
|
|
|
836
|
+
const { lines, eol, hasTrailingEol } = splitTextPreserveEol(content);
|
|
837
|
+
const keptLines = [];
|
|
838
|
+
let matchedUserMessages = 0;
|
|
839
|
+
let targetUserReached = false;
|
|
840
|
+
|
|
841
|
+
for (const line of lines) {
|
|
842
|
+
if (!line.trim()) {
|
|
843
|
+
keptLines.push(line);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
let parsed;
|
|
848
|
+
try {
|
|
849
|
+
parsed = JSON.parse(line);
|
|
850
|
+
} catch (err) {
|
|
851
|
+
keptLines.push(line);
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (parsed?.type !== 'response_item' || !parsed?.payload) {
|
|
856
|
+
keptLines.push(line);
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const preview = extractCodexPreviewFromResponseItem(parsed.payload);
|
|
861
|
+
if (!preview) {
|
|
862
|
+
keptLines.push(line);
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (targetUserReached) {
|
|
867
|
+
return joinTextPreserveEol(keptLines, eol, hasTrailingEol);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
matchedUserMessages += 1;
|
|
871
|
+
keptLines.push(line);
|
|
872
|
+
if (matchedUserMessages >= afterUserMessageNumber) {
|
|
873
|
+
targetUserReached = true;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
if (targetUserReached) {
|
|
878
|
+
return joinTextPreserveEol(keptLines, eol, hasTrailingEol);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
throw new Error(`afterUserMessageNumber ${afterUserMessageNumber} exceeds available user messages (${matchedUserMessages})`);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function readSessionMetaSummaryFast(filePath) {
|
|
902
885
|
let fd;
|
|
903
886
|
try {
|
|
904
887
|
fd = fs.openSync(filePath, 'r');
|
|
@@ -936,9 +919,7 @@ function readSessionMetaSummaryFast(filePath, fileMeta = null) {
|
|
|
936
919
|
}
|
|
937
920
|
|
|
938
921
|
if (!payload) return null;
|
|
939
|
-
|
|
940
|
-
setCachedSessionMetaSummary(filePath, fileMeta, summary);
|
|
941
|
-
return summary;
|
|
922
|
+
return { payload, preview };
|
|
942
923
|
} catch (err) {
|
|
943
924
|
return null;
|
|
944
925
|
} finally {
|
|
@@ -952,8 +933,8 @@ function readSessionMetaSummaryFast(filePath, fileMeta = null) {
|
|
|
952
933
|
}
|
|
953
934
|
}
|
|
954
935
|
|
|
955
|
-
function readSessionMetaPayloadFast(filePath
|
|
956
|
-
const summary = readSessionMetaSummaryFast(filePath
|
|
936
|
+
function readSessionMetaPayloadFast(filePath) {
|
|
937
|
+
const summary = readSessionMetaSummaryFast(filePath);
|
|
957
938
|
return summary?.payload || null;
|
|
958
939
|
}
|
|
959
940
|
|
|
@@ -978,7 +959,7 @@ function calculateProjectAndSessionCounts() {
|
|
|
978
959
|
|
|
979
960
|
const projectNames = new Set();
|
|
980
961
|
sessions.forEach((session) => {
|
|
981
|
-
const payload = readSessionMetaPayloadFast(session.filePath
|
|
962
|
+
const payload = readSessionMetaPayloadFast(session.filePath);
|
|
982
963
|
const projectName = extractCodexProjectNameFromMeta(payload || {});
|
|
983
964
|
if (projectName) {
|
|
984
965
|
projectNames.add(projectName);
|
|
@@ -1029,6 +1010,5 @@ module.exports = {
|
|
|
1029
1010
|
saveSessionOrder,
|
|
1030
1011
|
getProjectOrder,
|
|
1031
1012
|
saveProjectOrder,
|
|
1032
|
-
getProjectAndSessionCounts
|
|
1033
|
-
rewriteForkedCodexSessionContent
|
|
1013
|
+
getProjectAndSessionCounts
|
|
1034
1014
|
};
|