cc-viewer 1.0.1 → 1.0.2

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 CHANGED
@@ -16,9 +16,9 @@ npm install -g cc-viewer
16
16
  ccv
17
17
  ```
18
18
 
19
- 该命令会自动配置本地安装的 Claude Code 以启用监控,并在 shell 配置文件(`~/.zshrc` 或 `~/.bashrc`)中添加自动修复 hook。之后正常使用 Claude Code,打开浏览器访问 `http://localhost:7008` 即可查看监控界面。
19
+ 该命令会自动将监控脚本注入到本地安装的 Claude Code 中,并在 shell 配置文件(`~/.zshrc` 或 `~/.bashrc`)中添加自动重注入 hook。之后正常使用 Claude Code,打开浏览器访问 `http://localhost:7008` 即可查看监控界面。
20
20
 
21
- Claude Code 更新后无需手动操作,下次运行 `claude` 时会自动检测并重新配置。
21
+ Claude Code 更新后无需手动操作,下次运行 `claude` 时会自动检测并重新注入。
22
22
 
23
23
  ### 卸载
24
24
 
@@ -26,7 +26,7 @@ Claude Code 更新后无需手动操作,下次运行 `claude` 时会自动检
26
26
  ccv --uninstall
27
27
  ```
28
28
 
29
- 一键清理 cli.js 中的配置和 shell 配置文件中的 hook。
29
+ 一键清理 cli.js 中的注入代码和 shell 配置文件中的 hook。
30
30
 
31
31
  ## 功能
32
32
 
@@ -52,8 +52,8 @@ ccv --uninstall
52
52
  - `thinking` 块默认折叠,点击展开查看思考过程
53
53
  - `tool_use` 显示为紧凑的工具调用卡片(Bash、Read、Edit、Write、Glob、Grep、Task 等均有专属展示)
54
54
  - 用户选择型消息(AskUserQuestion)以问答形式展示
55
- - 系统标签(`<system-reminder>`、`<project-reminder>` 等)自动折叠
56
- - 自动过滤系统文本,只展示用户的真实输入
55
+ - 系统注入标签(`<system-reminder>`、`<project-reminder>` 等)自动折叠
56
+ - 自动过滤系统注入文本,只展示用户的真实输入
57
57
  - 支持多 session 分段展示(`/compact`、`/clear` 等操作后自动分段)
58
58
  - 每条消息显示精确到秒的时间戳
59
59
 
package/interceptor.js CHANGED
@@ -18,7 +18,9 @@ function generateLogFilePath() {
18
18
  + String(now.getHours()).padStart(2, '0')
19
19
  + String(now.getMinutes()).padStart(2, '0')
20
20
  + String(now.getSeconds()).padStart(2, '0');
21
- const projectName = basename(process.cwd()).replace(/[^a-zA-Z0-9_\-\.]/g, '_');
21
+ let cwd;
22
+ try { cwd = process.cwd(); } catch { cwd = homedir(); }
23
+ const projectName = basename(cwd).replace(/[^a-zA-Z0-9_\-\.]/g, '_');
22
24
  const dir = join(homedir(), '.claude', 'cc-viewer');
23
25
  try { mkdirSync(dir, { recursive: true }); } catch {}
24
26
  return join(dir, `${projectName}_${ts}.jsonl`);
@@ -208,7 +210,7 @@ export function setupInterceptor() {
208
210
 
209
211
  requestEntry = {
210
212
  timestamp,
211
- project: basename(process.cwd()),
213
+ project: (() => { try { return basename(process.cwd()); } catch { return 'unknown'; } })(),
212
214
  url: urlStr,
213
215
  method: options?.method || 'GET',
214
216
  headers,
package/lib/server.js CHANGED
@@ -56,29 +56,9 @@ function checkPortAlive(port) {
56
56
  });
57
57
  }
58
58
 
59
- function registerLogToServer(port, logFile) {
60
- return new Promise((resolve) => {
61
- const data = JSON.stringify({ logFile });
62
- const req = httpRequest({
63
- host: HOST, port, path: '/api/register-log', method: 'POST',
64
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
65
- timeout: 2000,
66
- }, (res) => {
67
- res.resume();
68
- resolve(true);
69
- });
70
- req.on('error', () => resolve(false));
71
- req.on('timeout', () => { req.destroy(); resolve(false); });
72
- req.write(data);
73
- req.end();
74
- });
75
- }
76
-
77
59
  let clients = [];
78
60
  let server;
79
61
  let actualPort = START_PORT;
80
- // 跟踪所有被 watch 的日志文件
81
- const watchedFiles = new Map();
82
62
 
83
63
  const MIME_TYPES = {
84
64
  '.html': 'text/html; charset=utf-8',
@@ -123,13 +103,11 @@ function sendToClients(entry) {
123
103
  });
124
104
  }
125
105
 
126
- function watchLogFile(logFile) {
127
- if (watchedFiles.has(logFile)) return;
106
+ function startWatching() {
128
107
  let lastSize = 0;
129
- watchedFiles.set(logFile, true);
130
- watchFile(logFile, { interval: 500 }, () => {
108
+ watchFile(LOG_FILE, { interval: 500 }, () => {
131
109
  try {
132
- const content = readFileSync(logFile, 'utf-8');
110
+ const content = readFileSync(LOG_FILE, 'utf-8');
133
111
  const newContent = content.slice(lastSize);
134
112
  lastSize = content.length;
135
113
 
@@ -150,10 +128,6 @@ function watchLogFile(logFile) {
150
128
  });
151
129
  }
152
130
 
153
- function startWatching() {
154
- watchLogFile(LOG_FILE);
155
- }
156
-
157
131
  function handleRequest(req, res) {
158
132
  const { url, method } = req;
159
133
 
@@ -168,29 +142,6 @@ function handleRequest(req, res) {
168
142
  return;
169
143
  }
170
144
 
171
- // 注册新的日志文件进行 watch(供新进程复用旧服务时调用)
172
- if (url === '/api/register-log' && method === 'POST') {
173
- let body = '';
174
- req.on('data', chunk => { body += chunk; });
175
- req.on('end', () => {
176
- try {
177
- const { logFile } = JSON.parse(body);
178
- if (logFile && typeof logFile === 'string' && logFile.startsWith(LOG_DIR) && existsSync(logFile)) {
179
- watchLogFile(logFile);
180
- res.writeHead(200, { 'Content-Type': 'application/json' });
181
- res.end(JSON.stringify({ ok: true }));
182
- } else {
183
- res.writeHead(400, { 'Content-Type': 'application/json' });
184
- res.end(JSON.stringify({ error: 'Invalid log file path' }));
185
- }
186
- } catch {
187
- res.writeHead(400, { 'Content-Type': 'application/json' });
188
- res.end(JSON.stringify({ error: 'Invalid request body' }));
189
- }
190
- });
191
- return;
192
- }
193
-
194
145
  // SSE endpoint
195
146
  if (url === '/events' && method === 'GET') {
196
147
  res.writeHead(200, {
@@ -357,7 +308,6 @@ export async function startViewer() {
357
308
  const alive = await checkPortAlive(existingPort);
358
309
  if (alive) {
359
310
  actualPort = existingPort;
360
- await registerLogToServer(existingPort, LOG_FILE);
361
311
  return null;
362
312
  }
363
313
  }
@@ -376,7 +326,6 @@ export async function startViewer() {
376
326
  const alive = await checkPortAlive(existingPort);
377
327
  if (alive) {
378
328
  actualPort = existingPort;
379
- await registerLogToServer(existingPort, LOG_FILE);
380
329
  releaseLock();
381
330
  console.log(t('server.reuse', { host: HOST, port: existingPort }));
382
331
  return null;
@@ -429,10 +378,7 @@ export async function startViewer() {
429
378
  }
430
379
 
431
380
  export function stopViewer() {
432
- for (const logFile of watchedFiles.keys()) {
433
- unwatchFile(logFile);
434
- }
435
- watchedFiles.clear();
381
+ unwatchFile(LOG_FILE);
436
382
  clients.forEach(client => client.end());
437
383
  clients = [];
438
384
  if (server) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Claude Code Logger visualization management tool",
5
5
  "license": "MIT",
6
6
  "main": "lib/server.js",