cc-viewer 1.6.348 → 1.7.0
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/README.md +13 -1
- package/cli.js +116 -37
- package/dist/assets/App-DTkacfRW.js +2 -0
- package/dist/assets/{MdxEditorPanel-ualoRFOL.js → MdxEditorPanel-C5Q6uUyH.js} +1 -1
- package/dist/assets/Mobile-MoBp9c8k.js +1 -0
- package/dist/assets/{_baseUniq-CG81mgN_.js → _baseUniq-D9_E5ZDp.js} +1 -1
- package/dist/assets/{arc-C161J4f2.js → arc-D4iFrlhb.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-BmbDMdn9.js → architectureDiagram-Q4EWVU46-f1neo7hg.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-DXpMvdxd.js → blockDiagram-DXYQGD6D-DWi9e9tG.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-8Mypg0IU.js → c4Diagram-AHTNJAMY-DAdj49A2.js} +1 -1
- package/dist/assets/{channel-B2khdmvc.js → channel-B3VthT0m.js} +1 -1
- package/dist/assets/{chunk-4BX2VUAB-I61r1g6z.js → chunk-4BX2VUAB-BrPcAHV3.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-VFPlRE5E.js → chunk-4TB4RGXK-Bvopc1V9.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DvCamg_6.js → chunk-55IACEB6-Juw-7Z3i.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-Dj4Q8RwR.js → chunk-EDXVE4YY-DVYP9tpB.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-_bgQuuJh.js → chunk-FMBD7UC4-VQ7VOXl3.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-3to38LyN.js → chunk-OYMX7WX6-F8Rj4ZUZ.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-Zl26wKKz.js → chunk-QZHKN3VN-Dn7K3yi9.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-RLYPrlh0.js → chunk-YZCP3GAM-UjO-KTsn.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-DmSozxoO.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-DmSozxoO.js +1 -0
- package/dist/assets/clone-BQtvy9Ae.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-CsYOjJ0V.js → cose-bilkent-S5V4N54A-BDhNj8rq.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-C3YXXaFU.js → dagre-KV5264BT-BzdcDuyt.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-DUCOg1ld.js → diagram-5BDNPKRD-DP_SX65N.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-BtlLl_zX.js → diagram-G4DWMVQ6-B-pBFw8Q.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-1Qp9xfOk.js → diagram-MMDJMWI5-6XSfN1eN.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-Cj7VMp3L.js → diagram-TYMM5635-DJvGxvpE.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-CVZVn541.js → erDiagram-SMLLAGMA-CpqdpGYj.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CiU9Fkkv.js → flowDiagram-DWJPFMVM-C9m7wjwI.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-aRaG97ID.js → ganttDiagram-T4ZO3ILL-CU0XOfY3.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-Cr2LJlvX.js → gitGraphDiagram-UUTBAWPF-ipyGULnP.js} +1 -1
- package/dist/assets/{graph-B59lDyMn.js → graph-BR2KmES9.js} +1 -1
- package/dist/assets/{index-C1ptnCpq.js → index--wgRW_OB.js} +1 -1
- package/dist/assets/index-Ac8vnV3v.js +2 -0
- package/dist/assets/{index-CRAWA8PC.js → index-C-be29ey.js} +1 -1
- package/dist/assets/{index-CSM1Kw8l.js → index-C16fiVhv.js} +1 -1
- package/dist/assets/{index-Ddk-kg22.js → index-D5YruOKD.js} +1 -1
- package/dist/assets/{index-BnhNIjbj.js → index-D5tSeMU7.js} +1 -1
- package/dist/assets/{index-vFEQq-NB.js → index-DuZQ6FqR.js} +1 -1
- package/dist/assets/{index-CO2hZ1s4.js → index-OWzSX2T8.js} +1 -1
- package/dist/assets/{infoDiagram-42DDH7IO-CnO22bw8.js → infoDiagram-42DDH7IO-BQWQjfFX.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-ByNzpScH.js → ishikawaDiagram-UXIWVN3A-Df-KH_Hz.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-yaVaCWPq.js → journeyDiagram-VCZTEJTY-CMbB_4G9.js} +1 -1
- package/dist/assets/{jszip.min-BdVFP3OW.js → jszip.min-B6k2t9Ch.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-RIPR_RQ3.js → kanban-definition-6JOO6SKY-CImdlKN3.js} +1 -1
- package/dist/assets/{layout-0rzel0v_.js → layout-DQl4aw09.js} +1 -1
- package/dist/assets/{linear-sktN0tB6.js → linear-CLD3aPlK.js} +1 -1
- package/dist/assets/mermaid.core-CN35dvKJ.js +7 -0
- package/dist/assets/{min-rspGOkz_.js → min-_D86henJ.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-BsVzZzTz.js → mindmap-definition-QFDTVHPH-2JVcu1nk.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-CsoWsw4f.js → pieDiagram-DEJITSTG-Dik-m5u7.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-CTMKuCAI.js → quadrantDiagram-34T5L4WZ-Bm5l40Jq.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-C67256te.js → requirementDiagram-MS252O5E-Ck9w5rTj.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-X8Y7KnW0.js → sankeyDiagram-XADWPNL6-BbGVCFEf.js} +1 -1
- package/dist/assets/seqResourceLoaders-Cd_TAXTb.js +2 -0
- package/dist/assets/{seqResourceLoaders-B5Zy_CC_.css → seqResourceLoaders-CxuD1CYl.css} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-Cils0TAp.js → sequenceDiagram-FGHM5R23-vjvEdksk.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-DFnohvpG.js → stateDiagram-FHFEXIEX-DTqMoiB2.js} +1 -1
- package/dist/assets/{stateDiagram-v2-QKLJ7IA2-BTX_4o81.js → stateDiagram-v2-QKLJ7IA2-CNbAwnhu.js} +1 -1
- package/dist/assets/{timeline-definition-GMOUNBTQ-DrIYFrzZ.js → timeline-definition-GMOUNBTQ-Cw8O-52d.js} +1 -1
- package/dist/assets/{vendor-antd-D5HmSDSg.js → vendor-antd-DI7JL-mE.js} +1 -1
- package/dist/assets/{vendor-codemirror-C5yuj7ze.js → vendor-codemirror-B9c49dtM.js} +1 -1
- package/dist/assets/{vendor-mdxeditor-BcEooTvb.js → vendor-mdxeditor-B6cpBtIE.js} +2 -2
- package/dist/assets/{vendor-qrcode-d9LCn0-9.js → vendor-qrcode-Dn90-s-u.js} +1 -1
- package/dist/assets/{vendor-virtuoso-K9m4MN6a.js → vendor-virtuoso-CpdSKD_6.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-lRqBYTr9.js → vennDiagram-DHZGUBPP-DCuqymb7.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-e8YfxpOe.js → wardley-RL74JXVD-BJ20wJUU.js} +1 -1
- package/dist/assets/{wardleyDiagram-NUSXRM2D-Bxrg774h.js → wardleyDiagram-NUSXRM2D-Vz6US3qt.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-DWhZqhq8.js → xychartDiagram-5P7HB3ND-CzkLKrmi.js} +1 -1
- package/dist/index.html +4 -4
- package/findcc.js +4 -3
- package/package.json +2 -1
- package/server/i18n.js +21 -141
- package/server/interceptor.js +211 -403
- package/server/lib/async-write-queue.js +12 -1
- package/server/lib/delta-reconstructor.js +3 -3
- package/server/lib/error-report.js +45 -0
- package/server/lib/im-log-watcher.js +68 -8
- package/server/lib/interceptor-core.js +19 -174
- package/server/lib/log-management.js +214 -247
- package/server/lib/log-stream.js +59 -108
- package/server/lib/log-watcher.js +34 -272
- package/server/lib/log-zip.js +148 -0
- package/server/lib/session-boundary.js +266 -0
- package/server/lib/session-pin-store.js +10 -19
- package/server/lib/stats-worker.js +138 -267
- package/server/lib/user-prompt-extract.js +250 -0
- package/server/lib/v2/adapter.js +1059 -0
- package/server/lib/v2/blob-store.js +44 -0
- package/server/lib/v2/conversation-store.js +217 -0
- package/server/lib/v2/convert-manager.js +79 -0
- package/server/lib/v2/convert-worker.js +42 -0
- package/server/lib/v2/convert.js +409 -0
- package/server/lib/v2/identity.js +204 -0
- package/server/lib/v2/journal.js +62 -0
- package/server/lib/v2/layout.js +167 -0
- package/server/lib/v2/live-feed.js +451 -0
- package/server/lib/v2/migrate-prompt.js +54 -0
- package/server/lib/v2/replay.js +191 -0
- package/server/lib/v2/session-select.js +222 -0
- package/server/lib/v2/singleflight.js +104 -0
- package/server/lib/v2/v2-writer.js +513 -0
- package/server/lib/v2/verify.js +233 -0
- package/server/routes/events.js +30 -130
- package/server/routes/files-fs.js +4 -2
- package/server/routes/im.js +11 -2
- package/server/routes/logs.js +194 -152
- package/server/routes/preferences.js +0 -3
- package/server/routes/project-meta.js +2 -3
- package/server/routes/session-pin.js +7 -8
- package/server/routes/workspaces.js +44 -9
- package/server/server.js +47 -65
- package/server/workspace-registry.js +24 -4
- package/dist/assets/App-VTo4NVdo.js +0 -2
- package/dist/assets/Mobile-CcmYvMm_.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BXnyUgsj.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-BXnyUgsj.js +0 -1
- package/dist/assets/clone-md-Mc1-T.js +0 -1
- package/dist/assets/index-DCo650PG.js +0 -2
- package/dist/assets/mermaid.core-C2Parj1q.js +0 -7
- package/dist/assets/seqResourceLoaders-BVozq68i.js +0 -2
- package/server/lib/instance-registry.js +0 -57
- package/server/lib/jsonl-archive.js +0 -180
- package/server/lib/teammate-detect.js +0 -32
- package/server/lib/zip-safety.js +0 -68
package/server/server.js
CHANGED
|
@@ -74,18 +74,19 @@ function execWithStdin(cmd, args, input, options) {
|
|
|
74
74
|
child.stdin.end();
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
-
import {
|
|
78
|
-
import {
|
|
77
|
+
import { _initPromise, _projectName, _logDir, _v2Writer, streamingState, resetStreamingState, PROFILE_PATH, setLivePort, getImLiveText, resetImLiveText } from './interceptor.js';
|
|
78
|
+
import { V2LiveFeed } from './lib/v2/live-feed.js';
|
|
79
|
+
import { sanitizePathComponent } from './lib/v2/layout.js';
|
|
80
|
+
import { maybeResumeConvert } from './lib/v2/convert-manager.js';
|
|
79
81
|
import { LOG_DIR, setLogDir, getClaudeConfigDir, isBrowserOpenSuppressed } from '../findcc.js';
|
|
80
82
|
import { t, getLang, setLang } from './i18n.js';
|
|
81
83
|
import { loadAuthConfig, loadAuthState, saveAuthConfig, clearProjectOverride, generatePassword, decideAuth, parseCookies, renderLoginPage, localeFromAcceptLanguage } from './lib/auth.js';
|
|
82
84
|
import { checkAndUpdate } from './lib/updater.js';
|
|
83
85
|
import { loadPlugins, runWaterfallHook, runParallelHook } from './lib/plugin-loader.js';
|
|
84
86
|
import { CONTEXT_WINDOW_FILE, readModelContextSize } from './lib/context-watcher.js';
|
|
85
|
-
import {
|
|
87
|
+
import { sendEventToClients, sendToClients } from './lib/log-watcher.js';
|
|
86
88
|
import { createImLogWatcher } from './lib/im-log-watcher.js';
|
|
87
89
|
import { unwatchAllWorkflows } from './lib/workflow-watcher.js';
|
|
88
|
-
import { cleanupExtractCache } from './lib/jsonl-archive.js';
|
|
89
90
|
import { backupConfigs } from './lib/config-backup.js';
|
|
90
91
|
import { normalizeBasePath, validateBasePath, stripBasePath } from './lib/base-path.js';
|
|
91
92
|
import { createHardenedCleanup } from './lib/term-signals.js';
|
|
@@ -111,12 +112,6 @@ try {
|
|
|
111
112
|
const isCliMode = process.env.CCV_CLI_MODE === '1';
|
|
112
113
|
const isSdkMode = process.env.CCV_SDK_MODE === '1';
|
|
113
114
|
const isWorkspaceMode = process.env.CCV_WORKSPACE_MODE === '1';
|
|
114
|
-
// Instance id from `ccv --pid <name>` (sanitized in cli.js). null = default mode (no
|
|
115
|
-
// per-instance isolation): the session-pin file falls back to the project-shared key.
|
|
116
|
-
const INSTANCE_ID = process.env.CCV_INSTANCE_ID || null;
|
|
117
|
-
// Remember this id under the project so the CLI banner can list past ids next run (memory
|
|
118
|
-
// aid only). Fire-and-forget; no-ops when there's no active project (e.g. workspace mode).
|
|
119
|
-
if (INSTANCE_ID && _logDir) recordInstance(_logDir, INSTANCE_ID).catch(() => {});
|
|
120
115
|
const _defaultProxyProfiles = { active: 'max', profiles: [{ id: 'max', name: 'Default' }] };
|
|
121
116
|
const _maskApiKey = (k) => k && typeof k === 'string' && k.length > 4 ? '****' + k.slice(-4) : k ? '****' : '';
|
|
122
117
|
const _maskProfiles = (data) => {
|
|
@@ -305,9 +300,12 @@ let _launchCallback = null;
|
|
|
305
300
|
export function setLaunchCallback(fn) { _launchCallback = fn; }
|
|
306
301
|
export function setWorkspaceLaunched(v) { _workspaceLaunched = v; }
|
|
307
302
|
export function initPostLaunch() {
|
|
308
|
-
|
|
303
|
+
startLogWatch();
|
|
309
304
|
if (!statsWorker) startStatsWorker();
|
|
310
305
|
startStreamingStatusTimer();
|
|
306
|
+
// wire-v2 S8: a migration the previous process left unfinished resumes here
|
|
307
|
+
// (resident-until-done, user decision; file-level checkpoints make it cheap).
|
|
308
|
+
try { if (_projectName) maybeResumeConvert(LOG_DIR, _projectName); } catch (err) { console.error('[CC Viewer] wire-v2 convert resume failed:', err.message); }
|
|
311
309
|
}
|
|
312
310
|
|
|
313
311
|
// Global POST body size limit (10MB) to prevent OOM from malicious/buggy clients
|
|
@@ -418,16 +416,34 @@ const MIME_TYPES = {
|
|
|
418
416
|
'.woff2': 'font/woff2',
|
|
419
417
|
};
|
|
420
418
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
419
|
+
// S6b: v2 live feed — the live channel when the v2 writer is active. Lazy
|
|
420
|
+
// singleton so worker/test paths that never watch anything pay nothing.
|
|
421
|
+
let _v2LiveFeed = null;
|
|
422
|
+
function _ensureV2LiveFeed() {
|
|
423
|
+
if (!_v2LiveFeed) {
|
|
424
|
+
_v2LiveFeed = new V2LiveFeed({
|
|
425
|
+
clients,
|
|
426
|
+
getClaudePid,
|
|
427
|
+
runParallelHook,
|
|
428
|
+
// The stats worker's v2 units are session dirs (stats-worker.js v9).
|
|
429
|
+
notifyStatsWorker,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return _v2LiveFeed;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** Start the live channel for the current project (v2 live feed). No-op until
|
|
436
|
+
* a project is bound (workspace mode defers to the launch route). */
|
|
437
|
+
function startLogWatch() {
|
|
438
|
+
if (!_projectName) return;
|
|
439
|
+
const feed = _ensureV2LiveFeed();
|
|
440
|
+
feed.start(join(LOG_DIR, sanitizePathComponent(_projectName)));
|
|
441
|
+
_v2Writer.setOnActivity((dir) => feed.tick(dir));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function stopLogWatch() {
|
|
445
|
+
if (_v2LiveFeed) _v2LiveFeed.stop();
|
|
446
|
+
_v2Writer.setOnActivity(null);
|
|
431
447
|
}
|
|
432
448
|
|
|
433
449
|
function getLocalIp() {
|
|
@@ -516,7 +532,8 @@ const deps = {
|
|
|
516
532
|
persistAskEntry: _persistAskEntry,
|
|
517
533
|
persistAskDelete: _persistAskDelete,
|
|
518
534
|
notifyParentPending: _notifyParentPending,
|
|
519
|
-
|
|
535
|
+
startLogWatch,
|
|
536
|
+
stopLogWatch,
|
|
520
537
|
scheduleTurnEndBroadcast: _scheduleTurnEndBroadcast,
|
|
521
538
|
ensureImWatch: _ensureImWatch,
|
|
522
539
|
maskProfiles: _maskProfiles,
|
|
@@ -578,7 +595,6 @@ const deps = {
|
|
|
578
595
|
isCliMode,
|
|
579
596
|
isSdkMode,
|
|
580
597
|
isWorkspaceMode,
|
|
581
|
-
instanceId: INSTANCE_ID,
|
|
582
598
|
defaultProxyProfiles: _defaultProxyProfiles,
|
|
583
599
|
};
|
|
584
600
|
|
|
@@ -855,9 +871,6 @@ export async function startViewer() {
|
|
|
855
871
|
// 加载插件(需要在创建服务器之前,以便通过 hook 获取 HTTPS 证书)
|
|
856
872
|
await loadPlugins();
|
|
857
873
|
|
|
858
|
-
// 清理过期解压缓存(fire-and-forget;任何错误吞掉)
|
|
859
|
-
setImmediate(() => { try { cleanupExtractCache(); } catch { /* ignore */ } });
|
|
860
|
-
|
|
861
874
|
// 启动期配置备份:preferences/profile/workspaces → LOG_DIR 外的 cc-viewer-config-backups/
|
|
862
875
|
// (滚动留 10 份)。2026-06-06 事故:配置随 LOG_DIR 整树丢失后无处可恢复。fire-and-forget。
|
|
863
876
|
setImmediate(() => { try { backupConfigs(); } catch { /* ignore */ } });
|
|
@@ -1025,9 +1038,11 @@ export async function startViewer() {
|
|
|
1025
1038
|
// 工作区模式下延迟到选择工作区后再启动监听
|
|
1026
1039
|
if (!isWorkspaceMode) {
|
|
1027
1040
|
readModelContextSize(); // Cache model→size mapping at startup
|
|
1028
|
-
|
|
1041
|
+
startLogWatch();
|
|
1029
1042
|
startStatsWorker();
|
|
1030
1043
|
startStreamingStatusTimer();
|
|
1044
|
+
// wire-v2 S8: resume an unfinished migration (see initPostLaunch)
|
|
1045
|
+
try { if (_projectName) maybeResumeConvert(LOG_DIR, _projectName); } catch (err) { console.error('[CC Viewer] wire-v2 convert resume failed:', err.message); }
|
|
1031
1046
|
}
|
|
1032
1047
|
// CLI 模式下启动 WebSocket 服务 (必须 await,否则插件 hook 拿不到 upgrade listeners)
|
|
1033
1048
|
if (isCliMode) {
|
|
@@ -2004,15 +2019,6 @@ export function getAuthConfig() {
|
|
|
2004
2019
|
return authConfig;
|
|
2005
2020
|
}
|
|
2006
2021
|
|
|
2007
|
-
// Instance id (`--pid`) + the project's previously-used ids — printed by cli.js in the
|
|
2008
|
-
// CLI-mode startup banner so the user can recall which id to reuse with `-c` next time.
|
|
2009
|
-
export function getInstanceId() {
|
|
2010
|
-
return INSTANCE_ID;
|
|
2011
|
-
}
|
|
2012
|
-
export function getKnownInstances() {
|
|
2013
|
-
try { return listInstances(_logDir); } catch { return []; }
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
2022
|
// In-process broadcast helper for the `turn_end` SSE event. Two callers:
|
|
2017
2023
|
// 1. /api/turn-end-notify POST handler (PTY/CLI mode via Stop hook bridge)
|
|
2018
2024
|
// 2. cli.js runSdkMode → sdkManager.initSdkSession({ onTurnEnd }) (SDK mode —
|
|
@@ -2238,28 +2244,12 @@ async function _doStop() {
|
|
|
2238
2244
|
new Promise(r => setTimeout(r, 3000)),
|
|
2239
2245
|
]);
|
|
2240
2246
|
} catch { }
|
|
2241
|
-
|
|
2242
|
-
if (_resumeState && _resumeState.tempFile) {
|
|
2243
|
-
try {
|
|
2244
|
-
const { tempFile } = _resumeState;
|
|
2245
|
-
if (existsSync(tempFile)) {
|
|
2246
|
-
// 只有非空 temp 文件才 rename 为正式文件,空文件直接删除
|
|
2247
|
-
const sz = statSync(tempFile).size;
|
|
2248
|
-
if (sz > 0) {
|
|
2249
|
-
const newPath = tempFile.replace('_temp.jsonl', '.jsonl');
|
|
2250
|
-
renameSync(tempFile, newPath);
|
|
2251
|
-
} else {
|
|
2252
|
-
unlinkSync(tempFile);
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
} catch { }
|
|
2256
|
-
}
|
|
2257
|
-
unwatchAll();
|
|
2247
|
+
stopLogWatch();
|
|
2258
2248
|
unwatchAllWorkflows();
|
|
2259
2249
|
unwatchFile(CONTEXT_WINDOW_FILE);
|
|
2260
2250
|
clients.forEach(client => client.end());
|
|
2261
2251
|
// Truncate in place (not `clients = []`) so the array reference stays stable across
|
|
2262
|
-
// stop/start cycles — deps.clients
|
|
2252
|
+
// stop/start cycles — deps.clients holds this same reference.
|
|
2263
2253
|
clients.length = 0;
|
|
2264
2254
|
if (server) {
|
|
2265
2255
|
// 销毁所有活跃连接,防止 keep-alive 阻止进程退出
|
|
@@ -2399,17 +2389,9 @@ if (!isWorkspaceMode) {
|
|
|
2399
2389
|
});
|
|
2400
2390
|
}
|
|
2401
2391
|
|
|
2402
|
-
//
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
try {
|
|
2406
|
-
if (existsSync(_resumeState.tempFile)) {
|
|
2407
|
-
const newPath = _resumeState.tempFile.replace('_temp.jsonl', '.jsonl');
|
|
2408
|
-
renameSync(_resumeState.tempFile, newPath);
|
|
2409
|
-
}
|
|
2410
|
-
} catch { }
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2392
|
+
// 进程退出钩子(v1 时代负责临时文件转正;v2 无暂存文件,保留空挂点防
|
|
2393
|
+
// 未来清理动作无处可挂)
|
|
2394
|
+
function handleExit() {}
|
|
2413
2395
|
// 防御性单次注册:ESM cache 已保证模块顶层只执行一次,但若未来重构把这段
|
|
2414
2396
|
// 移入函数(cleanupViewer 等)或用 dev hot-reload,缺守卫会导致 handler 堆积
|
|
2415
2397
|
// → 进程退出时多次调用 stopViewer。globalThis flag 兼容所有 import 路径。
|
|
@@ -3,6 +3,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from '
|
|
|
3
3
|
import { readdir, stat } from 'node:fs/promises';
|
|
4
4
|
import { renameSyncWithRetry } from './lib/file-api.js';
|
|
5
5
|
import { withFileLockAsync } from './lib/async-file-lock.js';
|
|
6
|
+
import { dirSizeSync } from './lib/v2/layout.js';
|
|
6
7
|
import { join, basename, resolve } from 'node:path';
|
|
7
8
|
import { randomBytes } from 'node:crypto';
|
|
8
9
|
import { LOG_DIR } from '../findcc.js';
|
|
@@ -92,19 +93,38 @@ export async function removeWorkspace(id) {
|
|
|
92
93
|
export async function getWorkspaces() {
|
|
93
94
|
const list = loadWorkspaces();
|
|
94
95
|
const enriched = await Promise.all(list.map(async (w) => {
|
|
95
|
-
|
|
96
|
+
// wire-v2 (1.7.0): logs live in per-session dirs under sessions/. The v1
|
|
97
|
+
// *.jsonl glob is kept as unmigratedV1Count — the combined logCount must
|
|
98
|
+
// stay non-zero for a declined-migration workspace, because the launcher's
|
|
99
|
+
// `logCount > 0 → auto -c` heuristic hangs off it (WorkspaceList.jsx).
|
|
100
|
+
let sessionCount = 0;
|
|
101
|
+
let unmigratedV1Count = 0;
|
|
96
102
|
let totalSize = 0;
|
|
97
103
|
const logDir = join(LOG_DIR, w.projectName);
|
|
98
104
|
try {
|
|
99
105
|
const files = await readdir(logDir);
|
|
100
106
|
for (const f of files) {
|
|
101
|
-
if (f.endsWith('.jsonl')) {
|
|
102
|
-
|
|
107
|
+
if (f.endsWith('.jsonl') && !f.endsWith('_temp.jsonl')) {
|
|
108
|
+
unmigratedV1Count++;
|
|
103
109
|
try { totalSize += (await stat(join(logDir, f))).size; } catch { }
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
} catch { }
|
|
107
|
-
|
|
113
|
+
try {
|
|
114
|
+
const sids = await readdir(join(logDir, 'sessions'), { withFileTypes: true });
|
|
115
|
+
for (const e of sids) {
|
|
116
|
+
if (!e.isDirectory()) continue;
|
|
117
|
+
const sessionDir = join(logDir, 'sessions', e.name);
|
|
118
|
+
try {
|
|
119
|
+
await stat(join(sessionDir, 'journal.jsonl'));
|
|
120
|
+
// Folder size, not journal size — conv/blob/response files carry
|
|
121
|
+
// most of a session's bytes (journal alone undercounts ~12x).
|
|
122
|
+
totalSize += dirSizeSync(sessionDir);
|
|
123
|
+
sessionCount++;
|
|
124
|
+
} catch { /* dir without a journal is not a session yet */ }
|
|
125
|
+
}
|
|
126
|
+
} catch { }
|
|
127
|
+
return { ...w, logCount: sessionCount + unmigratedV1Count, sessionCount, unmigratedV1Count, totalSize };
|
|
108
128
|
}));
|
|
109
129
|
return enriched.sort((a, b) => new Date(b.lastUsed) - new Date(a.lastUsed));
|
|
110
130
|
}
|