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 +22 -7
- package/findcc.js +48 -0
- package/lib/plugin-loader.js +20 -1
- package/package.json +1 -1
- package/pty-manager.js +15 -5
- package/server.js +7 -1
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
|
-
|
|
237
|
-
|
|
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
|
-
|
|
308
|
-
|
|
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}`]) {
|
package/lib/plugin-loader.js
CHANGED
|
@@ -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
|
-
|
|
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
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
|
-
|
|
38
|
+
// 如果没有提供 claudePath,尝试自动查找
|
|
39
39
|
if (!claudePath) {
|
|
40
|
-
|
|
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
|
-
|
|
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(
|
|
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;
|