cc-viewer 1.2.7 → 1.2.9

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/interceptor.js CHANGED
@@ -4,6 +4,7 @@ import { appendFileSync, mkdirSync, readdirSync, readFileSync, writeFileSync, st
4
4
  import { homedir } from 'node:os';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { dirname, join, basename } from 'node:path';
7
+ import { LOG_DIR } from './findcc.js';
7
8
 
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
@@ -30,7 +31,7 @@ function generateNewLogFilePath() {
30
31
  let cwd;
31
32
  try { cwd = process.cwd(); } catch { cwd = homedir(); }
32
33
  const projectName = basename(cwd).replace(/[^a-zA-Z0-9_\-\.]/g, '_');
33
- const dir = join(homedir(), '.claude', 'cc-viewer', projectName);
34
+ const dir = join(LOG_DIR, projectName);
34
35
  try { mkdirSync(dir, { recursive: true }); } catch { }
35
36
  return { filePath: join(dir, `${projectName}_${ts}.jsonl`), dir, projectName };
36
37
  }
@@ -241,8 +242,6 @@ function isAnthropicApiPath(urlStr) {
241
242
  }
242
243
  }
243
244
 
244
- // 不再需要折叠函数,保存完整 JSON 供前端渲染
245
-
246
245
  // 组装流式消息为完整的 message 对象
247
246
  function assembleStreamMessage(events) {
248
247
  let message = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.2.7",
3
+ "version": "1.2.9",
4
4
  "description": "Claude Code Logger visualization management tool",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
@@ -47,6 +47,7 @@
47
47
  "proxy.js",
48
48
  "interceptor.js",
49
49
  "i18n.js",
50
+ "findcc.js",
50
51
  "locales/",
51
52
  "concepts/"
52
53
  ],
