cc-viewer 1.6.323 → 1.6.324
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/assets/{App-BxLvhawm.js → App-DxhvxOv5.js} +2 -2
- package/dist/assets/{MdxEditorPanel-Bsdt2-sG.js → MdxEditorPanel-Cvce-OZ1.js} +1 -1
- package/dist/assets/{Mobile-NzzhDBV0.js → Mobile-eQkx6wDh.js} +1 -1
- package/dist/assets/{_baseUniq-CKn860hN.js → _baseUniq-Bd-sKzQW.js} +1 -1
- package/dist/assets/{arc-C4-SwDEU.js → arc-D_6rT0rI.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-zNU8vGgf.js → architectureDiagram-Q4EWVU46-ClH0N8bK.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-Blq7VyAY.js → blockDiagram-DXYQGD6D-VBVbHV93.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-CilEO7Pu.js → c4Diagram-AHTNJAMY-CHd3FVhr.js} +1 -1
- package/dist/assets/{channel-K9LDJtR8.js → channel-DtAIDDMS.js} +1 -1
- package/dist/assets/{chunk-4BX2VUAB-DB6nsrqZ.js → chunk-4BX2VUAB-ldBMt7Zz.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-O_q60rT5.js → chunk-4TB4RGXK-BpaavRFi.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-D4PJNgqD.js → chunk-55IACEB6-CCQR_GqB.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-T78XNZnN.js → chunk-EDXVE4YY-B94gGHJo.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-BMajr_WJ.js → chunk-FMBD7UC4-D2pvMCHW.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-BLXJEzI0.js → chunk-OYMX7WX6-BC1K8PFw.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-C4ozCDaC.js → chunk-QZHKN3VN-BlKIHWPg.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-xsMRpY5W.js → chunk-YZCP3GAM-CO46Dpuq.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BVr-1vS2.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-BVr-1vS2.js +1 -0
- package/dist/assets/clone-BLbDEtop.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-BRm6sjuX.js → cose-bilkent-S5V4N54A-UW2sc3aR.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-CapXy71H.js → dagre-KV5264BT-nawhI5CM.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-CHaUXNHL.js → diagram-5BDNPKRD-BrLDz-85.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-YInXouX7.js → diagram-G4DWMVQ6-DDT86i03.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-Bb9563xL.js → diagram-MMDJMWI5-BQMzmHLU.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-BRYSGtnv.js → diagram-TYMM5635-BX7-PwHQ.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-DiYsrHLg.js → erDiagram-SMLLAGMA-IwLVLNv0.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CNZRDRgn.js → flowDiagram-DWJPFMVM-B8_X77WK.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-D9QY8NPb.js → ganttDiagram-T4ZO3ILL-DgeHpfKD.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-R_yUkND4.js → gitGraphDiagram-UUTBAWPF-GImqd44T.js} +1 -1
- package/dist/assets/{graph-Bp7rzoPN.js → graph-BFNvFrIw.js} +1 -1
- package/dist/assets/{index-C2X1QxuP.js → index-CG43hSOO.js} +1 -1
- package/dist/assets/{index-skAVyRFx.js → index-CL43Q_T6.js} +1 -1
- package/dist/assets/{index-d2L6kDE9.js → index-Cw_igOCh.js} +2 -2
- package/dist/assets/{index-5ATcFzQK.js → index-DH6neeAn.js} +1 -1
- package/dist/assets/{index-FjGT9Yrp.js → index-DYZfyIFS.js} +1 -1
- package/dist/assets/{index-CQm1mKh-.js → index-DqxWEMZG.js} +1 -1
- package/dist/assets/{index-B6uEt4Lm.js → index-SMWE-umF.js} +1 -1
- package/dist/assets/{index-D7TrcLgR.js → index-Zcwywgin.js} +1 -1
- package/dist/assets/{infoDiagram-42DDH7IO-D7SIo5T_.js → infoDiagram-42DDH7IO-DafQ4Zwr.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-D1w7_hBw.js → ishikawaDiagram-UXIWVN3A-CsS59UI8.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-D2ZcdIwf.js → journeyDiagram-VCZTEJTY-C8OZZC2d.js} +1 -1
- package/dist/assets/{jszip.min-BhyPZTcl.js → jszip.min-DWTs-GDW.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-BtXrBplk.js → kanban-definition-6JOO6SKY-BThryIIr.js} +1 -1
- package/dist/assets/{layout-l4xJ0EnV.js → layout-B2c4ocGM.js} +1 -1
- package/dist/assets/{linear-BFgL1PM1.js → linear-whLrOj6w.js} +1 -1
- package/dist/assets/{mermaid.core-CBJGZX-R.js → mermaid.core-CVxcxPSX.js} +2 -2
- package/dist/assets/{min-D7RE9cIU.js → min-DcYONCON.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-B2jur7ry.js → mindmap-definition-QFDTVHPH-B7cxL7F7.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-B249cLkg.js → pieDiagram-DEJITSTG-C5W3T882.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-CdZHycc8.js → quadrantDiagram-34T5L4WZ-DOZS2aRU.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-BezSNLdn.js → requirementDiagram-MS252O5E-C_ZtyqTm.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-C-RZnw4b.js → sankeyDiagram-XADWPNL6-DImjFcUx.js} +1 -1
- package/dist/assets/seqResourceLoaders-sKqNZgOI.js +2 -0
- package/dist/assets/{sequenceDiagram-FGHM5R23-Fs0rcjkY.js → sequenceDiagram-FGHM5R23-BQwGiH06.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-CIcdyOPX.js → stateDiagram-FHFEXIEX-D8zGNMAW.js} +1 -1
- package/dist/assets/{stateDiagram-v2-QKLJ7IA2-BK2tAdaI.js → stateDiagram-v2-QKLJ7IA2-DY86eLVa.js} +1 -1
- package/dist/assets/{timeline-definition-GMOUNBTQ-BXg7hZyc.js → timeline-definition-GMOUNBTQ-LfDkNw95.js} +1 -1
- package/dist/assets/{vendor-antd-BSgMQtAG.js → vendor-antd-x2KtHa4R.js} +1 -1
- package/dist/assets/{vendor-codemirror-rm3PGybQ.js → vendor-codemirror-z6VSi4ab.js} +1 -1
- package/dist/assets/{vendor-mdxeditor-UIfRXbTk.js → vendor-mdxeditor-Zi3mchEB.js} +2 -2
- package/dist/assets/{vendor-qrcode-BiBG2gg5.js → vendor-qrcode-Dtu30dr9.js} +1 -1
- package/dist/assets/{vendor-virtuoso-DNNzcVZ0.js → vendor-virtuoso-BhHaLmft.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-IyEu5-2i.js → vennDiagram-DHZGUBPP-DRRBdcoR.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-BEUuSlqC.js → wardley-RL74JXVD-DtWPL3Ou.js} +1 -1
- package/dist/assets/{wardleyDiagram-NUSXRM2D-Cv-nt0oQ.js → wardleyDiagram-NUSXRM2D-CDGkjbzC.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-B4QuJzX1.js → xychartDiagram-5P7HB3ND-drrUfgKK.js} +1 -1
- package/dist/index.html +4 -4
- package/package.json +4 -4
- package/server/interceptor.js +32 -9
- package/server/lib/interceptor-core.js +53 -5
- package/server/lib/log-management.js +57 -25
- package/server/routes/logs.js +5 -2
- package/dist/assets/classDiagram-6PBFFD2Q-BHoVlsUt.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-BHoVlsUt.js +0 -1
- package/dist/assets/clone-BdK-5KXX.js +0 -1
- package/dist/assets/seqResourceLoaders-3quDY60a.js +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { appendFileSync, existsSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { renameSyncWithRetry } from './file-api.js';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { join, basename } from 'node:path';
|
|
4
4
|
|
|
5
5
|
const SUBAGENT_SYSTEM_RE = /(?:command execution|file search|planning) specialist|general-purpose agent|security monitor|performing a web search/i;
|
|
6
6
|
|
|
@@ -251,12 +251,33 @@ export function createStreamAssembler() {
|
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
// 日志文件名前缀【单一来源】:writer(generateNewLogFilePath / initForWorkspace)与 matcher 共用,
|
|
255
|
+
// 防止两侧命名漂移。默认 `<project>_`;`--pid` 实例 `<pid>__<project>_`。完整文件名 = `${prefix}${ts}.jsonl`。
|
|
256
|
+
export function logFilePrefix(projectName, instanceId) {
|
|
257
|
+
return instanceId ? `${instanceId}__${projectName}_` : `${projectName}_`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 日志文件名归属判定(实例隔离)。`--pid` 实例文件名为 `<pid>__<project>_<ts>…`,无 pid 为 `<project>_<ts>…`。
|
|
261
|
+
// - instanceId 非空:必须精确以 `<pid>__<project>_` 开头(只认本实例)。
|
|
262
|
+
// - instanceId 空(默认):以 `<project>_` 开头【且】不含 pid 分隔特征 `__<project>_`。后一条堵住前缀碰撞——
|
|
263
|
+
// 若用户把 --pid 取成项目名或 `<项目>_…`(如项目 proj 用 --pid=proj → `proj__proj_…` 也会 startsWith `proj_`),
|
|
264
|
+
// 仅靠前缀无法排除;而任何 pid 文件按构造必含 `__<project>_`,无标签文件绝不含(即便项目名自身带 `__`),据此精确排除。
|
|
265
|
+
export function logFileMatcher(projectName, instanceId) {
|
|
266
|
+
const p = logFilePrefix(projectName, instanceId);
|
|
267
|
+
if (instanceId) return (f) => f.startsWith(p);
|
|
268
|
+
const pidMark = `__${projectName}_`;
|
|
269
|
+
return (f) => f.startsWith(p) && !f.includes(pidMark);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// instanceId 为可选第 3 参(默认 null = 现状):非空时只在本实例自己的日志里找(多进程隔离),
|
|
273
|
+
// 现有 2 参调用方与单测零改动。
|
|
274
|
+
export function findRecentLog(dir, projectName, instanceId = null) {
|
|
255
275
|
try {
|
|
276
|
+
const owns = logFileMatcher(projectName, instanceId);
|
|
256
277
|
const files = readdirSync(dir)
|
|
257
278
|
// 排除 *_temp.jsonl:临时文件是未完成的写入态(resume 流程中途产物),
|
|
258
279
|
// 不应被当作"最近完整日志"(否则 _temp 因 sort 排在正式文件之后会被误选)。
|
|
259
|
-
.filter(f => f
|
|
280
|
+
.filter(f => owns(f) && f.endsWith('.jsonl') && !f.endsWith('_temp.jsonl'))
|
|
260
281
|
.sort()
|
|
261
282
|
.reverse();
|
|
262
283
|
if (files.length === 0) return null;
|
|
@@ -265,10 +286,37 @@ export function findRecentLog(dir, projectName) {
|
|
|
265
286
|
return null;
|
|
266
287
|
}
|
|
267
288
|
|
|
268
|
-
|
|
289
|
+
// 首启接管(仅 --pid 实例):本 pid 还没有自己的日志时,把项目里最近的【无标签】日志原子 rename 成
|
|
290
|
+
// `<pid>__<project>_<ts>.jsonl`,并入该 pid 血脉(move 而非 copy:不双计入统计)。返回接管后的新路径,或 null。
|
|
291
|
+
//
|
|
292
|
+
// 这是 best-effort 便利特性,不是核心隔离(核心隔离=各 pid 读自己的日志,不依赖接管)。为不误抢一个仍存活的
|
|
293
|
+
// 无 pid 实例(评审标记的 live-writer / 卡在 resume 的窄边角),双重守卫:① 项目里若存在任何无标签 `*_temp.jsonl`
|
|
294
|
+
// (某个无 pid 实例正卡在 resume / 在写)→ 放弃;② 无标签日志 mtime 必须早于 freshnessMs(默认 5min)。仍有理论窄缝
|
|
295
|
+
// (进程挂起 >5min 且无 temp),属可接受的便利取舍。并发下原子 rename 只一个赢、输者 throw→吞→null(回退全新)。
|
|
296
|
+
export function claimUntaggedLog(dir, projectName, instanceId, { freshnessMs = 300000 } = {}) {
|
|
297
|
+
if (!instanceId) return null;
|
|
298
|
+
try {
|
|
299
|
+
const untagged = findRecentLog(dir, projectName, null); // 最近无标签日志(已排除 pid 文件 + temp)
|
|
300
|
+
if (!untagged) return null;
|
|
301
|
+
// ① 存在无标签 temp ⇒ 有无 pid 实例正活动/卡在 resume → 不接管。
|
|
302
|
+
const owns = logFileMatcher(projectName, null);
|
|
303
|
+
const hasUntaggedTemp = readdirSync(dir).some(f => owns(f) && f.endsWith('_temp.jsonl'));
|
|
304
|
+
if (hasUntaggedTemp) return null;
|
|
305
|
+
let st;
|
|
306
|
+
try { st = statSync(untagged); } catch { return null; }
|
|
307
|
+
if (Date.now() - st.mtimeMs < freshnessMs) return null; // ② 可能是活动写者 → 不碰
|
|
308
|
+
const claimed = join(dir, `${instanceId}__${basename(untagged)}`);
|
|
309
|
+
if (existsSync(claimed)) return null; // 极端撞名 → 放弃
|
|
310
|
+
renameSyncWithRetry(untagged, claimed);
|
|
311
|
+
return claimed;
|
|
312
|
+
} catch { return null; }
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function cleanupTempFiles(dir, projectName, instanceId = null) {
|
|
269
316
|
try {
|
|
317
|
+
const owns = logFileMatcher(projectName, instanceId);
|
|
270
318
|
const tempFiles = readdirSync(dir)
|
|
271
|
-
.filter(f => f
|
|
319
|
+
.filter(f => owns(f) && f.endsWith('_temp.jsonl'));
|
|
272
320
|
for (const f of tempFiles) {
|
|
273
321
|
try {
|
|
274
322
|
const tempPath = join(dir, f);
|
|
@@ -6,6 +6,7 @@ import { join, sep } from 'node:path';
|
|
|
6
6
|
import { reconstructEntries } from './delta-reconstructor.js';
|
|
7
7
|
import { streamReconstructedEntriesAsync } from './log-stream.js';
|
|
8
8
|
import { archiveJsonl, resolveJsonlPath } from './jsonl-archive.js';
|
|
9
|
+
import { logFileMatcher } from './interceptor-core.js';
|
|
9
10
|
|
|
10
11
|
export function validateLogPath(logDir, file) {
|
|
11
12
|
const filePath = join(logDir, file);
|
|
@@ -28,7 +29,20 @@ function isLogFileName(name) {
|
|
|
28
29
|
return name.endsWith('.jsonl') || name.endsWith('.jsonl.zip');
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
// 解析日志文件名里的时间戳 `YYYYMMDD_HHMMSS`(带不带 `<pid>__` 前缀、归档与否都适用)。
|
|
33
|
+
// 用于「按时间排序 / 判最新」——文件名整串排序会把 `<pid>__` 前缀('1' < 'c')的最新文件排到最底,
|
|
34
|
+
// 必须按时间戳排。无法解析时返回 ''(排到最后)。listLocalLogs 与 archiveLogFiles 共用,防漂移。
|
|
35
|
+
function parseLogTs(name) {
|
|
36
|
+
const m = name.match(/_(\d{8}_\d{6})\.jsonl(\.zip)?$/);
|
|
37
|
+
return m ? m[1] : '';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// instanceId / showAll 为可选项(默认 null / false = 现状的反面:硬隔离):
|
|
41
|
+
// - instanceId 非空:只列本实例 `<pid>__<project>_…` 的日志;
|
|
42
|
+
// - instanceId 空:只列无标签 `<project>_…` 日志(排除任何 `<pid>__` 文件);
|
|
43
|
+
// - showAll=true:越过上面的归属过滤,列出目录下全部日志(顶部「显示全部」开关用)。
|
|
44
|
+
// 复用 interceptor-core 的 logFileMatcher(与写入端共用,防命名漂移)。第 3 参可选 → 旧 2 参调用零改动。
|
|
45
|
+
export async function listLocalLogs(logDir, currentProjectName, { instanceId = null, showAll = false } = {}) {
|
|
32
46
|
const grouped = {};
|
|
33
47
|
if (!existsSync(logDir)) return { ...grouped, _currentProject: currentProjectName || '' };
|
|
34
48
|
|
|
@@ -37,30 +51,47 @@ export async function listLocalLogs(logDir, currentProjectName) {
|
|
|
37
51
|
if (!entry.isDirectory()) continue;
|
|
38
52
|
const project = entry.name;
|
|
39
53
|
const projectDir = join(logDir, project);
|
|
40
|
-
|
|
41
|
-
.filter(isLogFileName)
|
|
42
|
-
.sort()
|
|
43
|
-
.reverse();
|
|
44
|
-
let statsFiles = null;
|
|
54
|
+
// 单个项目目录读失败(权限 / 被并发删除 / EMFILE 等)只跳过该项目,不拖垮整个列表。
|
|
45
55
|
try {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
const owns = logFileMatcher(project, instanceId);
|
|
57
|
+
const files = (await readdir(projectDir)).filter(isLogFileName);
|
|
58
|
+
let statsFiles = null;
|
|
59
|
+
try {
|
|
60
|
+
const statsFile = join(projectDir, `${project}.json`);
|
|
61
|
+
if (existsSync(statsFile)) {
|
|
62
|
+
statsFiles = JSON.parse(await readFile(statsFile, 'utf-8')).files;
|
|
63
|
+
}
|
|
64
|
+
} catch { }
|
|
65
|
+
for (const f of files) {
|
|
66
|
+
if (!showAll && !owns(f)) continue;
|
|
67
|
+
const match = f.match(/^(.+?)_(\d{8}_\d{6})\.jsonl(\.zip)?$/);
|
|
68
|
+
if (!match) continue;
|
|
69
|
+
const ts = match[2];
|
|
70
|
+
const archived = !!match[3];
|
|
71
|
+
// 从前缀解析归属实例 id 用于行内 badge:`<pid>__<project>` → pid;`<project>` → 无(null)。
|
|
72
|
+
// 项目名自身可含下划线,故用 endsWith('__'+project) 精确剥离,而非按 '__' split。
|
|
73
|
+
const g1 = match[1];
|
|
74
|
+
const fileInstanceId = (g1 !== project && g1.endsWith('__' + project))
|
|
75
|
+
? g1.slice(0, -(project.length + 2))
|
|
76
|
+
: null;
|
|
77
|
+
const filePath = join(projectDir, f);
|
|
78
|
+
let size;
|
|
79
|
+
try { size = (await stat(filePath)).size; } catch { continue; }
|
|
80
|
+
if (size === 0) continue;
|
|
81
|
+
const stats = statsFiles?.[f] || (archived ? statsFiles?.[f.slice(0, -4)] : null);
|
|
82
|
+
const turns = stats?.summary?.sessionCount || 0;
|
|
83
|
+
if (!grouped[project]) grouped[project] = [];
|
|
84
|
+
grouped[project].push({ file: `${project}/${f}`, timestamp: ts, size, turns, preview: stats?.preview || [], archived, instanceId: fileInstanceId });
|
|
49
85
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (size === 0) continue;
|
|
60
|
-
const stats = statsFiles?.[f] || (archived ? statsFiles?.[f.slice(0, -4)] : null);
|
|
61
|
-
const turns = stats?.summary?.sessionCount || 0;
|
|
62
|
-
if (!grouped[project]) grouped[project] = [];
|
|
63
|
-
grouped[project].push({ file: `${project}/${f}`, timestamp: ts, size, turns, preview: stats?.preview || [], archived });
|
|
86
|
+
// 按时间戳降序(文件名降序兜底,处理同秒 tie,保证确定性)。
|
|
87
|
+
// 不能再依赖文件名整串排序:`<pid>__` 前缀会把最新日志排到最底。
|
|
88
|
+
if (grouped[project]) {
|
|
89
|
+
grouped[project].sort((a, b) =>
|
|
90
|
+
b.timestamp.localeCompare(a.timestamp) || b.file.localeCompare(a.file));
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error(`[CC Viewer] listLocalLogs: 跳过无法读取的项目 ${project}:`, err?.message || err);
|
|
94
|
+
continue;
|
|
64
95
|
}
|
|
65
96
|
}
|
|
66
97
|
return { ...grouped, _currentProject: currentProjectName || '' };
|
|
@@ -242,10 +273,11 @@ export function archiveLogFiles(logDir, files) {
|
|
|
242
273
|
const projectDir = join(logDir, project);
|
|
243
274
|
let latest = null;
|
|
244
275
|
try {
|
|
276
|
+
// 「最新文件不允许归档」——按时间戳判最新(与 listLocalLogs 同口径),
|
|
277
|
+
// 否则文件名整串排序会把 `<pid>__` 前缀的最新活跃日志误判成非最新而放行归档。
|
|
245
278
|
const projectEntries = readdirSync(projectDir)
|
|
246
279
|
.filter(isLogFileName)
|
|
247
|
-
.sort()
|
|
248
|
-
.reverse();
|
|
280
|
+
.sort((a, b) => parseLogTs(b).localeCompare(parseLogTs(a)) || b.localeCompare(a));
|
|
249
281
|
latest = projectEntries[0] || null;
|
|
250
282
|
} catch {}
|
|
251
283
|
|
package/server/routes/logs.js
CHANGED
|
@@ -6,9 +6,12 @@ import { _projectName } from '../interceptor.js';
|
|
|
6
6
|
import { listLocalLogs, deleteLogFiles, mergeLogFiles, archiveLogFiles, validateLogPath } from '../lib/log-management.js';
|
|
7
7
|
import { countLogEntries, streamRawEntriesAsync, readTailEntries } from '../lib/log-stream.js';
|
|
8
8
|
|
|
9
|
-
async function localLogs(req, res) {
|
|
9
|
+
async function localLogs(req, res, parsedUrl, isLocal, deps) {
|
|
10
10
|
try {
|
|
11
|
-
|
|
11
|
+
// 按当前实例 pid 硬隔离日志列表;`?all=1` 越过过滤看全部实例(顶部「显示全部」开关)。
|
|
12
|
+
const instanceId = (deps && deps.instanceId) || null;
|
|
13
|
+
const showAll = parsedUrl?.searchParams?.get('all') === '1';
|
|
14
|
+
const result = await listLocalLogs(LOG_DIR, _projectName, { instanceId, showAll });
|
|
12
15
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
13
16
|
res.end(JSON.stringify(result));
|
|
14
17
|
} catch (err) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as styles_default,c as classRenderer_v3_unified_default,a as classDiagram_default,C as ClassDB}from"./chunk-4TB4RGXK-O_q60rT5.js";import{_ as __name}from"./mermaid.core-CBJGZX-R.js";import"./chunk-FMBD7UC4-BMajr_WJ.js";import"./chunk-YZCP3GAM-xsMRpY5W.js";import"./chunk-55IACEB6-D4PJNgqD.js";import"./chunk-EDXVE4YY-T78XNZnN.js";import"./vendor-markdown-DOJHsAxX.js";import"./vendor-mdxeditor-UIfRXbTk.js";import"./vendor-antd-BSgMQtAG.js";import"./vendor-codemirror-rm3PGybQ.js";var diagram={parser:classDiagram_default,get db(){return new ClassDB},renderer:classRenderer_v3_unified_default,styles:styles_default,init:__name(cnf=>{cnf.class||(cnf.class={}),cnf.class.arrowMarkerAbsolute=cnf.arrowMarkerAbsolute},"init")};export{diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as styles_default,c as classRenderer_v3_unified_default,a as classDiagram_default,C as ClassDB}from"./chunk-4TB4RGXK-O_q60rT5.js";import{_ as __name}from"./mermaid.core-CBJGZX-R.js";import"./chunk-FMBD7UC4-BMajr_WJ.js";import"./chunk-YZCP3GAM-xsMRpY5W.js";import"./chunk-55IACEB6-D4PJNgqD.js";import"./chunk-EDXVE4YY-T78XNZnN.js";import"./vendor-markdown-DOJHsAxX.js";import"./vendor-mdxeditor-UIfRXbTk.js";import"./vendor-antd-BSgMQtAG.js";import"./vendor-codemirror-rm3PGybQ.js";var diagram={parser:classDiagram_default,get db(){return new ClassDB},renderer:classRenderer_v3_unified_default,styles:styles_default,init:__name(cnf=>{cnf.class||(cnf.class={}),cnf.class.arrowMarkerAbsolute=cnf.arrowMarkerAbsolute},"init")};export{diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as baseClone}from"./graph-Bp7rzoPN.js";function clone(value){return baseClone(value,4)}export{clone as c};
|