cc-viewer 1.6.160 → 1.6.162

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.
Files changed (64) hide show
  1. package/dist/assets/{App-BWbD88f1.js → App-DEQuNHI0.js} +18 -18
  2. package/dist/assets/{AppHeader-BWpOJ1UH.css → AppHeader-DslPf_SB.css} +2 -2
  3. package/dist/assets/{AppHeader.module-BuIkaeYL.js → AppHeader.module-CRlxeJt8.js} +172 -172
  4. package/dist/assets/{Mobile-oXXWHyUN.js → Mobile-C9vVRRdB.js} +2 -2
  5. package/dist/assets/{_baseUniq-Bv1g9AKk.js → _baseUniq-vfNPF29h.js} +1 -1
  6. package/dist/assets/{arc-CahHLzqv.js → arc-AEy4SEJc.js} +1 -1
  7. package/dist/assets/{architectureDiagram-Q4EWVU46-CXM1aMfu.js → architectureDiagram-Q4EWVU46-_lidm5qH.js} +1 -1
  8. package/dist/assets/{blockDiagram-DXYQGD6D-BugprbsF.js → blockDiagram-DXYQGD6D-DJLx-ke5.js} +1 -1
  9. package/dist/assets/{c4Diagram-AHTNJAMY-ChUPS-o_.js → c4Diagram-AHTNJAMY-B71Spppv.js} +1 -1
  10. package/dist/assets/channel-DRFCr8AD.js +1 -0
  11. package/dist/assets/{chunk-4BX2VUAB-D-ZdY__s.js → chunk-4BX2VUAB-CMcq3Vv2.js} +1 -1
  12. package/dist/assets/{chunk-4TB4RGXK-CJwUXaEg.js → chunk-4TB4RGXK-xo3KNvva.js} +1 -1
  13. package/dist/assets/{chunk-55IACEB6-TyTrQiAX.js → chunk-55IACEB6-Ds1Pt5Cm.js} +1 -1
  14. package/dist/assets/{chunk-EDXVE4YY-rthcFfJc.js → chunk-EDXVE4YY-B_5Nfk4A.js} +1 -1
  15. package/dist/assets/{chunk-FMBD7UC4-CsDh9m8Q.js → chunk-FMBD7UC4-CUbD37I1.js} +1 -1
  16. package/dist/assets/{chunk-OYMX7WX6-Fo7iMm6t.js → chunk-OYMX7WX6-Bg__QtDz.js} +1 -1
  17. package/dist/assets/{chunk-QZHKN3VN-CP02hnHr.js → chunk-QZHKN3VN-CNfqcIST.js} +1 -1
  18. package/dist/assets/{chunk-YZCP3GAM-_OpiJebR.js → chunk-YZCP3GAM-Cmp2ccd_.js} +1 -1
  19. package/dist/assets/classDiagram-6PBFFD2Q-DogDJNWC.js +1 -0
  20. package/dist/assets/classDiagram-v2-HSJHXN6E-DogDJNWC.js +1 -0
  21. package/dist/assets/clone-pWa_kygB.js +1 -0
  22. package/dist/assets/{cose-bilkent-S5V4N54A-L9dLDHaV.js → cose-bilkent-S5V4N54A-CnEp9Tzl.js} +1 -1
  23. package/dist/assets/{dagre-KV5264BT-CCuxDesa.js → dagre-KV5264BT-BiW4MW84.js} +1 -1
  24. package/dist/assets/{diagram-5BDNPKRD-CUdHU8If.js → diagram-5BDNPKRD-4XN6WbFE.js} +1 -1
  25. package/dist/assets/{diagram-G4DWMVQ6-BOyY9p0C.js → diagram-G4DWMVQ6-BOFKJUJU.js} +1 -1
  26. package/dist/assets/{diagram-MMDJMWI5-Bd9hrp_H.js → diagram-MMDJMWI5-D8iRoYOZ.js} +1 -1
  27. package/dist/assets/{diagram-TYMM5635-rQ9DoCZG.js → diagram-TYMM5635-DpAXl8xj.js} +1 -1
  28. package/dist/assets/{erDiagram-SMLLAGMA-luUkz9px.js → erDiagram-SMLLAGMA-BEV6ItnT.js} +1 -1
  29. package/dist/assets/{flowDiagram-DWJPFMVM-Bov8LfK_.js → flowDiagram-DWJPFMVM-BxuT2HMm.js} +1 -1
  30. package/dist/assets/{ganttDiagram-T4ZO3ILL-C0Ymc_ta.js → ganttDiagram-T4ZO3ILL-BABD11n2.js} +1 -1
  31. package/dist/assets/{gitGraphDiagram-UUTBAWPF-1bxXpZTC.js → gitGraphDiagram-UUTBAWPF-DP5a5eUL.js} +1 -1
  32. package/dist/assets/{graph-dQACcLqY.js → graph-ChrYtVLf.js} +1 -1
  33. package/dist/assets/{index-CmwiYM1W.js → index-gXn8GRro.js} +2 -2
  34. package/dist/assets/{infoDiagram-42DDH7IO-91O-A95a.js → infoDiagram-42DDH7IO-iZxRWGAK.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-UXIWVN3A-Cbfe1WsH.js → ishikawaDiagram-UXIWVN3A-B_WKzou-.js} +1 -1
  36. package/dist/assets/{journeyDiagram-VCZTEJTY-D6dOpe-H.js → journeyDiagram-VCZTEJTY-Cu7zGVpB.js} +1 -1
  37. package/dist/assets/{kanban-definition-6JOO6SKY-m7w3p53N.js → kanban-definition-6JOO6SKY-n55IUz1o.js} +1 -1
  38. package/dist/assets/{layout-CeORj580.js → layout-D42K2GkR.js} +1 -1
  39. package/dist/assets/{linear-CWhG43pd.js → linear-zXcFtURh.js} +1 -1
  40. package/dist/assets/{mermaid.core-rKXVP1Sx.js → mermaid.core-BG6UfeN0.js} +4 -4
  41. package/dist/assets/{min-CgKceRth.js → min-CzF_ovn8.js} +1 -1
  42. package/dist/assets/{mindmap-definition-QFDTVHPH-Ba_34kHT.js → mindmap-definition-QFDTVHPH-DfUCo3bN.js} +1 -1
  43. package/dist/assets/{pieDiagram-DEJITSTG-BTamBzcB.js → pieDiagram-DEJITSTG-CWZg93zu.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-34T5L4WZ-Ckb7QXjG.js → quadrantDiagram-34T5L4WZ-D49GHlp0.js} +1 -1
  45. package/dist/assets/{requirementDiagram-MS252O5E-DPEEXcQx.js → requirementDiagram-MS252O5E-013_nBsh.js} +1 -1
  46. package/dist/assets/{sankeyDiagram-XADWPNL6-BLXi1v8D.js → sankeyDiagram-XADWPNL6-CDf-UTzA.js} +1 -1
  47. package/dist/assets/{sequenceDiagram-FGHM5R23-CBzpty6Y.js → sequenceDiagram-FGHM5R23-D6e5b8mu.js} +1 -1
  48. package/dist/assets/{stateDiagram-FHFEXIEX-CPJ4ofxr.js → stateDiagram-FHFEXIEX-DUVDgqff.js} +1 -1
  49. package/dist/assets/stateDiagram-v2-QKLJ7IA2-DtUjuVbt.js +1 -0
  50. package/dist/assets/{timeline-definition-GMOUNBTQ-CwED_YTP.js → timeline-definition-GMOUNBTQ-DiY4hsig.js} +1 -1
  51. package/dist/assets/{vennDiagram-DHZGUBPP-BCoR7nJS.js → vennDiagram-DHZGUBPP-BQwRRtR6.js} +1 -1
  52. package/dist/assets/{wardley-RL74JXVD-yYqGgrtg.js → wardley-RL74JXVD-BvTEYtxw.js} +1 -1
  53. package/dist/assets/{wardleyDiagram-NUSXRM2D-BEPFl2rJ.js → wardleyDiagram-NUSXRM2D-C893ScYt.js} +1 -1
  54. package/dist/assets/{xychartDiagram-5P7HB3ND-DmEFd-vp.js → xychartDiagram-5P7HB3ND-d4f7pFLE.js} +1 -1
  55. package/dist/index.html +1 -1
  56. package/interceptor.js +135 -4
  57. package/lib/interceptor-core.js +95 -0
  58. package/package.json +1 -1
  59. package/server.js +66 -1
  60. package/dist/assets/channel-DMfVW6j3.js +0 -1
  61. package/dist/assets/classDiagram-6PBFFD2Q-DlpJX-68.js +0 -1
  62. package/dist/assets/classDiagram-v2-HSJHXN6E-DlpJX-68.js +0 -1
  63. package/dist/assets/clone-BBXBdZcN.js +0 -1
  64. package/dist/assets/stateDiagram-v2-QKLJ7IA2-OHkMopV-.js +0 -1
