shizai-agent-mcp 0.5.4 → 0.7.29
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 +13 -11
- package/bin/shizai-agent-mcp.js +147 -60
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
# shizai-agent-mcp
|
|
2
2
|
|
|
3
|
-
实在 Agent 本地 MCP 的轻量 npm/npx
|
|
3
|
+
实在 Agent 本地 MCP 的轻量 npm/npx 安装器。默认安装 MCP、准备独立浏览器扩展文件并注册本地连接程序。
|
|
4
4
|
|
|
5
5
|
## 使用
|
|
6
6
|
|
|
7
7
|
```powershell
|
|
8
|
-
npx -y shizai-agent-mcp@latest install
|
|
9
|
-
npx -y shizai-agent-mcp@latest
|
|
8
|
+
npx -y shizai-agent-mcp@latest install
|
|
9
|
+
npx -y shizai-agent-mcp@latest status
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
也可以注册 Cursor 或 Claude Desktop:
|
|
13
13
|
|
|
14
14
|
```powershell
|
|
15
|
-
npx -y shizai-agent-mcp@latest register cursor
|
|
16
|
-
npx -y shizai-agent-mcp@latest register claude
|
|
15
|
+
npx -y shizai-agent-mcp@latest register cursor
|
|
16
|
+
npx -y shizai-agent-mcp@latest register claude
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
常用命令:
|
|
20
20
|
|
|
21
21
|
```powershell
|
|
22
|
-
npx -y shizai-agent-mcp@latest status
|
|
23
|
-
npx -y shizai-agent-mcp@latest path
|
|
24
|
-
npx -y shizai-agent-mcp@latest uninstall
|
|
22
|
+
npx -y shizai-agent-mcp@latest status
|
|
23
|
+
npx -y shizai-agent-mcp@latest path
|
|
24
|
+
npx -y shizai-agent-mcp@latest uninstall
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
从 `0.7.29` 起 npm 安装器与 MCP 采用同一版本号,默认下载对应的 Windows x64 发布包。
|
|
28
|
+
安装前校验实际构建产生的工具清单、扩展文件和所有载荷哈希,缺件或版本不符直接失败。
|
|
29
|
+
首次使用仍须在 `chrome://extensions` 或 `edge://extensions` 中加载返回的扩展目录;
|
|
30
|
+
浏览器是否真正连接以 `shizai_browser_status` 为准。可用 `--without-browser-extension` 跳过本地连接程序注册。
|
|
29
31
|
|
|
30
32
|
`install` 覆盖文件后会自动注册 Codex MCP;需要跳过配置写入时使用
|
|
31
33
|
`install --skip-register`,之后可单独运行 `register codex`。
|
|
32
34
|
|
|
33
35
|
发布 Release 时必须同时上传同名 `.sha256` 文件。安装器先校验、解压到同盘暂存目录,
|
|
34
|
-
|
|
36
|
+
再替换现有版本;注册成功后才清理旧安装。升级保留扩展连接密钥和端口。测试企业内网镜像时可设置
|
|
35
37
|
`SHIZAI_AGENT_RELEASE_BASE_URL`,测试其他版本可设置 `SHIZAI_AGENT_VERSION`。
|
|
36
38
|
仅临时调试未签名镜像时可显式设置 `SHIZAI_AGENT_ALLOW_UNVERIFIED=1`。
|
|
37
39
|
|
package/bin/shizai-agent-mcp.js
CHANGED
|
@@ -9,7 +9,7 @@ const { execFileSync, spawn } = require('node:child_process');
|
|
|
9
9
|
|
|
10
10
|
const SERVER = 'shizai-agent';
|
|
11
11
|
const REPO = process.env.SHIZAI_AGENT_REPO || 'yimoxzy/shizai-agent';
|
|
12
|
-
const DEFAULT_VERSION = process.env.SHIZAI_AGENT_VERSION || '
|
|
12
|
+
const DEFAULT_VERSION = process.env.SHIZAI_AGENT_VERSION || require('../package.json').version;
|
|
13
13
|
const installRoot = process.env.SHIZAI_AGENT_HOME || path.join(
|
|
14
14
|
process.env.LOCALAPPDATA || path.join(os.homedir(), '.local'), 'Programs', 'ShizaiAgent'
|
|
15
15
|
);
|
|
@@ -17,7 +17,7 @@ const installRoot = process.env.SHIZAI_AGENT_HOME || path.join(
|
|
|
17
17
|
function fail(message) { console.error(`shizai-agent-mcp: ${message}`); process.exitCode = 1; }
|
|
18
18
|
function json(value) { console.log(JSON.stringify(value, null, 2)); }
|
|
19
19
|
function help() {
|
|
20
|
-
console.log(`Shizai Agent MCP installer\n\nUsage:\n shizai-agent-mcp install [--version VERSION]\n shizai-agent-mcp register <codex|cursor|claude>\n shizai-agent-mcp status\n shizai-agent-mcp path\n shizai-agent-mcp uninstall\n\nEnvironment:\n SHIZAI_AGENT_VERSION Release version (default: ${DEFAULT_VERSION})\n SHIZAI_AGENT_HOME Installation directory\n SHIZAI_AGENT_REPO GitHub repository owner/name\n SHIZAI_AGENT_RELEASE_BASE_URL Override release asset base URL\n`);
|
|
20
|
+
console.log(`Shizai Agent MCP installer\n\nUsage:\n shizai-agent-mcp install [--version VERSION] [--without-browser-extension]\n shizai-agent-mcp register <codex|cursor|claude|browser>\n shizai-agent-mcp status\n shizai-agent-mcp path\n shizai-agent-mcp uninstall\n\nBrowser extension files and native host registration are prepared by default.\nLoad the extension in Chrome/Edge once; --without-browser-extension skips host setup.\n\nEnvironment:\n SHIZAI_AGENT_VERSION Release version (default: ${DEFAULT_VERSION})\n SHIZAI_AGENT_HOME Installation directory\n SHIZAI_AGENT_REPO GitHub repository owner/name\n SHIZAI_AGENT_RELEASE_BASE_URL Override release asset base URL\n`);
|
|
21
21
|
}
|
|
22
22
|
function binaryPath() { return path.join(installRoot, 'shizai-agent-mcp.exe'); }
|
|
23
23
|
function validateInstallRoot() {
|
|
@@ -26,11 +26,21 @@ function validateInstallRoot() {
|
|
|
26
26
|
if (resolved === parsed.root || resolved === path.resolve(os.homedir())) {
|
|
27
27
|
throw new Error(`拒绝使用过宽的安装目录:${resolved}`);
|
|
28
28
|
}
|
|
29
|
+
if (fs.existsSync(resolved) && fs.readdirSync(resolved).length && !fs.existsSync(path.join(resolved, 'shizai-agent-mcp.exe'))) {
|
|
30
|
+
throw new Error('拒绝替换非 Shizai Agent 安装目录');
|
|
31
|
+
}
|
|
29
32
|
return resolved;
|
|
30
33
|
}
|
|
31
34
|
function psQuote(value) { return `'${String(value).replaceAll("'", "''")}'`; }
|
|
35
|
+
function powerShellOptions() {
|
|
36
|
+
// A host running PowerShell 7 can pass its module search path through Node.
|
|
37
|
+
// Windows PowerShell must resolve its own built-in modules first.
|
|
38
|
+
const builtin = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'Modules');
|
|
39
|
+
return { encoding: 'utf8', windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'],
|
|
40
|
+
env: { ...process.env, PSModulePath: [builtin, process.env.PSModulePath].filter(Boolean).join(path.delimiter) } };
|
|
41
|
+
}
|
|
32
42
|
function runPowerShell(script, args = []) {
|
|
33
|
-
return execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script, ...args],
|
|
43
|
+
return execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script, ...args], powerShellOptions());
|
|
34
44
|
}
|
|
35
45
|
function sleepSync(milliseconds) {
|
|
36
46
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
@@ -53,7 +63,8 @@ function stopInstalledProcesses(destination) {
|
|
|
53
63
|
if (process.platform !== 'win32') return [];
|
|
54
64
|
const executables = [
|
|
55
65
|
path.join(destination, 'shizai-agent-mcp.exe'),
|
|
56
|
-
path.join(destination, 'shizai-agent.exe')
|
|
66
|
+
path.join(destination, 'shizai-agent.exe'),
|
|
67
|
+
path.join(destination, 'shizai-agent-browser-host.exe')
|
|
57
68
|
];
|
|
58
69
|
const targetList = executables.map(psQuote).join(', ');
|
|
59
70
|
const script = String.raw`
|
|
@@ -112,22 +123,22 @@ function verifySha256(file, expected) {
|
|
|
112
123
|
const actual = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex').toLowerCase();
|
|
113
124
|
if (actual !== expected.toLowerCase()) throw new Error(`SHA256 校验失败:期望 ${expected},实际 ${actual}`);
|
|
114
125
|
}
|
|
115
|
-
function download(url, destination, options = {}) {
|
|
116
|
-
const progress = options.progress !== false;
|
|
117
|
-
const curl = process.env.SHIZAI_AGENT_CURL || 'curl.exe';
|
|
118
|
-
const args = ['--fail', '--location', '--silent', '--show-error'];
|
|
119
|
-
if (progress) args.push('--progress-bar');
|
|
120
|
-
args.push('--output', destination, url);
|
|
121
|
-
try {
|
|
122
|
-
execFileSync(curl, args, { stdio: ['ignore', 'ignore', 'inherit'] });
|
|
123
|
-
if (progress) process.stderr.write('\n');
|
|
124
|
-
return;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
if (error?.code !== 'ENOENT') throw error;
|
|
127
|
-
}
|
|
128
|
-
const script =
|
|
129
|
-
runPowerShell(script);
|
|
130
|
-
}
|
|
126
|
+
function download(url, destination, options = {}) {
|
|
127
|
+
const progress = options.progress !== false;
|
|
128
|
+
const curl = process.env.SHIZAI_AGENT_CURL || 'curl.exe';
|
|
129
|
+
const args = ['--fail', '--location', '--silent', '--show-error'];
|
|
130
|
+
if (progress) args.push('--progress-bar');
|
|
131
|
+
args.push('--output', destination, url);
|
|
132
|
+
try {
|
|
133
|
+
execFileSync(curl, args, { stdio: ['ignore', 'ignore', 'inherit'] });
|
|
134
|
+
if (progress) process.stderr.write('\n');
|
|
135
|
+
return;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
138
|
+
}
|
|
139
|
+
const script = `$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -UseBasicParsing -Uri ${psQuote(url)} -OutFile ${psQuote(destination)}`;
|
|
140
|
+
runPowerShell(script);
|
|
141
|
+
}
|
|
131
142
|
function install(version, options = {}) {
|
|
132
143
|
const destination = validateInstallRoot();
|
|
133
144
|
const asset = assetName(version);
|
|
@@ -140,11 +151,12 @@ function install(version, options = {}) {
|
|
|
140
151
|
const stage = path.join(parent, `.shizai-agent-stage-${nonce}`);
|
|
141
152
|
const backup = path.join(parent, `.shizai-agent-backup-${nonce}`);
|
|
142
153
|
let verified = false;
|
|
154
|
+
let committed = false;
|
|
143
155
|
const stoppedProcesses = new Set();
|
|
144
156
|
try {
|
|
145
|
-
download(url, archive, { progress: true });
|
|
146
|
-
try {
|
|
147
|
-
download(`${url}.sha256`, checksum, { progress: false });
|
|
157
|
+
download(url, archive, { progress: true });
|
|
158
|
+
try {
|
|
159
|
+
download(`${url}.sha256`, checksum, { progress: false });
|
|
148
160
|
const text = fs.readFileSync(checksum, 'utf8');
|
|
149
161
|
const expected = (text.match(/[a-f0-9]{64}/i) || [])[0];
|
|
150
162
|
if (!expected) throw new Error('校验文件中没有 SHA-256');
|
|
@@ -166,10 +178,17 @@ function install(version, options = {}) {
|
|
|
166
178
|
if (!nested) throw new Error('发布包中未找到 shizai-agent-mcp.exe');
|
|
167
179
|
source = nested;
|
|
168
180
|
}
|
|
169
|
-
|
|
170
|
-
|
|
181
|
+
const verification = verifyPackage(source, version);
|
|
182
|
+
fs.writeFileSync(path.join(source, 'version.json'), JSON.stringify({
|
|
183
|
+
version, asset, installedAt: new Date().toISOString(),
|
|
184
|
+
manifest_sha256: verification.manifest_sha256, tools: verification.tools
|
|
185
|
+
}, null, 2));
|
|
186
|
+
const oldBridge = path.join(destination, 'browser-bridge.json');
|
|
187
|
+
if (fs.existsSync(oldBridge)) fs.copyFileSync(oldBridge, path.join(source, 'browser-bridge.json'));
|
|
171
188
|
for (const processId of stopInstalledProcesses(destination)) stoppedProcesses.add(processId);
|
|
172
189
|
let previousMoved = false;
|
|
190
|
+
const result = { installed: true, version, directory: destination, command: binaryPath(), verified,
|
|
191
|
+
tools: verification.tools, browser_extension_version: verification.extension_version };
|
|
173
192
|
try {
|
|
174
193
|
if (fs.existsSync(destination)) {
|
|
175
194
|
fs.renameSync(destination, backup);
|
|
@@ -177,51 +196,69 @@ function install(version, options = {}) {
|
|
|
177
196
|
for (const processId of stopInstalledProcesses(destination)) stoppedProcesses.add(processId);
|
|
178
197
|
}
|
|
179
198
|
fs.renameSync(source, destination);
|
|
199
|
+
if (!options.skipRegister) result.registration = registerCodex(true);
|
|
200
|
+
if (options.withBrowserExtension !== false) result.browser_extension = registerBrowser(true);
|
|
201
|
+
committed = true;
|
|
180
202
|
} catch (error) {
|
|
181
|
-
if (
|
|
203
|
+
if (previousMoved && fs.existsSync(backup)) {
|
|
204
|
+
for (const processId of stopInstalledProcesses(destination)) stoppedProcesses.add(processId);
|
|
205
|
+
// Keep the old installation recoverable until registration also succeeds.
|
|
206
|
+
const failedInstall = path.join(parent, `.shizai-agent-failed-${nonce}`);
|
|
207
|
+
if (fs.existsSync(destination)) fs.renameSync(destination, failedInstall);
|
|
182
208
|
fs.renameSync(backup, destination);
|
|
209
|
+
removeDirectoryWithRetries(failedInstall);
|
|
183
210
|
}
|
|
184
211
|
throw error;
|
|
185
212
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
removeDirectoryWithRetries(backup);
|
|
193
|
-
} catch (retryError) {
|
|
194
|
-
const failedInstall = path.join(temp, 'failed-install');
|
|
195
|
-
fs.renameSync(destination, failedInstall);
|
|
196
|
-
fs.renameSync(backup, destination);
|
|
197
|
-
removeDirectoryWithRetries(failedInstall);
|
|
198
|
-
throw new Error(`旧版本清理失败,已恢复原安装:${retryError.message}`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
213
|
+
result.stopped_processes = [...stoppedProcesses].sort((left, right) => left - right);
|
|
214
|
+
console.log(`安装成功:Shizai Agent MCP v${version}`);
|
|
215
|
+
if (result.registration?.registered) console.log('Codex MCP 已注册');
|
|
216
|
+
if (result.browser_extension?.registered) {
|
|
217
|
+
console.log(`浏览器扩展文件已准备:${result.browser_extension.extension_path}`);
|
|
218
|
+
console.log('首次使用:在 chrome://extensions 或 edge://extensions 加载该目录;连接状态以 shizai_browser_status 为准。');
|
|
201
219
|
}
|
|
202
|
-
|
|
203
|
-
const result = { installed: true, version, directory: destination, command: binaryPath(), verified, stopped_processes: [...stoppedProcesses].sort((left, right) => left - right) };
|
|
204
|
-
if (!options.skipRegister) result.registration = registerCodex(true);
|
|
205
|
-
console.log(`安装成功:Shizai Agent MCP v${version}`);
|
|
206
|
-
if (result.registration?.registered) console.log('Codex MCP 已注册');
|
|
207
|
-
return result;
|
|
220
|
+
return result;
|
|
208
221
|
} finally {
|
|
209
222
|
removeDirectoryWithRetries(temp);
|
|
210
223
|
removeDirectoryWithRetries(stage);
|
|
211
|
-
if (
|
|
224
|
+
if (committed && fs.existsSync(backup)) removeDirectoryWithRetries(backup);
|
|
212
225
|
}
|
|
213
226
|
}
|
|
227
|
+
function verifyPackage(root, version) {
|
|
228
|
+
const script = path.join(root, 'scripts', 'verify-package.ps1');
|
|
229
|
+
if (!fs.existsSync(script)) throw new Error('发布包缺少完整性校验器,请使用新版发布包');
|
|
230
|
+
const output = execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
|
|
231
|
+
'-File', script, '-PackageRoot', root, '-Version', version],
|
|
232
|
+
powerShellOptions()).replace(/^\uFEFF/, '').trim();
|
|
233
|
+
return JSON.parse(output);
|
|
234
|
+
}
|
|
235
|
+
function installationStatus() {
|
|
236
|
+
const metadataFile = path.join(installRoot, 'version.json');
|
|
237
|
+
const metadata = fs.existsSync(metadataFile) ? JSON.parse(fs.readFileSync(metadataFile, 'utf8').replace(/^\uFEFF/, '')) : {};
|
|
238
|
+
let verification;
|
|
239
|
+
try { verification = verifyPackage(installRoot, metadata.version || DEFAULT_VERSION); }
|
|
240
|
+
catch (_) { verification = { verified: false, reason: '安装文件缺失、损坏或来自未校验的旧发布包,请重新安装最新版' }; }
|
|
241
|
+
return { installed: fs.existsSync(binaryPath()), version: metadata.version || null, installer_version: require('../package.json').version,
|
|
242
|
+
directory: installRoot, command: binaryPath(), verification,
|
|
243
|
+
browser_extension_files_present: fs.existsSync(path.join(installRoot, 'browser-extension', 'manifest.json')),
|
|
244
|
+
native_host_present: fs.existsSync(path.join(installRoot, 'shizai-agent-browser-host.exe')),
|
|
245
|
+
browser_config_present: fs.existsSync(path.join(installRoot, 'browser-bridge.json')),
|
|
246
|
+
browser_extension_loaded: null, browser_extension_path: path.join(installRoot, 'browser-extension') };
|
|
247
|
+
}
|
|
214
248
|
function replaceTomlSection(content, section, replacement) {
|
|
215
|
-
const header = `[${section}]`;
|
|
216
249
|
const lines = String(content || '').split(/\r?\n/);
|
|
217
250
|
const kept = [];
|
|
218
251
|
let skipping = false;
|
|
219
252
|
for (const line of lines) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
253
|
+
const match = line.trim().match(/^\[([^\]]+)\]$/);
|
|
254
|
+
if (match) {
|
|
255
|
+
const table = match[1];
|
|
256
|
+
if (table === section || table.startsWith(`${section}.`)) {
|
|
257
|
+
skipping = true;
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
skipping = false;
|
|
223
261
|
}
|
|
224
|
-
if (skipping && /^\s*\[.*\]\s*$/.test(line)) skipping = false;
|
|
225
262
|
if (!skipping) kept.push(line);
|
|
226
263
|
}
|
|
227
264
|
const next = kept.join('\n').trimEnd();
|
|
@@ -233,10 +270,27 @@ function registerCodex(silent = false) {
|
|
|
233
270
|
fs.mkdirSync(path.dirname(config), { recursive: true });
|
|
234
271
|
const old = fs.existsSync(config) ? fs.readFileSync(config, 'utf8') : '';
|
|
235
272
|
if (old) fs.copyFileSync(config, `${config}.shizai-agent.bak`);
|
|
236
|
-
|
|
273
|
+
// Keep this local stdio server on Codex's direct tool path. Some custom
|
|
274
|
+
// Responses providers do not support Codex's deferred tool-search payload.
|
|
275
|
+
const section = `[mcp_servers.${SERVER}]\ncommand = ${tomlString(binaryPath())}\nargs = []\nenabled = true\nstartup_timeout_sec = 30\ntool_timeout_sec = 120\nomit_tools_from = [\"deferred\"]\n\n[mcp_servers.${SERVER}.env]\nSHIZAI_MCP_TOOL_PROFILE = \"rpa-authoring\"`;
|
|
237
276
|
const next = replaceTomlSection(old, `mcp_servers.${SERVER}`, section);
|
|
238
277
|
fs.writeFileSync(config, next, 'utf8');
|
|
239
|
-
const
|
|
278
|
+
const persisted = fs.readFileSync(config, 'utf8');
|
|
279
|
+
const verified = persisted.includes(`[mcp_servers.${SERVER}]`) &&
|
|
280
|
+
persisted.includes(`[mcp_servers.${SERVER}.env]`) &&
|
|
281
|
+
persisted.includes(`command = ${tomlString(binaryPath())}`) &&
|
|
282
|
+
persisted.includes('enabled = true') &&
|
|
283
|
+
persisted.includes('SHIZAI_MCP_TOOL_PROFILE = "rpa-authoring"');
|
|
284
|
+
if (!verified) throw new Error('Codex MCP 配置写入后校验失败');
|
|
285
|
+
const result = {
|
|
286
|
+
registered: true,
|
|
287
|
+
verified,
|
|
288
|
+
client: 'codex',
|
|
289
|
+
config,
|
|
290
|
+
command: binaryPath(),
|
|
291
|
+
tool_profile: 'rpa-authoring',
|
|
292
|
+
restart_required: true
|
|
293
|
+
};
|
|
240
294
|
if (!silent) json(result);
|
|
241
295
|
return result;
|
|
242
296
|
}
|
|
@@ -254,9 +308,37 @@ function registerJsonClient(client) {
|
|
|
254
308
|
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
255
309
|
json({ registered: true, client, config: file, command: binaryPath() });
|
|
256
310
|
}
|
|
311
|
+
function registerBrowser(silent = false) {
|
|
312
|
+
const script = path.join(installRoot, 'scripts', 'register-browser.ps1');
|
|
313
|
+
if (!fs.existsSync(script)) throw new Error('发布包中缺少 register-browser.ps1');
|
|
314
|
+
const output = execFileSync('powershell.exe', [
|
|
315
|
+
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
|
|
316
|
+
'-File', script, '-InstallDir', installRoot
|
|
317
|
+
], powerShellOptions()).replace(/^\uFEFF/, '').trim();
|
|
318
|
+
const result = JSON.parse(output);
|
|
319
|
+
if (!silent) json(result);
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
function unregisterBrowser() {
|
|
323
|
+
const script = path.join(installRoot, 'scripts', 'unregister-browser.ps1');
|
|
324
|
+
if (!fs.existsSync(script)) return null;
|
|
325
|
+
const output = execFileSync('powershell.exe', [
|
|
326
|
+
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
|
|
327
|
+
'-File', script, '-InstallDir', installRoot
|
|
328
|
+
], powerShellOptions()).replace(/^\uFEFF/, '').trim();
|
|
329
|
+
return output ? JSON.parse(output) : null;
|
|
330
|
+
}
|
|
257
331
|
function launch() {
|
|
258
|
-
|
|
332
|
+
// stdout is the MCP protocol, so setup/progress must never be printed here.
|
|
333
|
+
// Upgrades also must not stop other running MCP sessions during a new launch.
|
|
334
|
+
const metadataFile = path.join(installRoot, 'version.json');
|
|
335
|
+
const metadata = fs.existsSync(metadataFile) ? JSON.parse(fs.readFileSync(metadataFile, 'utf8').replace(/^\uFEFF/, '')) : {};
|
|
336
|
+
if (!fs.existsSync(binaryPath()) || metadata.version !== DEFAULT_VERSION) {
|
|
337
|
+
throw new Error(`请先运行 npx -y shizai-agent-mcp@${DEFAULT_VERSION} install,再启动 MCP`);
|
|
338
|
+
}
|
|
339
|
+
verifyPackage(installRoot, DEFAULT_VERSION);
|
|
259
340
|
const child = spawn(binaryPath(), process.argv.slice(2), { stdio: 'inherit', windowsHide: true });
|
|
341
|
+
child.on('error', error => fail(error.message));
|
|
260
342
|
child.on('exit', code => { process.exitCode = code ?? 0; });
|
|
261
343
|
}
|
|
262
344
|
function main() {
|
|
@@ -268,22 +350,27 @@ function main() {
|
|
|
268
350
|
const index = args.indexOf('--version');
|
|
269
351
|
const version = index >= 0 ? args[index + 1] : args.find(value => !value.startsWith('-'));
|
|
270
352
|
if (index >= 0 && !version) throw new Error('--version 后需要版本号');
|
|
271
|
-
return install(version || DEFAULT_VERSION, {
|
|
353
|
+
return install(version || DEFAULT_VERSION, {
|
|
354
|
+
skipRegister: args.includes('--skip-register'),
|
|
355
|
+
withBrowserExtension: !args.includes('--without-browser-extension')
|
|
356
|
+
});
|
|
272
357
|
}
|
|
273
358
|
if (command === 'register') {
|
|
274
359
|
if (!fs.existsSync(binaryPath())) throw new Error('尚未安装,请先运行 install');
|
|
275
360
|
if (target === 'codex') return registerCodex();
|
|
276
361
|
if (target === 'cursor' || target === 'claude') return registerJsonClient(target);
|
|
277
|
-
|
|
362
|
+
if (target === 'browser') return registerBrowser();
|
|
363
|
+
throw new Error('请指定 codex、cursor、claude 或 browser');
|
|
278
364
|
}
|
|
279
|
-
if (command === 'status') return json(
|
|
365
|
+
if (command === 'status') return json(installationStatus());
|
|
280
366
|
if (command === 'path') return console.log(binaryPath());
|
|
281
367
|
if (command === 'uninstall') {
|
|
282
368
|
const destination = validateInstallRoot();
|
|
283
369
|
const stoppedProcesses = stopInstalledProcesses(destination);
|
|
370
|
+
const browser = unregisterBrowser();
|
|
284
371
|
removeDirectoryWithRetries(destination);
|
|
285
372
|
if (fs.existsSync(path.dirname(destination))) cleanupInstallArtifacts(path.dirname(destination));
|
|
286
|
-
return json({ uninstalled: true, directory: destination, stopped_processes: stoppedProcesses });
|
|
373
|
+
return json({ uninstalled: true, directory: destination, stopped_processes: stoppedProcesses, browser });
|
|
287
374
|
}
|
|
288
375
|
throw new Error(`未知命令:${command}`);
|
|
289
376
|
}
|