@znmin/claude-remote-cli 0.3.0 → 0.3.1

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
@@ -25,10 +25,10 @@ npm install -g git+https://github.com/znmin/claude-remote-cli.git
25
25
  也可以从已下载的分发包安装:
26
26
 
27
27
  ```bash
28
- npm install -g ./znmin-claude-remote-cli-0.3.0.tgz
28
+ npm install -g ./znmin-claude-remote-cli-0.3.1.tgz
29
29
  ```
30
30
 
31
- 若系统 npm 全局目录不可写,可使用 `npm install -g --prefix "$HOME/.local" ./znmin-claude-remote-cli-0.3.0.tgz`,并确保 `~/.local/bin` 在 PATH 中;卸载时也使用相同的 `--prefix`。
31
+ 若系统 npm 全局目录不可写,可使用 `npm install -g --prefix "$HOME/.local" ./znmin-claude-remote-cli-0.3.1.tgz`,并确保 `~/.local/bin` 在 PATH 中;卸载时也使用相同的 `--prefix`。
32
32
 
33
33
  npm 中不带作用域的 `claude-remote-cli` 属于其他项目。本项目使用独立的 `@znmin/claude-remote-cli` 作用域包名;请安装完整包名,或使用上述 GitHub 地址及本项目分发的 `.tgz` 文件。
34
34
 
@@ -52,7 +52,7 @@ claude
52
52
  - 文件工具在 Windows 上按不区分大小写的方式判断路径是否位于项目内。CRLF 换行的文件读取和搜索时不显示 `\r`,`edit_file` 按 LF 文本匹配时会保留原有 CRLF 换行(适用于所有平台)。
53
53
  - 卸载、配置和缓存目录与其他平台相同,分别位于 `%USERPROFILE%\.config\claude-remote`、`%USERPROFILE%\.cache\claude-remote` 和 `%USERPROFILE%\.ssh\claude-remote`。
54
54
 
55
- 如果用户已经安装其他提供 `claude` 命令的软件,应先卸载旧客户端,避免 npm 命令冲突或 PATH 指向旧程序。无需使用 `--force` 覆盖。
55
+ 如果用户已经安装其他提供 `claude` 命令的软件,应先卸载旧客户端,避免 npm 命令冲突或 PATH 指向旧程序。无需使用 `--force` 覆盖。Windows 上如果 `where.exe claude` 指向本工具,运行 `claude` 却报错(如 `claude.exe not found on PATH`),通常是 PowerShell 配置文件中残留了同名函数或别名;`claude-remote doctor` 会列出它及其所在文件。
56
56
 
57
57
  ## 开始使用
58
58
 
@@ -256,7 +256,7 @@ npm run test:windows # Windows 上运行的客户端测试(CI 在 windows-la
256
256
  CLAUDE_REMOTE_INTEGRATION=1 CLAUDE_REMOTE_BROWSER_TEST=1 npm test
257
257
 
258
258
  npm pack
259
- npm install -g ./znmin-claude-remote-cli-0.3.0.tgz
259
+ npm install -g ./znmin-claude-remote-cli-0.3.1.tgz
260
260
 
261
261
  # 确定包名、版本和使用条款后,使用有发布权限的 npm 账号发布
262
262
  npm login
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@znmin/claude-remote-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Run Claude Code on an SSH server from macOS, Linux or Windows with local filesystem, terminal and browser tools, or remote bidirectional sync",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/src/cli.js CHANGED
@@ -127,6 +127,8 @@ export async function main(asClaude, args = process.argv.slice(2), client = new
127
127
  const { describeShell, findShell } = await import('./local/shell.js');
128
128
  const shell = await findShell(client.env);
129
129
  console.log(`本地终端:${describeShell(shell)}(${shell.file})${shell.kind === 'powershell' ? ';安装 Git for Windows 后改用 Git Bash' : ''}`);
130
+ const { conflictReport } = await import('./powershell-conflicts.js');
131
+ for (const line of await conflictReport(client.env)) console.log(line);
130
132
  }