@@ -146,6 +146,101 @@ export function assembleStreamMessage(events) {
146
146
  return message;
147
147
  }
148
148
 
149
+ /**
150
+ * Incremental stream assembler — mutable state for SSE live streaming.
151
+ *
152
+ * Usage:
153
+ * const asm = createStreamAssembler();
154
+ * asm.feed(event); // consume each SSE event incrementally
155
+ * const snap = asm.snapshot(); // get current partial message
156
+ *
157
+ * Mirrors assembleStreamMessage but maintains mutable state for O(1) updates
158
+ * rather than O(n) rebuild per call.
159
+ */
160
+ export function createStreamAssembler() {
161
+ let message = null;
162
+ const contentBlocks = [];
163
+ let currentBlockIndex = -1;
164
+
165
+ return {
166
+ feed(event) {
167
+ if (!event || typeof event !== 'object' || !event.type) return;
168
+ switch (event.type) {
169
+ case 'message_start':
170
+ message = { ...event.message };
171
+ message.content = [];
172
+ break;
173
+ case 'content_block_start':
174
+ currentBlockIndex = event.index;
175
+ contentBlocks[currentBlockIndex] = { ...event.content_block };
176
+ if (contentBlocks[currentBlockIndex].type === 'text') {
177
+ contentBlocks[currentBlockIndex].text = '';
178
+ } else if (contentBlocks[currentBlockIndex].type === 'thinking') {
179
+ contentBlocks[currentBlockIndex].thinking = '';
180
+ }
181
+ break;
182
+ case 'content_block_delta':
183
+ if (event.index >= 0 && contentBlocks[event.index] && event.delta) {
184
+ if (event.delta.type === 'text_delta' && event.delta.text) {
185
+ contentBlocks[event.index].text += event.delta.text;
186
+ } else if (event.delta.type === 'input_json_delta' && event.delta.partial_json) {
187
+ if (typeof contentBlocks[event.index]._inputJson !== 'string') {
188
+ contentBlocks[event.index]._inputJson = '';
189
+ }
190
+ contentBlocks[event.index]._inputJson += event.delta.partial_json;
191
+ } else if (event.delta.type === 'thinking_delta' && event.delta.thinking) {
192
+ contentBlocks[event.index].thinking += event.delta.thinking;
193
+ } else if (event.delta.type === 'signature_delta' && event.delta.signature) {
194
+ contentBlocks[event.index].signature = event.delta.signature;
195
+ }
196
+ }
197
+ break;
198
+ case 'content_block_stop':
199
+ if (event.index >= 0 && contentBlocks[event.index]) {
200
+ const blk = contentBlocks[event.index];
201
+ if (blk.type === 'tool_use' && typeof blk._inputJson === 'string') {
202
+ try { blk.input = JSON.parse(blk._inputJson); }
203
+ catch { blk.input = blk._inputJson; }
204
+ delete blk._inputJson;
205
+ }
206
+ }
207
+ break;
208
+ case 'message_delta':
209
+ if (message && event.delta) {
210
+ if (event.delta.stop_reason) message.stop_reason = event.delta.stop_reason;
211
+ if (event.delta.stop_sequence !== undefined) message.stop_sequence = event.delta.stop_sequence;
212
+ }
213
+ if (message && event.usage) message.usage = { ...message.usage, ...event.usage };
214
+ break;
215
+ }
216
+ },
217
+ /**
218
+ * Return a snapshot of the current message state.
219
+ * For incomplete tool_use blocks (no content_block_stop yet), input is undefined
220
+ * and _inputJsonPartial carries the raw accumulated string.
221
+ * Deep clones to avoid mutation during live streaming.
222
+ */
223
+ snapshot() {
224
+ if (!message) return null;
225
+ const snapBlocks = [];
226
+ for (let i = 0; i < contentBlocks.length; i++) {
227
+ const b = contentBlocks[i];
228
+ if (!b) continue;
229
+ const clone = { ...b };
230
+ if (b.type === 'tool_use' && typeof b._inputJson === 'string') {
231
+ // Partial JSON - don't parse, expose as raw for UI hint
232
+ clone._inputJsonPartial = b._inputJson;
233
+ clone.input = undefined;
234
+ delete clone._inputJson;
235
+ }
236
+ snapBlocks.push(clone);
237
+ }
238
+ return { ...message, content: snapBlocks };
239
+ },
240
+ hasMessage() { return message !== null; },
241
+ };
242
+ }
243
+
149
244
  export function findRecentLog(dir, projectName) {
150
245
  try {
151
246
  const files = readdirSync(dir)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.6.160",
3
+ "version": "1.6.162",
4
4
  "description": "Claude Code Logger visualization management tool",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -34,7 +34,7 @@ function execWithStdin(cmd, args, input, options) {
34
34
  child.stdin.end();
35
35
  });
36
36
  }