package/proxy.js CHANGED
@@ -34,13 +34,7 @@ function getOriginalBaseUrl() {
34
34
  export function startProxy() {
35
35
  return new Promise((resolve, reject) => {
36
36
  const server = createServer(async (req, res) => {
37
- // [Debug] Log incoming request
38
- // console.error(`[CC-Viewer Proxy] Received request: ${req.method} ${req.url}`);
39
-
40
- // Handle CORS preflight if needed (though claude cli probably doesn't send OPTIONS)
41
-
42
37
  const originalBaseUrl = getOriginalBaseUrl();
43
- const targetUrl = new URL(req.url, originalBaseUrl);
44
38
 
45
39
  // Use the patched fetch (which logs to cc-viewer)
46
40
  try {
@@ -48,25 +42,12 @@ export function startProxy() {
48
42
  const headers = { ...req.headers };
49
43
  delete headers.host; // Let fetch set the host
50
44
 
51
- // [Fix] Handle compressed body
52
- // If content-encoding is set (e.g. gzip), and we read the raw stream into a buffer,
53
- // we are passing the compressed buffer as body.
54
- // fetch will automatically add content-length, but might not handle content-encoding correctly if we just pass the buffer?
55
- // Actually, fetch should handle it fine if we pass the headers.
56
-
57
- // However, if we are reading the body here to pass it to fetch, we are buffering it.
58
- // For large uploads this might be bad, but for text prompts it's fine.
59
-
60
- // We need to read the body if any
61
45
  const buffers = [];
62
46
  for await (const chunk of req) {
63
47
  buffers.push(chunk);
64
48
  }
65
49
  const body = Buffer.concat(buffers);
66
50
 
67
- // [Debug] Log body size
68
- // console.error(`[CC-Viewer Proxy] Request body size: ${body.length}`);
69
-
70
51
  const fetchOptions = {
71
52
  method: req.method,
72
53
  headers: headers,
@@ -80,94 +61,14 @@ export function startProxy() {
80
61
  fetchOptions.body = body;
81
62
  }
82
63
 
83
- // [Crucial Fix]
84
- // If originalBaseUrl is also a proxy or special endpoint, make sure we construct the full URL correctly.
85
- // originalBaseUrl might end with /v1 or not.
86
- // req.url from proxy server is usually just the path (e.g. /v1/messages) if client is configured with base_url=http://localhost:port
87
- // So new URL(req.url, originalBaseUrl) should work.
88
-
89
- // However, if originalBaseUrl already contains a path (e.g. /api/anthropic), and req.url is /v1/messages,
90
- // new URL(req.url, originalBaseUrl) might treat req.url as absolute path if it starts with /, replacing the path in originalBaseUrl?
91
- // Let's test: new URL('/v1/messages', 'https://example.com/api').toString() -> 'https://example.com/v1/messages' (path replaced!)
92
-
93
- // This is why we get 404! The user's base URL is https://antchat.alipay.com/api/anthropic
94
- // But our proxy constructs: https://antchat.alipay.com/v1/messages
95
- // It lost the /api/anthropic part!
96
-
97
- // We need to append req.url to the pathname of originalBaseUrl, carefully avoiding double slashes.
98
-
99
- const originalUrlObj = new URL(originalBaseUrl);
100
- // Ensure original pathname doesn't end with slash if we append
101
- let basePath = originalUrlObj.pathname;
102
- if (basePath.endsWith('/')) basePath = basePath.slice(0, -1);
103
-
104
- // req.url starts with / usually
105
- const reqPath = req.url.startsWith('/') ? req.url : '/' + req.url;
106
-
107
- // Check if we should join them
108
- // If req.url is /v1/messages, and base is /api/anthropic, we want /api/anthropic/v1/messages
109
- originalUrlObj.pathname = basePath + reqPath;
110
- // Search params are already in req.url? Yes, req.url includes query string in Node http server.
111
- // Wait, new URL(req.url, base) parses query string correctly.
112
- // But if we manually concat pathname, we need to handle query string separately.
113
-
114
- // Let's do it simpler: use string concatenation for the full URL
115
- // But we need to handle the origin correctly.
116
-
117
- // Better approach:
118
- // 1. Remove trailing slash from originalBaseUrl
64
+ // 拼接完整 URL,保留 originalBaseUrl 中的路径前缀
119
65
  const cleanBase = originalBaseUrl.endsWith('/') ? originalBaseUrl.slice(0, -1) : originalBaseUrl;
120
- // 2. Remove leading slash from req.url
121
66
  const cleanReq = req.url.startsWith('/') ? req.url.slice(1) : req.url;
122
- // 3. Join
123
67
  const fullUrl = `${cleanBase}/${cleanReq}`;
124
68
 
125
- // [Debug] Proxying to
126
- // console.error(`[CC-Viewer Proxy] Forwarding to: ${fullUrl}`);
127
-
128
69
  const response = await fetch(fullUrl, fetchOptions);
129
70
 
130
- // [Crucial Fix]
131
- // Handle decompression manually if needed.
132
- // Node's fetch automatically decompresses if 'compress: true' is default?
133
- // Actually, fetch handles gzip/deflate by default.
134
- // But if we pipe the response body to the client response (res), we need to be careful.
135
- // The issue is likely that we are trying to read the body as text/json for logging,
136
- // but it might be compressed or binary.
137
-
138
- // Let's modify how we handle the response body.
139
- // We need to:
140
- // 1. Pipe the response to the client (res) so Claude Code gets the data.
141
- // 2. Clone the response to read it for logging? fetch response.clone() might not work with streaming body easily.
142
-
143
- // Better approach: intercept the stream.
144
- // Or simply: don't log the response body for now to avoid breaking the stream.
145
- // User just wants to see the request in the viewer.
146
- // If we want to log response, we need to handle it carefully.
147
-
148
- // Let's check where ZlibError comes from. It likely comes from `response.text()` or `response.json()`
149
- // if the content-encoding header is set but fetch didn't decompress it automatically?
150
- // Or maybe we are double decompressing?
151
-
152
- // Wait, if we use `response.body.pipe(res)`, that's fine.
153
- // But do we read the body elsewhere?
154
-
155
- // Let's look at how we log the response.
156
- // We are not logging response body in this proxy.js currently.
157
- // Wait, line 105: `response.body.pipe(res);`
158
-
159
- // If the error happens, it might be because `fetch` failed to decompress?
160
- // Or maybe `response.body` is already decompressed stream, but we are piping it to `res` which expects raw?
161
- // No, `res` (http.ServerResponse) expects raw data.
162
-
163
- // If `fetch` decompresses automatically, then `response.body` yields decompressed chunks.
164
- // But `res` writes those chunks to the client.
165
- // The client (Claude Code) expects compressed data if it sent `Accept-Encoding: gzip`.
166
- // If we send decompressed data but keep `Content-Encoding: gzip` header, client will try to decompress again -> ZlibError!
167
-
168
- // FIX: Remove content-encoding header from response headers before piping to client.
169
- // This tells the client "the data I'm sending you is NOT compressed" (because fetch already decompressed it).
170
-
71
+ // fetch 自动解压,需移除编码相关 header 避免客户端重复解压
171
72
  const responseHeaders = {};
172
73
  for (const [key, value] of response.headers.entries()) {
173
74
  // Skip Content-Encoding and Transfer-Encoding to let Node/Client handle it
@@ -178,20 +79,11 @@ export function startProxy() {
178
79
 
179
80
  res.writeHead(response.status, responseHeaders);
180
81
 
181
- // Also log that we are piping
182
- // console.error(`[CC-Viewer Proxy] Response status: ${response.status}`);
183
-
184
82
  if (response.body) {
185
- // We need to convert Web Stream (response.body) to Node Stream for piping to res
186
- // Node 18+ fetch returns a Web ReadableStream.
187
- // We can use Readable.fromWeb(response.body)
188
83
  const { Readable } = await import('node:stream');
189
84
  // @ts-ignore
190
85
  const nodeStream = Readable.fromWeb(response.body);
191
86
  nodeStream.pipe(res);
192
-
193
- // Optional: Log response body for debugging (careful with streams)
194
- // For now, let's just ensure reliability.
195
87
  } else {
196
88
  res.end();
197
89
  }
package/server.js CHANGED
@@ -5,9 +5,9 @@ import { dirname, join, extname, basename } from 'node:path';
5
5
  import { homedir, userInfo, platform } from 'node:os';
6
6
  import { execSync } from 'node:child_process';
7
7
  import { LOG_FILE, _initPromise, _resumeState, resolveResumeChoice, _projectName, _cachedApiKey, _cachedAuthHeader, _cachedHaikuModel } from './interceptor.js';
8
+ import { LOG_DIR } from './findcc.js';
8
9
  import { t, detectLanguage } from './i18n.js';
9
10
 
10
- const LOG_DIR = join(homedir(), '.claude', 'cc-viewer');
11
11
  const PREFS_FILE = join(LOG_DIR, 'preferences.json');
12
12
 
13
13