131
133
  try {
132
134
  const { browserReady } = await import('./local/browser.js');
@@ -0,0 +1,67 @@
1
+ import path from 'node:path';
2
+ import { execute, which } from './system.js';
3
+
4
+ const MARKER = 'CLAUDE_REMOTE_JSON:';
5
+
6
+ // Runs with the user's profile loaded, as an interactive window would. Only
7
+ // aliases and functions are reported: they win over programs on PATH, so a
8
+ // leftover `function claude` hides this tool while where.exe shows nothing wrong.
9
+ const SCRIPT = [
10
+ "$ErrorActionPreference = 'SilentlyContinue'",
11
+ 'try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}',
12
+ '$items = @(Get-Command claude -All -CommandType Alias,Function,Filter | ForEach-Object {',
13
+ " $alias = $_.CommandType -eq 'Alias'",
14
+ ' [pscustomobject]@{ type = [string]$_.CommandType; target = $(if ($alias) { [string]$_.Definition } else { $null });',
15
+ ' file = $(if ($alias) { $null } else { [string]$_.ScriptBlock.File }); module = [string]$_.ModuleName }',
16
+ '})',
17
+ `'${MARKER}' + (ConvertTo-Json -Compress -Depth 3 -InputObject ([pscustomobject]@{ profile = [string]$PROFILE; items = $items }))`
18
+ ].join('\n');
19
+
20
+ const encoded = Buffer.from(SCRIPT, 'utf16le').toString('base64');
21
+
22
+ export async function powershellHosts(env = process.env) {
23
+ const hosts = [{ name: 'Windows PowerShell',
24
+ file: path.win32.join(env.SystemRoot || env.SYSTEMROOT || 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe') }];
25
+ const pwsh = await which('pwsh', env.PATH || env.Path || '', { platform: 'win32' });
26
+ if (pwsh) hosts.push({ name: 'PowerShell 7', file: pwsh });
27
+ return hosts;
28
+ }
29
+
30
+ export function parseConflicts(stdout) {
31
+ const line = stdout.split(/\r?\n/).reverse().find(text => text.startsWith(MARKER));
32
+ if (!line) throw new Error('没有得到检查结果');
33
+ const result = JSON.parse(line.slice(MARKER.length));
34
+ const items = result.items == null ? [] : [].concat(result.items);
35
+ return { profile: result.profile || '', items };
36
+ }
37
+
38
+ export async function findClaudeConflicts(host, { env = process.env, run = execute, timeout = 20000 } = {}) {
39
+ const { stdout } = await run(host.file, ['-NoLogo', '-NonInteractive', '-EncodedCommand', encoded],
40
+ { capture: true, check: false, timeout, env });
41
+ return parseConflicts(stdout);
42
+ }
43
+
44
+ export function describeConflict(item) {
45
+ if (item.type === 'Alias') return `别名 → ${item.target}`;
46
+ const kind = item.type === 'Filter' ? '筛选器' : '函数';
47
+ if (item.file) return `${kind}(定义于 ${item.file})`;
48
+ if (item.module) return `${kind}(来自模块 ${item.module})`;
49
+ return kind;
50
+ }
51
+
52
+ // Lines for `claude-remote doctor`.
53
+ export async function conflictReport(env = process.env, options = {}) {
54
+ const lines = [];
55
+ for (const host of await powershellHosts(env)) {
56
+ try {
57
+ const { profile, items } = await findClaudeConflicts(host, { env, ...options });
58
+ if (!items.length) { lines.push(`${host.name} 中的 claude:无冲突`); continue; }
59
+ lines.push(`${host.name} 中的 claude:被占用,在该终端输入 claude 不会运行本工具`);
60
+ for (const item of items) lines.push(' - ' + describeConflict(item));
61
+ lines.push(` 处理:删除上述定义(配置文件 ${profile},可用 notepad $PROFILE 打开)后重新打开终端;临时可运行 claude.cmd`);
62
+ } catch (error) {
63
+ lines.push(`${host.name} 中的 claude:无法检查(${error.message})`);
64
+ }
65
+ }
66
+ return lines;
67
+ }