imtoagent 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/modules/utils/backend-check.ts +84 -14
- package/package.json +2 -2
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// ================================================================
|
|
4
4
|
|
|
5
5
|
import { execSync } from 'child_process';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
6
8
|
|
|
7
9
|
export interface BackendInfo {
|
|
8
10
|
type: 'claude' | 'codex' | 'opencode';
|
|
@@ -18,20 +20,64 @@ const BACKEND_DEFS: Omit<BackendInfo, 'installed' | 'version'>[] = [
|
|
|
18
20
|
{ type: 'opencode', label: 'OpenCode', installHint: 'npm install -g opencode' },
|
|
19
21
|
];
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
// ================================================================
|
|
24
|
+
// 获取 npm 全局 bin 目录
|
|
25
|
+
// 解决 PATH 未包含 npm global bin 时的检测失败问题
|
|
26
|
+
// ================================================================
|
|
27
|
+
|
|
28
|
+
let _cachedNpmBin: string | null | undefined = undefined;
|
|
29
|
+
|
|
30
|
+
function getNpmGlobalBin(): string | null {
|
|
31
|
+
if (_cachedNpmBin !== undefined) return _cachedNpmBin;
|
|
22
32
|
try {
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
version = execSync('codex --version', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
28
|
-
} else {
|
|
29
|
-
version = execSync('opencode version', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
33
|
+
const prefix = execSync('npm get prefix', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
34
|
+
if (!prefix) {
|
|
35
|
+
_cachedNpmBin = null;
|
|
36
|
+
return null;
|
|
30
37
|
}
|
|
38
|
+
const binDir = path.join(prefix, 'bin');
|
|
39
|
+
if (fs.existsSync(binDir)) {
|
|
40
|
+
_cachedNpmBin = binDir;
|
|
41
|
+
return binDir;
|
|
42
|
+
}
|
|
43
|
+
_cachedNpmBin = null;
|
|
44
|
+
return null;
|
|
45
|
+
} catch {
|
|
46
|
+
_cachedNpmBin = null;
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function checkOne(b: Omit<BackendInfo, 'installed' | 'version'>): BackendInfo {
|
|
52
|
+
const versionCmd: Record<string, string> = {
|
|
53
|
+
claude: 'claude --version',
|
|
54
|
+
codex: 'codex --version',
|
|
55
|
+
opencode: 'opencode version',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// 先尝试 PATH 中的命令
|
|
59
|
+
try {
|
|
60
|
+
const version = execSync(versionCmd[b.type], { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
31
61
|
return { ...b, installed: true, version };
|
|
32
62
|
} catch {
|
|
33
|
-
|
|
63
|
+
// PATH 中找不到,继续尝试 npm global bin
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// fallback:直接从 npm global bin 目录运行
|
|
67
|
+
const npmBin = getNpmGlobalBin();
|
|
68
|
+
if (npmBin) {
|
|
69
|
+
const binPath = path.join(npmBin, b.type);
|
|
70
|
+
try {
|
|
71
|
+
if (fs.existsSync(binPath)) {
|
|
72
|
+
const version = execSync(`"${binPath}" --version`, { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
73
|
+
return { ...b, installed: true, version };
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// bin 存在但执行失败,视为未安装
|
|
77
|
+
}
|
|
34
78
|
}
|
|
79
|
+
|
|
80
|
+
return { ...b, installed: false, version: null };
|
|
35
81
|
}
|
|
36
82
|
|
|
37
83
|
export function checkAllBackends(): BackendInfo[] {
|
|
@@ -75,11 +121,16 @@ export async function installBackend(
|
|
|
75
121
|
console.log(` 命令: ${b.installHint}\n`);
|
|
76
122
|
|
|
77
123
|
try {
|
|
78
|
-
//
|
|
79
|
-
const
|
|
124
|
+
// 获取 npm 全局 bin 目录,用于安装后验证(复用 getNpmGlobalBin 缓存)
|
|
125
|
+
const npmBinDir = getNpmGlobalBin();
|
|
126
|
+
|
|
127
|
+
// 用 zsh -ic 加载用户 shell 环境(匹配 macOS 默认 shell)
|
|
128
|
+
// 传入当前 PATH 环境变量,确保 npm 可执行文件可访问
|
|
129
|
+
const child = Bun.spawn(['zsh', '-ic', b.installHint], {
|
|
80
130
|
stdout: 'pipe',
|
|
81
131
|
stderr: 'pipe',
|
|
82
|
-
stdin: '
|
|
132
|
+
stdin: 'ignore',
|
|
133
|
+
env: { ...process.env },
|
|
83
134
|
});
|
|
84
135
|
|
|
85
136
|
const decoder = new TextDecoder();
|
|
@@ -104,16 +155,35 @@ export async function installBackend(
|
|
|
104
155
|
|
|
105
156
|
if (exitCode !== 0) {
|
|
106
157
|
console.error(`\n❌ ${b.label} 安装失败 (退出码: ${exitCode})`);
|
|
158
|
+
console.error(` 可手动运行: ${b.installHint}`);
|
|
107
159
|
return false;
|
|
108
160
|
}
|
|
109
161
|
|
|
110
|
-
// 安装完成后验证
|
|
162
|
+
// 安装完成后验证 — 优先用 npm bin 目录直接检查
|
|
163
|
+
if (npmBinDir) {
|
|
164
|
+
const binPath = path.join(npmBinDir, b.type);
|
|
165
|
+
try {
|
|
166
|
+
if (fs.existsSync(binPath)) {
|
|
167
|
+
const version = execSync(`"${binPath}" --version`, { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
168
|
+
console.log(`\n✅ ${b.label} 安装成功! 版本: ${version}`);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
} catch {}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// fallback: 通过 PATH 查找
|
|
111
175
|
const info = checkOne(b);
|
|
112
176
|
if (info.installed) {
|
|
113
177
|
console.log(`\n✅ ${b.label} 安装成功! 版本: ${info.version}`);
|
|
114
178
|
return true;
|
|
115
179
|
} else {
|
|
116
|
-
console.error(`\n❌ ${b.label}
|
|
180
|
+
console.error(`\n❌ ${b.label} 安装后仍未检测到`);
|
|
181
|
+
if (npmBinDir) {
|
|
182
|
+
console.error(` npm 全局 bin 目录: ${npmBinDir}`);
|
|
183
|
+
console.error(` 建议将该目录添加到 PATH,或手动运行: ${b.installHint}`);
|
|
184
|
+
} else {
|
|
185
|
+
console.error(` 请手动运行: ${b.installHint}`);
|
|
186
|
+
}
|
|
117
187
|
return false;
|
|
118
188
|
}
|
|
119
189
|
} catch (e: any) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imtoagent",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "IM
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "IM ↔ Agent 统一网关 — 飞书/Telegram/微信/企业微信对接 Claude Code/Codex/OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"imtoagent": "./bin/imtoagent.cjs"
|