cc-viewer 1.5.2 → 1.5.4

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/index.html CHANGED
@@ -6,8 +6,8 @@
6
6
  <title>Claude Code Viewer</title>
7
7
  <link rel="icon" href="/favicon.ico?v=1">
8
8
  <link rel="shortcut icon" href="/favicon.ico?v=1">
9
- <script type="module" crossorigin src="/assets/index-DgmtYyOQ.js"></script>
10
- <link rel="stylesheet" crossorigin href="/assets/index-Bj_6pwE9.css">
9
+ <script type="module" crossorigin src="/assets/index-BVa7fmnE.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/assets/index-BPhYI4on.css">
11
11
  </head>
12
12
  <body>
13
13
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "Claude Code Logger visualization management tool",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
package/proxy.js CHANGED
@@ -127,6 +127,8 @@ export function startProxy() {
127
127
  const { Readable, pipeline } = await import('node:stream');
128
128
  // @ts-ignore
129
129
  const nodeStream = Readable.fromWeb(response.body);
130
+ // 持久 error handler 兜底:防止 pipeline 清理后延迟到达的 error 事件导致进程崩溃
131
+ nodeStream.on('error', () => {});
130
132
  // pipeline handles stream errors; without this, unhandled 'error' events crash the process.
131
133
  pipeline(nodeStream, res, (err) => {
132
134
  if (err && process.env.CCV_DEBUG) {
package/pty-manager.js CHANGED
@@ -14,6 +14,45 @@ const MAX_BUFFER = 200000;
14
14
  let batchBuffer = '';
15
15
  let batchScheduled = false;
16
16
 
17
+ /**
18
+ * 在 outputBuffer 截断时,找到安全的截断位置,
19
+ * 避免从 ANSI 转义序列中间开始导致终端状态紊乱。
20
+ * 策略:从截断点向后扫描,跳过可能被截断的不完整转义序列。
21
+ */
22
+ function findSafeSliceStart(buf, rawStart) {
23
+ // 从 rawStart 开始,向后最多扫描 64 字节寻找安全起点
24
+ const scanLimit = Math.min(rawStart + 64, buf.length);
25
+ let i = rawStart;
26
+ while (i < scanLimit) {
27
+ const ch = buf.charCodeAt(i);
28
+ // 如果当前字符是 ESC (0x1b),可能是新转义序列的开头,
29
+ // 但也可能是被截断的序列的中间部分,跳过整个序列
30
+ if (ch === 0x1b) {
31
+ // 找到 ESC,向后寻找序列结束符(字母字符)
32
+ let j = i + 1;
33
+ while (j < scanLimit && !((buf.charCodeAt(j) >= 0x40 && buf.charCodeAt(j) <= 0x7e) && j > i + 1)) {
34
+ j++;
35
+ }
36
+ if (j < scanLimit) {
37
+ // 找到完整序列末尾,从下一个字符开始是安全的
38
+ return j + 1;
39
+ }
40
+ // 序列不完整,继续扫描
41
+ i = j;
42
+ continue;
43
+ }
44
+ // 如果字符是 CSI 参数字符 (0x30-0x3f) 或中间字符 (0x20-0x2f),
45
+ // 说明我们在转义序列中间,继续向后
46
+ if ((ch >= 0x20 && ch <= 0x3f)) {
47
+ i++;
48
+ continue;
49
+ }
50
+ // 普通可见字符或控制字符(非转义相关),这是安全位置
51
+ break;
52
+ }
53
+ return i < buf.length ? i : rawStart;
54
+ }
55
+
17
56
  function flushBatch() {
18
57
  batchScheduled = false;
19
58
  if (!batchBuffer) return;
@@ -87,7 +126,9 @@ export async function spawnClaude(proxyPort, cwd, extraArgs = [], claudePath = n
87
126
  ptyProcess.onData((data) => {
88
127
  outputBuffer += data;
89
128
  if (outputBuffer.length > MAX_BUFFER) {
90
- outputBuffer = outputBuffer.slice(-MAX_BUFFER);
129
+ const rawStart = outputBuffer.length - MAX_BUFFER;
130
+ const safeStart = findSafeSliceStart(outputBuffer, rawStart);
131
+ outputBuffer = outputBuffer.slice(safeStart);
91
132
  }
92
133
  batchBuffer += data;
93
134
  if (!batchScheduled) {