cc-viewer 1.4.21 → 1.4.22

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/cli.js CHANGED
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
6
6
  import { homedir } from 'node:os';
7
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
+ import { INJECT_IMPORT, resolveCliPath, resolveNativePath, resolveNpmClaudePath, buildShellCandidates } from './findcc.js';
10
10
 
11
11
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
12
12
 
@@ -233,8 +233,15 @@ async function runProxyCommand(args) {
233
233
  }
234
234
 
235
235
  async function runCliMode(extraClaudeArgs = [], cwd) {
236
- const nativePath = resolveNativePath();
237
- if (!nativePath) {
236
+ // 首先尝试 npm 版本(包括 nvm 安装),找不到再尝试 native 版本
237
+ let claudePath = resolveNpmClaudePath();
238
+ let isNpmVersion = !!claudePath;
239
+
240
+ if (!claudePath) {
241
+ claudePath = resolveNativePath();
242
+ }
243
+
244
+ if (!claudePath) {
238
245
  console.error(t('cli.cMode.notFound'));
239
246
  process.exit(1);
240
247
  }
@@ -276,7 +283,7 @@ async function runCliMode(extraClaudeArgs = [], cwd) {
276
283
  // 3. 启动 PTY 中的 claude
277
284
  const { spawnClaude, killPty } = await import('./pty-manager.js');
278
285
  try {
279
- await spawnClaude(proxyPort, workingDir, extraClaudeArgs);
286
+ await spawnClaude(proxyPort, workingDir, extraClaudeArgs, claudePath, isNpmVersion);
280
287
  } catch (err) {
281
288
  console.error('[CC Viewer] Failed to spawn Claude:', err.message);
282
289
  serverMod.stopViewer();
@@ -304,8 +311,15 @@ async function runCliMode(extraClaudeArgs = [], cwd) {
304
311
  }
305
312
 
306
313
  async function runCliModeWorkspaceSelector(extraClaudeArgs = []) {
307
- const nativePath = resolveNativePath();
308
- if (!nativePath) {
314
+ // 首先尝试 npm 版本(包括 nvm 安装),找不到再尝试 native 版本
315
+ let claudePath = resolveNpmClaudePath();
316
+ let isNpmVersion = !!claudePath;
317
+
318
+ if (!claudePath) {
319
+ claudePath = resolveNativePath();
320
+ }
321
+
322
+ if (!claudePath) {
309
323
  console.error(t('cli.cMode.notFound'));
310
324
  process.exit(1);
311
325
  }
@@ -328,8 +342,9 @@ async function runCliModeWorkspaceSelector(extraClaudeArgs = []) {
328
342
 
329
343
  const port = serverMod.getPort();
330
344
 
331
- // 保存 extraClaudeArgs 供后续 launch 使用
345
+ // 保存 extraClaudeArgs 和 claudePath 供后续 launch 使用
332
346
  serverMod.setWorkspaceClaudeArgs(extraClaudeArgs);
347
+ serverMod.setWorkspaceClaudePath(claudePath, isNpmVersion);
333
348
 
334
349
  // 自动打开浏览器
335
350
  const url = `http://127.0.0.1:${port}`;
package/findcc.js CHANGED
@@ -61,6 +61,54 @@ export function resolveCliPath() {
61
61
  return join(globalRoot || resolve(__dirname, '..'), PACKAGES[0], CLI_ENTRY);
62
62
  }
63
63
 
64
+ /**
65
+ * 查找 npm 版本的 claude(包括 nvm 安装)
66
+ * 返回 node_modules 中的 claude cli.js 路径
67
+ */
68
+ export function resolveNpmClaudePath() {
69
+ // 1. 尝试 which/command -v 找到 npm 安装的 claude
70
+ for (const cmd of [`which ${BINARY_NAME}`, `command -v ${BINARY_NAME}`]) {
71
+ try {
72
+ const result = execSync(cmd, { encoding: 'utf-8', shell: true, env: process.env }).trim();
73
+ // 排除 shell function 的输出(多行说明不是路径)
74
+ if (result && !result.includes('\n') && existsSync(result)) {
75
+ // 只接受 npm 安装的符号链接(解析后指向 node_modules)
76
+ try {
77
+ const real = realpathSync(result);
78
+ if (real.includes('node_modules')) {
79
+ // 找到 npm 版本,返回 cli.js 的路径
80
+ // real 可能是 .../node_modules/@anthropic-ai/claude-code/bin/claude
81
+ // 我们需要返回 .../node_modules/@anthropic-ai/claude-code/cli.js
82
+ const match = real.match(/(.*node_modules\/@[^/]+\/[^/]+)\//);
83
+ if (match) {
84
+ const packageDir = match[1];
85
+ const cliPath = join(packageDir, CLI_ENTRY);
86
+ if (existsSync(cliPath)) {
87
+ return cliPath;
88
+ }
89
+ }
90
+ }
91
+ } catch {}
92
+ }
93
+ } catch {
94
+ // ignore
95
+ }
96
+ }
97
+
98
+ // 2. 尝试从全局 node_modules 查找
99
+ const globalRoot = getGlobalNodeModulesDir();
100
+ if (globalRoot) {
101
+ for (const packageName of PACKAGES) {
102
+ const cliPath = join(globalRoot, packageName, CLI_ENTRY);
103
+ if (existsSync(cliPath)) {
104
+ return cliPath;
105
+ }
106
+ }
107
+ }
108
+
109
+ return null;
110
+ }
111
+
64
112
  export function resolveNativePath() {
65
113
  // 1. 尝试 which/command -v(继承当前 process.env PATH)
66
114
  for (const cmd of [`which ${BINARY_NAME}`, `command -v ${BINARY_NAME}`]) {
@@ -130,7 +130,26 @@ export function getPluginsInfo() {
130
130
 
131
131
  return files.map(file => {
132
132
  const loaded = _plugins.find(p => p.file === file);
133
- const name = loaded ? loaded.name : file;
133
+ let name = file;
134
+
135
+ // 如果插件已加载,使用加载时的 name
136
+ if (loaded) {
137
+ name = loaded.name;
138
+ } else {
139
+ // 如果插件未加载(可能被禁用),尝试读取文件获取真实的 name
140
+ try {
141
+ const filePath = join(PLUGINS_DIR, file);
142
+ const content = readFileSync(filePath, 'utf-8');
143
+ // 简单匹配 name: 'xxx' 或 name: "xxx"
144
+ const match = content.match(/name\s*:\s*['"]([^'"]+)['"]/);
145
+ if (match) {
146
+ name = match[1];
147
+ }
148
+ } catch {
149
+ // 读取失败,使用文件名
150
+ }
151
+ }
152
+
134
153
  const hooks = loaded ? Object.keys(loaded.hooks) : [];
135
154
  const enabled = !disabledPlugins.includes(name);
136
155
  return { name, file, hooks, enabled };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.4.21",
3
+ "version": "1.4.22",
4
4
  "description": "Claude Code Logger visualization management tool",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
package/pty-manager.js CHANGED
@@ -25,7 +25,7 @@ function fixSpawnHelperPermissions() {
25
25
  } catch {}
26
26
  }
27
27
 
28
- export async function spawnClaude(proxyPort, cwd, extraArgs = []) {
28
+ export async function spawnClaude(proxyPort, cwd, extraArgs = [], claudePath = null, isNpmVersion = false) {
29
29
  if (ptyProcess) {
30
30
  killPty();
31
31
  }
@@ -35,9 +35,12 @@ export async function spawnClaude(proxyPort, cwd, extraArgs = []) {
35
35
 
36
36
  fixSpawnHelperPermissions();
37
37
 
38
- const claudePath = resolveNativePath();
38
+ // 如果没有提供 claudePath,尝试自动查找
39
39
  if (!claudePath) {
40
- throw new Error('claude not found');
40
+ claudePath = resolveNativePath();
41
+ if (!claudePath) {
42
+ throw new Error('claude not found');
43
+ }
41
44
  }
42
45
 
43
46
  const env = { ...process.env };
@@ -48,13 +51,20 @@ export async function spawnClaude(proxyPort, cwd, extraArgs = []) {
48
51
  env: { ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL }
49
52
  });
50
53
 
51
- const args = ['--settings', settingsJson, ...extraArgs];
54
+ let command = claudePath;
55
+ let args = ['--settings', settingsJson, ...extraArgs];
56
+
57
+ // 如果是 npm 版本(cli.js),需要使用 node 来运行
58
+ if (isNpmVersion && claudePath.endsWith('.js')) {
59
+ command = process.execPath; // node 可执行文件路径
60
+ args = [claudePath, '--settings', settingsJson, ...extraArgs];
61
+ }
52
62
 
53
63
  lastExitCode = null;
54
64
  outputBuffer = '';
55
65
  currentWorkspacePath = cwd || process.cwd();
56
66
 
57
- ptyProcess = pty.spawn(claudePath, args, {
67
+ ptyProcess = pty.spawn(command, args, {
58
68
  name: 'xterm-256color',
59
69
  cols: 120,
60
70
  rows: 30,
package/server.js CHANGED
@@ -26,10 +26,16 @@ const IGNORED_PATTERNS = new Set([
26
26
 
27
27
  // 工作区模式:保存 Claude 额外参数,供 launch API 使用
28
28
  let _workspaceClaudeArgs = [];
29
+ let _workspaceClaudePath = null;
30
+ let _workspaceIsNpmVersion = false;
29
31
  let _workspaceLaunched = false; // 工作区是否已经启动了会话
30
32
  export function setWorkspaceClaudeArgs(args) {
31
33
  _workspaceClaudeArgs = args;
32
34
  }
35
+ export function setWorkspaceClaudePath(path, isNpm) {
36
+ _workspaceClaudePath = path;
37
+ _workspaceIsNpmVersion = isNpm;
38
+ }
33
39
 
34
40
 
35
41
  // macOS user profile (avatar + display name), cached once
@@ -504,7 +510,7 @@ async function handleRequest(req, res) {
504
510
  const proxyPort = process.env.CCV_PROXY_PORT;
505
511
  if (proxyPort) {
506
512
  const { spawnClaude } = await import('./pty-manager.js');
507
- await spawnClaude(parseInt(proxyPort), wsPath, _workspaceClaudeArgs);
513
+ await spawnClaude(parseInt(proxyPort), wsPath, _workspaceClaudeArgs, _workspaceClaudePath, _workspaceIsNpmVersion);
508
514
  }
509
515
 
510
516
  _workspaceLaunched = true;