37
- import { LOG_FILE, _initPromise, _resumeState, resolveResumeChoice, _projectName, _logDir, _cachedApiKey, _cachedAuthHeader, _cachedHaikuModel, initForWorkspace, resetWorkspace, streamingState, resetStreamingState, _loadProxyProfile, PROFILE_PATH, _defaultConfig } from './interceptor.js';
37
+ import { LOG_FILE, _initPromise, _resumeState, resolveResumeChoice, _projectName, _logDir, _cachedApiKey, _cachedAuthHeader, _cachedHaikuModel, initForWorkspace, resetWorkspace, streamingState, resetStreamingState, _loadProxyProfile, PROFILE_PATH, _defaultConfig, setLivePort } from './interceptor.js';
38
38
  import { LOG_DIR, setLogDir } from './findcc.js';
39
39
  import { t, detectLanguage } from './i18n.js';
40
40
  import { checkAndUpdate } from './lib/updater.js';
@@ -122,6 +122,10 @@ let pendingAskHook = null; // { questions, res, timer, createdAt }
122
122
  const pendingPermHooks = new Map(); // Map<id, { toolName, input, res, timer, createdAt }>
123
123
  const PERM_HOOK_MAP_MAX = 50;
124
124
 
125
+ // Live stream chunk sequence tracking (per request key) — prevents out-of-order broadcasts
126
+ const _liveStreamLastSeq = new Map(); // Map<`${timestamp}|${url}`, lastSeq>
127
+
128
+
125
129
  // Editor session state (for $EDITOR intercept)
