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/README.md +46 -28
- package/cli.js +43 -102
- package/dist/assets/{index-CN78DISW.js → index-Do8E8Nxd.js} +110 -110
- package/dist/index.html +1 -1
- package/findcc.js +107 -0
- package/i18n.js +2140 -6
- package/interceptor.js +2 -3
- package/package.json +2 -1
- package/proxy.js +2 -110
- package/server.js +1 -1
- package/locales/i18n.json +0 -2139
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CC-Viewer
|
|
2
2
|
|
|
3
|
-
Claude Code request monitoring system that captures and visualizes all API requests and responses from Claude Code in real time (raw text,
|
|
3
|
+
A Claude Code request monitoring system that captures and visualizes all API requests and responses from Claude Code in real time (raw text, unredacted). Helps developers monitor their context for review and troubleshooting during Vibe Coding sessions.
|
|
4
4
|
|
|
5
5
|
English | [简体中文](./docs/README.zh.md) | [繁體中文](./docs/README.zh-TW.md) | [한국어](./docs/README.ko.md) | [日本語](./docs/README.ja.md) | [Deutsch](./docs/README.de.md) | [Español](./docs/README.es.md) | [Français](./docs/README.fr.md) | [Italiano](./docs/README.it.md) | [Dansk](./docs/README.da.md) | [Polski](./docs/README.pl.md) | [Русский](./docs/README.ru.md) | [العربية](./docs/README.ar.md) | [Norsk](./docs/README.no.md) | [Português (Brasil)](./docs/README.pt-BR.md) | [ไทย](./docs/README.th.md) | [Türkçe](./docs/README.tr.md) | [Українська](./docs/README.uk.md)
|
|
6
6
|
|
|
@@ -12,32 +12,40 @@ English | [简体中文](./docs/README.zh.md) | [繁體中文](./docs/README.zh-
|
|
|
12
12
|
npm install -g cc-viewer
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### Running and Auto-Configuration
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
ccv
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
This command automatically detects
|
|
21
|
+
This command automatically detects how Claude Code is installed locally (NPM or Native Install) and adapts accordingly.
|
|
22
22
|
|
|
23
23
|
- **NPM Install**: Automatically injects an interceptor script into Claude Code's `cli.js`.
|
|
24
|
-
- **Native Install**: Automatically detects the `claude` binary, configures a local transparent proxy, and sets up a Zsh Shell Hook to
|
|
24
|
+
- **Native Install**: Automatically detects the `claude` binary, configures a local transparent proxy, and sets up a Zsh Shell Hook to forward traffic automatically.
|
|
25
25
|
|
|
26
26
|
### Configuration Override
|
|
27
27
|
|
|
28
|
-
If you need to use a custom API endpoint (e.g., corporate proxy), simply configure it in `~/.claude/settings.json` or set the `ANTHROPIC_BASE_URL` environment variable. `ccv` will automatically detect
|
|
28
|
+
If you need to use a custom API endpoint (e.g., a corporate proxy), simply configure it in `~/.claude/settings.json` or set the `ANTHROPIC_BASE_URL` environment variable. `ccv` will automatically detect and correctly forward requests.
|
|
29
29
|
|
|
30
30
|
### Silent Mode
|
|
31
31
|
|
|
32
|
-
By default, `ccv` runs in silent mode when wrapping `claude`,
|
|
32
|
+
By default, `ccv` runs in silent mode when wrapping `claude`, keeping your terminal output clean and consistent with the native experience. All logs are captured in the background and can be viewed at `http://localhost:7008`.
|
|
33
33
|
|
|
34
|
-
Once configured,
|
|
34
|
+
Once configured, use the `claude` command as normal. Visit `http://localhost:7008` to access the monitoring interface.
|
|
35
35
|
|
|
36
36
|
### Troubleshooting
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
If you encounter issues starting cc-viewer, here is the ultimate troubleshooting approach:
|
|
39
|
+
|
|
40
|
+
Step 1: Open Claude Code in any directory.
|
|
41
|
+
|
|
42
|
+
Step 2: Give Claude Code the following instruction:
|
|
43
|
+
```
|
|
44
|
+
I have installed the cc-viewer npm package, but after running ccv it still doesn't work properly. Please check cc-viewer's cli.js and findcc.js, and adapt them to the local Claude Code deployment based on the specific environment. Keep the scope of changes as constrained as possible within findcc.js.
|
|
45
|
+
```
|
|
46
|
+
Letting Claude Code diagnose the issue itself is more effective than asking anyone or reading any documentation!
|
|
47
|
+
|
|
48
|
+
After the above instruction is completed, `findcc.js` will be updated. If your project frequently requires local deployment, or if forked code often needs to resolve installation issues, keeping this file lets you simply copy it next time. At this stage, many projects and companies using Claude Code are not deploying on Mac but rather on server-side hosted environments, so the author has separated `findcc.js` to make it easier to track cc-viewer source code updates going forward.
|
|
41
49
|
|
|
42
50
|
### Uninstall
|
|
43
51
|
|
|
@@ -55,44 +63,44 @@ ccv --version
|
|
|
55
63
|
|
|
56
64
|
### Request Monitoring (Raw Mode)
|
|
57
65
|
<img width="1500" height="720" alt="image" src="https://github.com/user-attachments/assets/519dd496-68bd-4e76-84d7-2a3d14ae3f61" />
|
|
58
|
-
-
|
|
59
|
-
- Automatically identifies and labels Main Agent and Sub Agent requests (
|
|
60
|
-
- MainAgent requests support Body Diff JSON, showing a
|
|
66
|
+
- Captures all API requests made by Claude Code in real time, ensuring raw content rather than truncated logs (this is important!!!)
|
|
67
|
+
- Automatically identifies and labels Main Agent and Sub Agent requests (subtypes: Bash, Task, Plan, General)
|
|
68
|
+
- MainAgent requests support Body Diff JSON, showing a collapsed diff of changes from the previous MainAgent request (only changed/added fields)
|
|
61
69
|
- Inline token usage stats per request (input/output tokens, cache creation/read, hit rate)
|
|
62
|
-
- Compatible with Claude Code Router (CCR) and other proxy
|
|
70
|
+
- Compatible with Claude Code Router (CCR) and other proxy scenarios — falls back to API path pattern matching
|
|
63
71
|
|
|
64
|
-
###
|
|
72
|
+
### Conversation Mode
|
|
65
73
|
|
|
66
|
-
Click the "
|
|
74
|
+
Click the "Conversation Mode" button in the top-right corner to parse the Main Agent's full conversation history into a chat interface:
|
|
67
75
|
<img width="1500" height="730" alt="image" src="https://github.com/user-attachments/assets/c973f142-748b-403f-b2b7-31a5d81e33e6" />
|
|
68
76
|
|
|
69
77
|
|
|
70
78
|
- Agent Team display is not yet supported
|
|
71
79
|
- User messages are right-aligned (blue bubbles), Main Agent replies are left-aligned (dark bubbles)
|
|
72
|
-
- `thinking` blocks are collapsed by default, rendered in Markdown,
|
|
73
|
-
- User selection messages (AskUserQuestion) are displayed in Q&A format
|
|
74
|
-
- Bidirectional mode sync: switching to
|
|
75
|
-
- Settings panel: toggle default
|
|
80
|
+
- `thinking` blocks are collapsed by default, rendered in Markdown, and can be expanded to view the reasoning process; one-click translation is supported (feature is still unstable)
|
|
81
|
+
- User selection messages (AskUserQuestion) are displayed in a Q&A format
|
|
82
|
+
- Bidirectional mode sync: switching to Conversation Mode automatically navigates to the conversation corresponding to the selected request; switching back to Raw Mode automatically navigates to the selected request
|
|
83
|
+
- Settings panel: toggle the default collapsed state of tool results and thinking blocks
|
|
76
84
|
|
|
77
85
|
|
|
78
86
|
### Statistics Tool
|
|
79
87
|
|
|
80
|
-
"Data Statistics"
|
|
88
|
+
The "Data Statistics" floating panel in the header area:
|
|
81
89
|
<img width="1500" height="729" alt="image" src="https://github.com/user-attachments/assets/b23f9a81-fc3d-4937-9700-e70d84e4e5ce" />
|
|
82
90
|
|
|
83
91
|
- Displays cache creation/read counts and cache hit rate
|
|
84
|
-
- Cache rebuild statistics: grouped by reason (TTL, system/tools/model
|
|
85
|
-
- Tool usage statistics:
|
|
86
|
-
- Skill usage statistics:
|
|
87
|
-
- Concept help (?)
|
|
92
|
+
- Cache rebuild statistics: grouped by reason (TTL, system/tools/model changes, message truncation/modification, key changes) showing counts and cache_creation tokens
|
|
93
|
+
- Tool usage statistics: displays call frequency for each tool sorted by number of calls
|
|
94
|
+
- Skill usage statistics: displays call frequency for each skill sorted by number of calls
|
|
95
|
+
- Concept help (?) icon: click to view built-in documentation for MainAgent, CacheRebuild, and each tool
|
|
88
96
|
|
|
89
97
|
### Log Management
|
|
90
98
|
|
|
91
|
-
Via the CC-Viewer dropdown menu in the top
|
|
99
|
+
Via the CC-Viewer dropdown menu in the top-left corner:
|
|
92
100
|
<img width="1200" height="672" alt="image" src="https://github.com/user-attachments/assets/8cf24f5b-9450-4790-b781-0cd074cd3b39" />
|
|
93
101
|
|
|
94
|
-
- Import local logs: browse historical log files
|
|
95
|
-
- Load local JSONL file: directly select
|
|
102
|
+
- Import local logs: browse historical log files grouped by project, open in a new window
|
|
103
|
+
- Load local JSONL file: directly select a local `.jsonl` file to load and view (supports up to 500MB)
|
|
96
104
|
- Save current log as: download the current monitoring JSONL log file
|
|
97
105
|
- Merge logs: combine multiple JSONL log files into a single session for unified analysis
|
|
98
106
|
- View user Prompts: extract and display all user inputs, supporting three view modes — Raw mode (original content), Context mode (system tags collapsible), Text mode (plain text); slash commands (`/model`, `/context`, etc.) shown as standalone entries; command-related tags are auto-hidden from Prompt content
|
|
@@ -107,3 +115,13 @@ CC-Viewer supports 18 languages, automatically switching based on system locale:
|
|
|
107
115
|
## License
|
|
108
116
|
|
|
109
117
|
MIT
|
|
118
|
+
|
|
119
|
+
### How to Submit a PR
|
|
120
|
+
The author welcomes and encourages PRs from the community.
|
|
121
|
+
|
|
122
|
+
A few requirements:
|
|
123
|
+
|
|
124
|
+
When submitting a PR, please tell me what your Prompt was and which model you used to modify the code (PRs made with inferior models will not be accepted);
|
|
125
|
+
If there are UI changes, tell me what functionality was modified on the interface — a screenshot with circles drawn around the changes is recommended;
|
|
126
|
+
Changes to cli.js and interceptor.js will be reviewed very carefully, as I don't want issues in core files to affect everyone's usage;
|
|
127
|
+
Please make sure to verify the functionality locally before submitting — much appreciated!
|
package/cli.js
CHANGED
|
@@ -1,58 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
4
|
-
import { resolve
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
|
-
import { spawn
|
|
7
|
+
import { spawn } from 'node:child_process';
|
|
8
8
|
import { t } from './i18n.js';
|
|
9
|
+
import { INJECT_IMPORT, resolveCliPath, resolveNativePath, buildShellCandidates } from './findcc.js';
|
|
9
10
|
|
|
10
11
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
11
12
|
|
|
12
13
|
const INJECT_START = '// >>> Start CC Viewer Web Service >>>';
|
|
13
14
|
const INJECT_END = '// <<< Start CC Viewer Web Service <<<';
|
|
14
|
-
const INJECT_IMPORT = "import '../../cc-viewer/interceptor.js';";
|
|
15
15
|
const INJECT_BLOCK = `${INJECT_START}\n${INJECT_IMPORT}\n${INJECT_END}`;
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
const SHELL_HOOK_START = '# >>> CC-Viewer Auto-Inject >>>';
|
|
19
19
|
const SHELL_HOOK_END = '# <<< CC-Viewer Auto-Inject <<<';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
const CLAUDE_CODE_PACKAGES = [
|
|
23
|
-
'@anthropic-ai/claude-code',
|
|
24
|
-
'@ali/claude-code',
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
function getGlobalNodeModulesDir() {
|
|
28
|
-
try {
|
|
29
|
-
return execSync('npm root -g', { encoding: 'utf-8' }).trim();
|
|
30
|
-
} catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function resolveClaudeCodeCliPath() {
|
|
36
|
-
// 候选基础目录:__dirname 的上级(适用于常规 npm 安装)+ 全局 node_modules(适用于符号链接安装)
|
|
37
|
-
const baseDirs = [resolve(__dirname, '..')];
|
|
38
|
-
const globalRoot = getGlobalNodeModulesDir();
|
|
39
|
-
if (globalRoot && globalRoot !== resolve(__dirname, '..')) {
|
|
40
|
-
baseDirs.push(globalRoot);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
for (const baseDir of baseDirs) {
|
|
44
|
-
for (const packageName of CLAUDE_CODE_PACKAGES) {
|
|
45
|
-
const candidate = join(baseDir, packageName, 'cli.js');
|
|
46
|
-
if (existsSync(candidate)) {
|
|
47
|
-
return candidate;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// 兜底:返回全局目录下的默认路径,便于错误提示
|
|
52
|
-
return join(globalRoot || resolve(__dirname, '..'), CLAUDE_CODE_PACKAGES[0], 'cli.js');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const cliPath = resolveClaudeCodeCliPath();
|
|
21
|
+
const cliPath = resolveCliPath();
|
|
56
22
|
|
|
57
23
|
function getShellConfigPath() {
|
|
58
24
|
const shell = process.env.SHELL || '';
|
|
@@ -80,10 +46,11 @@ claude() {
|
|
|
80
46
|
${SHELL_HOOK_END}`;
|
|
81
47
|
}
|
|
82
48
|
|
|
49
|
+
const candidates = buildShellCandidates();
|
|
83
50
|
return `${SHELL_HOOK_START}
|
|
84
51
|
claude() {
|
|
85
52
|
local cli_js=""
|
|
86
|
-
for candidate in
|
|
53
|
+
for candidate in ${candidates}; do
|
|
87
54
|
if [ -f "$candidate" ]; then
|
|
88
55
|
cli_js="$candidate"
|
|
89
56
|
break
|
|
@@ -161,37 +128,6 @@ function removeCliJsInjection() {
|
|
|
161
128
|
}
|
|
162
129
|
}
|
|
163
130
|
|
|
164
|
-
function getNativeInstallPath() {
|
|
165
|
-
// 1. 尝试 which/command -v(继承当前 process.env PATH)
|
|
166
|
-
for (const cmd of ['which claude', 'command -v claude']) {
|
|
167
|
-
try {
|
|
168
|
-
const result = execSync(cmd, { encoding: 'utf-8', shell: true, env: process.env }).trim();
|
|
169
|
-
// 排除 shell function 的输出(多行说明不是路径)
|
|
170
|
-
if (result && !result.includes('\n') && existsSync(result)) {
|
|
171
|
-
return result;
|
|
172
|
-
}
|
|
173
|
-
} catch {
|
|
174
|
-
// ignore
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// 2. 检查常见 native 安装路径
|
|
179
|
-
const home = homedir();
|
|
180
|
-
const candidates = [
|
|
181
|
-
join(home, '.claude', 'local', 'claude'),
|
|
182
|
-
'/usr/local/bin/claude',
|
|
183
|
-
join(home, '.local', 'bin', 'claude'),
|
|
184
|
-
'/opt/homebrew/bin/claude',
|
|
185
|
-
];
|
|
186
|
-
for (const p of candidates) {
|
|
187
|
-
if (existsSync(p)) {
|
|
188
|
-
return p;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
131
|
async function runProxyCommand(args) {
|
|
196
132
|
try {
|
|
197
133
|
// Dynamic import to avoid side effects when just installing
|
|
@@ -229,49 +165,24 @@ async function runProxyCommand(args) {
|
|
|
229
165
|
}
|
|
230
166
|
|
|
231
167
|
const env = { ...process.env };
|
|
232
|
-
// [Debug] Verify hook execution
|
|
233
|
-
// console.error(`[CC-Viewer] Hook triggered for: ${cmd} ${cmdArgs.join(' ')}`);
|
|
234
|
-
|
|
235
168
|
// Determine the path to the native 'claude' executable
|
|
236
169
|
if (cmd === 'claude') {
|
|
237
|
-
const nativePath =
|
|
170
|
+
const nativePath = resolveNativePath();
|
|
238
171
|
if (nativePath) {
|
|
239
172
|
cmd = nativePath;
|
|
240
173
|
}
|
|
241
174
|
}
|
|
242
175
|
env.ANTHROPIC_BASE_URL = `http://127.0.0.1:${proxyPort}`;
|
|
243
176
|
|
|
244
|
-
// [Debug] Force ANTHROPIC_BASE_URL via process.env is not enough for some reason?
|
|
245
|
-
// Let's also check if we can pass it via command line args if supported, but claude cli doesn't seem to have a --base-url arg documented.
|
|
246
|
-
// However, maybe the issue is that 'env' in spawn options isn't overriding what claude internal config has?
|
|
247
|
-
// Claude Code likely reads from ~/.claude/settings.json which might take precedence over env vars?
|
|
248
|
-
// No, usually env vars take precedence.
|
|
249
|
-
|
|
250
|
-
// [Fix] Check if user has ANTHROPIC_BASE_URL in settings.json and use it in proxy.js
|
|
251
|
-
// We already do that in proxy.js: getOriginalBaseUrl().
|
|
252
|
-
|
|
253
|
-
// [Crucial Fix]
|
|
254
|
-
// Use --settings JSON argument to force ANTHROPIC_BASE_URL configuration
|
|
255
|
-
// This is safer and more reliable than env vars which might be ignored.
|
|
256
|
-
|
|
257
|
-
// console.error(`[CC-Viewer] Setting ANTHROPIC_BASE_URL to ${env.ANTHROPIC_BASE_URL}`);
|
|
258
|
-
|
|
259
|
-
// Construct settings JSON string
|
|
260
|
-
// Note: We need to be careful with quoting for the shell/spawn.
|
|
261
|
-
// Since we use spawn without shell, we can pass the JSON string directly as an argument.
|
|
262
177
|
const settingsJson = JSON.stringify({
|
|
263
178
|
env: {
|
|
264
179
|
ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL
|
|
265
180
|
}
|
|
266
181
|
});
|
|
267
182
|
|
|
268
|
-
// Inject --settings argument
|
|
269
|
-
// We put it at the beginning of args to ensure it's picked up
|
|
270
183
|
cmdArgs.unshift(settingsJson);
|
|
271
184
|
cmdArgs.unshift('--settings');
|
|
272
185
|
|
|
273
|
-
// Force non-interactive if needed? No, we want interactive.
|
|
274
|
-
|
|
275
186
|
const child = spawn(cmd, cmdArgs, { stdio: 'inherit', env });
|
|
276
187
|
|
|
277
188
|
child.on('exit', (code) => {
|
|
@@ -312,7 +223,7 @@ if (isVersion) {
|
|
|
312
223
|
|
|
313
224
|
if (args[0] === 'run') {
|
|
314
225
|
runProxyCommand(args);
|
|
315
|
-
} else if (
|
|
226
|
+
} else if (isUninstall) {
|
|
316
227
|
const cliResult = removeCliJsInjection();
|
|
317
228
|
const shellResult = removeShellHook();
|
|
318
229
|
|
|
@@ -338,12 +249,42 @@ if (args[0] === 'run') {
|
|
|
338
249
|
} else {
|
|
339
250
|
// Installation Logic
|
|
340
251
|
let mode = 'unknown';
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
252
|
+
|
|
253
|
+
// Check PATH to determine priority
|
|
254
|
+
let prefersNative = true; // default to native if not found in PATH
|
|
255
|
+
const paths = (process.env.PATH || '').split(':');
|
|
256
|
+
for (const dir of paths) {
|
|
257
|
+
if (!dir) continue;
|
|
258
|
+
const exePath = resolve(dir, 'claude');
|
|
259
|
+
if (existsSync(exePath)) {
|
|
260
|
+
try {
|
|
261
|
+
const real = realpathSync(exePath);
|
|
262
|
+
if (real.includes('node_modules')) {
|
|
263
|
+
prefersNative = false;
|
|
264
|
+
} else {
|
|
265
|
+
prefersNative = true;
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
} catch (e) {
|
|
269
|
+
// ignore
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const nativePath = resolveNativePath();
|
|
275
|
+
const hasNpm = existsSync(cliPath);
|
|
276
|
+
|
|
277
|
+
if (prefersNative) {
|
|
345
278
|
if (nativePath) {
|
|
346
279
|
mode = 'native';
|
|
280
|
+
} else if (hasNpm) {
|
|
281
|
+
mode = 'npm';
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
if (hasNpm) {
|
|
285
|
+
mode = 'npm';
|
|
286
|
+
} else if (nativePath) {
|
|
287
|
+
mode = 'native';
|
|
347
288
|
}
|
|
348
289
|
}
|
|
349
290
|
|