126
130
  const editorSessions = new Map(); // sessionId → { filePath, done, createdAt }
127
131
  // Periodically clean up abandoned editor sessions (older than 1 hour)
@@ -2002,6 +2006,60 @@ async function handleRequest(req, res) {
2002
2006
  return;
2003
2007
  }
2004
2008
 
2009
+ // 流式 chunk 接收端点:interceptor 在 SSE 流过程中推送的 partial entry
2010
+ // 仅广播,不落盘。前端按 timestamp|url 自动与最终 entry 去重覆盖。
2011
+ // 鉴权:server 绑 0.0.0.0 允许同机任意进程访问,必须校验 remote 必须是 loopback
2012
+ // 且请求带 x-cc-viewer-internal: 1 header,防止同机其他进程伪造 SSE 内容注入广播。
2013
+ if (url === '/api/stream-chunk' && method === 'POST') {
2014
+ const remote = req.socket.remoteAddress || '';
2015
+ const isLoopback = remote === '127.0.0.1' || remote === '::1' || remote === '::ffff:127.0.0.1';
2016
+ const internalHeader = req.headers['x-cc-viewer-internal'] === '1';
2017
+ if (!isLoopback || !internalHeader) {
2018
+ try { res.writeHead(403, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Forbidden' })); } catch {}
2019
+ return;
2020
+ }
2021
+ let body = '';
2022
+ let aborted = false;
2023
+ req.on('data', (chunk) => {
2024
+ if (aborted) return;
2025
+ body += chunk;
2026
+ if (body.length > 8 * 1024 * 1024) {
2027
+ aborted = true;
2028
+ try { res.writeHead(413, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Payload too large' })); } catch {}
2029
+ }
2030
+ });
2031
+ req.on('end', () => {
2032
+ if (aborted) return;
2033
+ try {
2034
+ const entry = JSON.parse(body);
2035
+ const key = `${entry.timestamp}|${entry.url}`;
2036
+ const seq = typeof entry._chunkSeq === 'number' ? entry._chunkSeq : 0;
2037
+ const lastSeq = _liveStreamLastSeq.get(key);
2038
+ if (lastSeq !== undefined && seq < lastSeq) {
2039
+ // 乱序到达的旧 chunk 丢弃
2040
+ try { res.writeHead(204); res.end(); } catch {}
2041
+ return;
2042
+ }
2043
+ _liveStreamLastSeq.set(key, seq);
2044
+ // 清理 seq 记录:超过 200 条时 FIFO 驱逐最早的 100 条(Map 保持插入顺序)
2045
+ if (_liveStreamLastSeq.size > 200) {
2046
+ const keys = Array.from(_liveStreamLastSeq.keys()).slice(0, 100);
2047
+ for (const k of keys) _liveStreamLastSeq.delete(k);
2048
+ }
2049
+ // 用 named event 'stream-progress' 避免混入 data: 流与 dedup 冲突
2050
+ // 精简 payload:前端只需要 timestamp/url/content 渲染 Live overlay
2051
+ sendEventToClients(clients, 'stream-progress', {
2052
+ timestamp: entry.timestamp,
2053
+ url: entry.url,
2054
+ content: entry.response?.body?.content || [],
2055
+ model: entry.body?.model,
2056
+ });
2057
+ } catch {}
2058
+ try { res.writeHead(204); res.end(); } catch {}
2059
+ });
2060
+ return;
2061
+ }
2062
+
2005
2063
  // 读取文件内容 API
2006
2064
  if (url === '/api/file-content' && method === 'GET') {
2007
2065
  const reqPath = parsedUrl.searchParams.get('path');
@@ -2789,6 +2847,10 @@ export async function startViewer() {
2789
2847
  currentServer.listen(port, HOST, async () => {
2790
2848
  server = currentServer;
2791
2849
  actualPort = port;
2850
+ // interceptor.js runs in this same process (via proxy.js → setupInterceptor).
2851
+ // Inject live-port via module-level setter instead of process.env to avoid
2852
+ // polluting env of child_process.spawn descendants (Bash tools / MCP / Electron tabs).
2853
+ setLivePort(port);
2792
2854
  const url = `${serverProtocol}://127.0.0.1:${port}`;
2793
2855
  if (!isCliMode) {
2794
2856
  console.error(t('server.started'));
@@ -3174,6 +3236,9 @@ async function _doStop() {
3174
3236
  _streamingStatusTimer = null;
3175
3237
  }
3176
3238
  resetStreamingState();
3239
+ // 清 interceptor 的 live-port,避免 stop/start 循环(Electron tab 切换 / 测试)间隙内
3240
+ // 早期请求向已关闭的端口 POST 丢包。新 startViewer 的 listen 回调会再次 setLivePort
3241
+ setLivePort(null);
3177
3242
  try { unwatchFile(PROFILE_PATH); } catch {} // 清理 interceptor 的 StatWatcher
3178
3243
  }
3179
3244
 
@@ -1 +0,0 @@
1
- import{ap as o,aq as n}from"./mermaid.core-rKXVP1Sx.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-CJwUXaEg.js";import{_ as i}from"./mermaid.core-rKXVP1Sx.js";import"./chunk-FMBD7UC4-CsDh9m8Q.js";import"./chunk-YZCP3GAM-_OpiJebR.js";import"./chunk-55IACEB6-TyTrQiAX.js";import"./chunk-EDXVE4YY-rthcFfJc.js";import"./AppHeader.module-BuIkaeYL.js";import"./index-CmwiYM1W.js";var f={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{f as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-CJwUXaEg.js";import{_ as i}from"./mermaid.core-rKXVP1Sx.js";import"./chunk-FMBD7UC4-CsDh9m8Q.js";import"./chunk-YZCP3GAM-_OpiJebR.js";import"./chunk-55IACEB6-TyTrQiAX.js";import"./chunk-EDXVE4YY-rthcFfJc.js";import"./AppHeader.module-BuIkaeYL.js";import"./index-CmwiYM1W.js";var f={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{f as diagram};
@@ -1 +0,0 @@
1
- import{b as r}from"./graph-dQACcLqY.js";var e=4;function a(o){return r(o,e)}export{a as c};
@@ -1 +0,0 @@
1
- import{s as r,b as e,a,S as s}from"./chunk-OYMX7WX6-Fo7iMm6t.js";import{_ as i}from"./mermaid.core-rKXVP1Sx.js";import"./chunk-55IACEB6-TyTrQiAX.js";import"./chunk-EDXVE4YY-rthcFfJc.js";import"./index-CmwiYM1W.js";import"./AppHeader.module-BuIkaeYL.js";var u={parser:a,get db(){return new s(2)},renderer:e,styles:r,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{u as diagram};