shizai-agent-mcp 0.2.2 → 0.2.4
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 +38 -38
- package/bin/shizai-agent-mcp.js +168 -168
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
# shizai-agent-mcp
|
|
2
|
-
|
|
3
|
-
实在 Agent 本地 MCP 的 npm/npx 安装器。一个 npm 包同时提供 Shizai MCP、BrowserMCP 和 Playwright MCP 注册能力,用户不需要分别安装多个 npm 包。
|
|
4
|
-
|
|
5
|
-
## 使用
|
|
6
|
-
|
|
7
|
-
```powershell
|
|
8
|
-
npx -y shizai-agent-mcp install
|
|
9
|
-
npx -y shizai-agent-mcp register codex --with-browsers
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
也可以注册 Cursor 或 Claude Desktop:
|
|
13
|
-
|
|
14
|
-
```powershell
|
|
15
|
-
npx -y shizai-agent-mcp register cursor --with-browsers
|
|
16
|
-
npx -y shizai-agent-mcp register claude --with-browsers
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
常用命令:
|
|
20
|
-
|
|
21
|
-
```powershell
|
|
22
|
-
npx -y shizai-agent-mcp status
|
|
23
|
-
npx -y shizai-agent-mcp path
|
|
24
|
-
npx -y shizai-agent-mcp uninstall
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
当前 npm launcher 版本为 `0.2.
|
|
28
|
-
`shizai-agent-v0.6.
|
|
29
|
-
|
|
30
|
-
注册默认会加入 `browsermcp` 服务,它配合 Browser MCP Chrome 扩展连接当前已登录标签页;
|
|
31
|
-
`--with-playwright` 或 `--with-browsers` 会额外加入隔离的 `playwright` 服务。
|
|
32
|
-
`--playwright-only` 可关闭默认的 BrowserMCP。
|
|
33
|
-
|
|
34
|
-
发布 Release 时需要同时上传同名 `.sha256` 文件。测试企业内网镜像时,可设置
|
|
35
|
-
`SHIZAI_AGENT_RELEASE_BASE_URL`;测试其他版本可设置 `SHIZAI_AGENT_VERSION`。
|
|
36
|
-
|
|
37
|
-
本地设计器需要正在运行且已登录。MCP 是本地 stdio 服务,Codex 显示 `Auth: Unsupported`
|
|
38
|
-
属于正常状态,实在 Agent 的登录会话由桌面设计器提供。
|
|
1
|
+
# shizai-agent-mcp
|
|
2
|
+
|
|
3
|
+
实在 Agent 本地 MCP 的 npm/npx 安装器。一个 npm 包同时提供 Shizai MCP、BrowserMCP 和 Playwright MCP 注册能力,用户不需要分别安装多个 npm 包。
|
|
4
|
+
|
|
5
|
+
## 使用
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
npx -y shizai-agent-mcp install
|
|
9
|
+
npx -y shizai-agent-mcp register codex --with-browsers
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
也可以注册 Cursor 或 Claude Desktop:
|
|
13
|
+
|
|
14
|
+
```powershell
|
|
15
|
+
npx -y shizai-agent-mcp register cursor --with-browsers
|
|
16
|
+
npx -y shizai-agent-mcp register claude --with-browsers
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
常用命令:
|
|
20
|
+
|
|
21
|
+
```powershell
|
|
22
|
+
npx -y shizai-agent-mcp status
|
|
23
|
+
npx -y shizai-agent-mcp path
|
|
24
|
+
npx -y shizai-agent-mcp uninstall
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
当前 npm launcher 版本为 `0.2.4`,默认下载最新的 `v0.6.4` Windows x64 资产:
|
|
28
|
+
`shizai-agent-v0.6.4-windows-x64.zip`。`@playwright/mcp` 会作为 npm 依赖自动安装。
|
|
29
|
+
|
|
30
|
+
注册默认会加入 `browsermcp` 服务,它配合 Browser MCP Chrome 扩展连接当前已登录标签页;
|
|
31
|
+
`--with-playwright` 或 `--with-browsers` 会额外加入隔离的 `playwright` 服务。
|
|
32
|
+
`--playwright-only` 可关闭默认的 BrowserMCP。
|
|
33
|
+
|
|
34
|
+
发布 Release 时需要同时上传同名 `.sha256` 文件。测试企业内网镜像时,可设置
|
|
35
|
+
`SHIZAI_AGENT_RELEASE_BASE_URL`;测试其他版本可设置 `SHIZAI_AGENT_VERSION`。
|
|
36
|
+
|
|
37
|
+
本地设计器需要正在运行且已登录。MCP 是本地 stdio 服务,Codex 显示 `Auth: Unsupported`
|
|
38
|
+
属于正常状态,实在 Agent 的登录会话由桌面设计器提供。
|
package/bin/shizai-agent-mcp.js
CHANGED
|
@@ -1,168 +1,168 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/* Lightweight distribution layer. The MCP binary remains in GitHub Releases. */
|
|
4
|
-
const fs = require('node:fs');
|
|
5
|
-
const path = require('node:path');
|
|
6
|
-
const os = require('node:os');
|
|
7
|
-
const crypto = require('node:crypto');
|
|
8
|
-
const { execFileSync, spawn } = require('node:child_process');
|
|
9
|
-
|
|
10
|
-
const SERVER = 'shizai-agent';
|
|
11
|
-
const REPO = process.env.SHIZAI_AGENT_REPO || 'yimoxzy/shizai-agent';
|
|
12
|
-
const DEFAULT_VERSION = process.env.SHIZAI_AGENT_VERSION || '0.6.
|
|
13
|
-
const BROWSERMCP_VERSION = process.env.SHIZAI_BROWSERMCP_VERSION || '0.1.3';
|
|
14
|
-
const PLAYWRIGHT_VERSION = process.env.SHIZAI_PLAYWRIGHT_VERSION || '0.0.79';
|
|
15
|
-
const installRoot = process.env.SHIZAI_AGENT_HOME || path.join(
|
|
16
|
-
process.env.LOCALAPPDATA || path.join(os.homedir(), '.local'), 'Programs', 'ShizaiAgent'
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
function fail(message) { console.error(`shizai-agent-mcp: ${message}`); process.exitCode = 1; }
|
|
20
|
-
function json(value) { console.log(JSON.stringify(value, null, 2)); }
|
|
21
|
-
function help() {
|
|
22
|
-
console.log(`Shizai Agent MCP installer\n\nUsage:\n shizai-agent-mcp install [--version VERSION]\n shizai-agent-mcp register <codex|cursor|claude> [--with-browsermcp]\n shizai-agent-mcp register <codex|cursor|claude> [--with-playwright]\n shizai-agent-mcp register <codex|cursor|claude> --with-browsers\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 SHIZAI_BROWSERMCP_VERSION BrowserMCP version (default: ${BROWSERMCP_VERSION})\n SHIZAI_PLAYWRIGHT_VERSION Bundled Playwright MCP version (default: ${PLAYWRIGHT_VERSION})\n`);
|
|
23
|
-
}
|
|
24
|
-
function binaryPath() { return path.join(installRoot, 'shizai-agent-mcp.exe'); }
|
|
25
|
-
function psQuote(value) { return `'${String(value).replaceAll("'", "''")}'`; }
|
|
26
|
-
function runPowerShell(script, args = []) {
|
|
27
|
-
return execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script, ...args], { encoding: 'utf8' });
|
|
28
|
-
}
|
|
29
|
-
function assetName(version) {
|
|
30
|
-
if (process.platform !== 'win32' || process.arch !== 'x64') {
|
|
31
|
-
throw new Error(`当前 npm 安装器暂时只提供 Windows x64,检测到 ${process.platform}/${process.arch}`);
|
|
32
|
-
}
|
|
33
|
-
return process.env.SHIZAI_AGENT_ASSET_NAME || `shizai-agent-v${version}-windows-x64.zip`;
|
|
34
|
-
}
|
|
35
|
-
function releaseUrl(version, asset) {
|
|
36
|
-
const base = process.env.SHIZAI_AGENT_RELEASE_BASE_URL || `https://github.com/${REPO}/releases/download/v${version}`;
|
|
37
|
-
return `${base.replace(/\/$/, '')}/${asset}`;
|
|
38
|
-
}
|
|
39
|
-
function verifySha256(file, expected) {
|
|
40
|
-
const actual = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex').toLowerCase();
|
|
41
|
-
if (actual !== expected.toLowerCase()) throw new Error(`SHA256 校验失败:期望 ${expected},实际 ${actual}`);
|
|
42
|
-
}
|
|
43
|
-
function download(url, destination) {
|
|
44
|
-
const script = `Invoke-WebRequest -UseBasicParsing -Uri ${psQuote(url)} -OutFile ${psQuote(destination)}`;
|
|
45
|
-
runPowerShell(script);
|
|
46
|
-
}
|
|
47
|
-
function install(version) {
|
|
48
|
-
const asset = assetName(version);
|
|
49
|
-
const url = releaseUrl(version, asset);
|
|
50
|
-
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'shizai-agent-'));
|
|
51
|
-
const archive = path.join(temp, asset);
|
|
52
|
-
const checksum = path.join(temp, `${asset}.sha256`);
|
|
53
|
-
try {
|
|
54
|
-
console.log(`下载 ${url}`);
|
|
55
|
-
download(url, archive);
|
|
56
|
-
try {
|
|
57
|
-
download(`${url}.sha256`, checksum);
|
|
58
|
-
const text = fs.readFileSync(checksum, 'utf8');
|
|
59
|
-
const expected = (text.match(/[a-f0-9]{64}/i) || [])[0];
|
|
60
|
-
if (expected) verifySha256(archive, expected);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
if (process.env.SHIZAI_AGENT_REQUIRE_CHECKSUM === '1') throw error;
|
|
63
|
-
console.warn('未找到校验文件,继续解压;发布正式版本时建议上传 .sha256。');
|
|
64
|
-
}
|
|
65
|
-
fs.mkdirSync(installRoot, { recursive: true });
|
|
66
|
-
runPowerShell(`if (Test-Path -LiteralPath ${psQuote(installRoot)}) { Remove-Item -LiteralPath ${psQuote(installRoot)} -Recurse -Force }; New-Item -ItemType Directory -Force -Path ${psQuote(installRoot)} | Out-Null; Expand-Archive -LiteralPath ${psQuote(archive)} -DestinationPath ${psQuote(installRoot)} -Force; $nested = Get-ChildItem -LiteralPath ${psQuote(installRoot)} -Directory | Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName 'shizai-agent-mcp.exe') } | Select-Object -First 1; if ($nested) { Get-ChildItem -LiteralPath $nested.FullName -Force | Move-Item -Destination ${psQuote(installRoot)} -Force }`);
|
|
67
|
-
if (!fs.existsSync(binaryPath())) throw new Error(`安装包中未找到 ${binaryPath()}`);
|
|
68
|
-
fs.writeFileSync(path.join(installRoot, 'version.json'), JSON.stringify({ version, asset, installedAt: new Date().toISOString() }, null, 2));
|
|
69
|
-
json({ installed: true, version, directory: installRoot, command: binaryPath() });
|
|
70
|
-
} finally {
|
|
71
|
-
fs.rmSync(temp, { recursive: true, force: true });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function replaceTomlSection(content, section, replacement) {
|
|
75
|
-
const escaped = section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
76
|
-
const pattern = new RegExp(`^\\[${escaped}\\]\\r?\\n[\\s\\S]*?(?=^\\[|$)`, 'm');
|
|
77
|
-
const next = content.replace(pattern, '').trimEnd();
|
|
78
|
-
return `${next}${next ? '\r\n\r\n' : ''}${replacement.trim()}\r\n`;
|
|
79
|
-
}
|
|
80
|
-
function playwrightSpec() {
|
|
81
|
-
try {
|
|
82
|
-
const packageJSON = require.resolve('@playwright/mcp/package.json');
|
|
83
|
-
const packageRoot = path.dirname(packageJSON);
|
|
84
|
-
return { command: process.execPath, args: [path.join(packageRoot, 'cli.js')] };
|
|
85
|
-
} catch (error) {
|
|
86
|
-
return { command: 'npx', args: ['-y', `@playwright/mcp@${PLAYWRIGHT_VERSION}`] };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function browserMcpSpec() {
|
|
90
|
-
try {
|
|
91
|
-
const packageJSON = require.resolve('@browsermcp/mcp/package.json');
|
|
92
|
-
const packageRoot = path.dirname(packageJSON);
|
|
93
|
-
return { command: process.execPath, args: [path.join(packageRoot, 'dist', 'index.js')] };
|
|
94
|
-
} catch (error) {
|
|
95
|
-
return { command: 'npx', args: ['-y', `@browsermcp/mcp@${BROWSERMCP_VERSION}`] };
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function tomlString(value) { return JSON.stringify(String(value)); }
|
|
99
|
-
function tomlArray(values) { return `[${values.map(tomlString).join(', ')}]`; }
|
|
100
|
-
function registerCodex(withPlaywright = false, withBrowserMcp = true) {
|
|
101
|
-
const config = process.env.SHIZAI_CODEX_CONFIG || path.join(os.homedir(), '.codex', 'config.toml');
|
|
102
|
-
fs.mkdirSync(path.dirname(config), { recursive: true });
|
|
103
|
-
const old = fs.existsSync(config) ? fs.readFileSync(config, 'utf8') : '';
|
|
104
|
-
if (old) fs.copyFileSync(config, `${config}.shizai-agent.bak`);
|
|
105
|
-
const section = `[mcp_servers.${SERVER}]\ncommand = ${psQuote(binaryPath())}\nargs = []\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
|
|
106
|
-
let next = replaceTomlSection(old, `mcp_servers.${SERVER}`, section);
|
|
107
|
-
if (withPlaywright) {
|
|
108
|
-
const spec = playwrightSpec();
|
|
109
|
-
const browserSection = `[mcp_servers.playwright]\ncommand = ${tomlString(spec.command)}\nargs = ${tomlArray(spec.args)}\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
|
|
110
|
-
next = replaceTomlSection(next, 'mcp_servers.playwright', browserSection);
|
|
111
|
-
}
|
|
112
|
-
if (withBrowserMcp) {
|
|
113
|
-
const spec = browserMcpSpec();
|
|
114
|
-
const browserMcpSection = `[mcp_servers.browsermcp]\ncommand = ${tomlString(spec.command)}\nargs = ${tomlArray(spec.args)}\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
|
|
115
|
-
next = replaceTomlSection(next, 'mcp_servers.browsermcp', browserMcpSection);
|
|
116
|
-
}
|
|
117
|
-
fs.writeFileSync(config, next, 'utf8');
|
|
118
|
-
json({ registered: true, client: 'codex', config, command: binaryPath(), browsermcp: withBrowserMcp ? browserMcpSpec() : null, playwright: withPlaywright ? playwrightSpec() : null });
|
|
119
|
-
}
|
|
120
|
-
function registerJsonClient(client, withPlaywright = false, withBrowserMcp = true) {
|
|
121
|
-
const locations = {
|
|
122
|
-
cursor: process.env.SHIZAI_CURSOR_CONFIG || path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Cursor', 'User', 'globalStorage', 'mcp.json'),
|
|
123
|
-
claude: process.env.SHIZAI_CLAUDE_CONFIG || path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json')
|
|
124
|
-
};
|
|
125
|
-
const file = locations[client];
|
|
126
|
-
if (!file) throw new Error(`未知客户端:${client}`);
|
|
127
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
128
|
-
const data = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : {};
|
|
129
|
-
data.mcpServers = data.mcpServers || {};
|
|
130
|
-
data.mcpServers[SERVER] = { command: binaryPath(), args: [] };
|
|
131
|
-
if (withPlaywright) {
|
|
132
|
-
const spec = playwrightSpec();
|
|
133
|
-
data.mcpServers.playwright = { command: spec.command, args: spec.args };
|
|
134
|
-
}
|
|
135
|
-
if (withBrowserMcp) {
|
|
136
|
-
const spec = browserMcpSpec();
|
|
137
|
-
data.mcpServers.browsermcp = { command: spec.command, args: spec.args };
|
|
138
|
-
}
|
|
139
|
-
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
140
|
-
json({ registered: true, client, config: file, command: binaryPath(), browsermcp: withBrowserMcp ? browserMcpSpec() : null, playwright: withPlaywright ? playwrightSpec() : null });
|
|
141
|
-
}
|
|
142
|
-
function launch() {
|
|
143
|
-
if (!fs.existsSync(binaryPath())) install(DEFAULT_VERSION);
|
|
144
|
-
const child = spawn(binaryPath(), process.argv.slice(2), { stdio: 'inherit', windowsHide: true });
|
|
145
|
-
child.on('exit', code => { process.exitCode = code ?? 0; });
|
|
146
|
-
}
|
|
147
|
-
function main() {
|
|
148
|
-
const [command, target, ...rest] = process.argv.slice(2);
|
|
149
|
-
if (!command) return launch();
|
|
150
|
-
if (command === '--help' || command === '-h') return help();
|
|
151
|
-
if (command === 'install') {
|
|
152
|
-
const index = rest.indexOf('--version');
|
|
153
|
-
return install(index >= 0 ? rest[index + 1] : (target && !target.startsWith('-') ? target : DEFAULT_VERSION));
|
|
154
|
-
}
|
|
155
|
-
if (command === 'register') {
|
|
156
|
-
if (!fs.existsSync(binaryPath())) throw new Error('尚未安装,请先运行 install');
|
|
157
|
-
const withPlaywright = rest.includes('--with-playwright') || rest.includes('--with-browsers');
|
|
158
|
-
const withBrowserMcp = !rest.includes('--playwright-only');
|
|
159
|
-
if (target === 'codex') return registerCodex(withPlaywright, withBrowserMcp);
|
|
160
|
-
if (target === 'cursor' || target === 'claude') return registerJsonClient(target, withPlaywright, withBrowserMcp);
|
|
161
|
-
throw new Error('请指定 codex、cursor 或 claude');
|
|
162
|
-
}
|
|
163
|
-
if (command === 'status') return json({ installed: fs.existsSync(binaryPath()), directory: installRoot, command: binaryPath(), versionFile: path.join(installRoot, 'version.json'), browsermcp: browserMcpSpec(), playwright: playwrightSpec() });
|
|
164
|
-
if (command === 'path') return console.log(binaryPath());
|
|
165
|
-
if (command === 'uninstall') { if (fs.existsSync(installRoot)) fs.rmSync(installRoot, { recursive: true, force: true }); return json({ uninstalled: true, directory: installRoot }); }
|
|
166
|
-
throw new Error(`未知命令:${command}`);
|
|
167
|
-
}
|
|
168
|
-
try { main(); } catch (error) { fail(error.message); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/* Lightweight distribution layer. The MCP binary remains in GitHub Releases. */
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const crypto = require('node:crypto');
|
|
8
|
+
const { execFileSync, spawn } = require('node:child_process');
|
|
9
|
+
|
|
10
|
+
const SERVER = 'shizai-agent';
|
|
11
|
+
const REPO = process.env.SHIZAI_AGENT_REPO || 'yimoxzy/shizai-agent';
|
|
12
|
+
const DEFAULT_VERSION = process.env.SHIZAI_AGENT_VERSION || '0.6.4';
|
|
13
|
+
const BROWSERMCP_VERSION = process.env.SHIZAI_BROWSERMCP_VERSION || '0.1.3';
|
|
14
|
+
const PLAYWRIGHT_VERSION = process.env.SHIZAI_PLAYWRIGHT_VERSION || '0.0.79';
|
|
15
|
+
const installRoot = process.env.SHIZAI_AGENT_HOME || path.join(
|
|
16
|
+
process.env.LOCALAPPDATA || path.join(os.homedir(), '.local'), 'Programs', 'ShizaiAgent'
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
function fail(message) { console.error(`shizai-agent-mcp: ${message}`); process.exitCode = 1; }
|
|
20
|
+
function json(value) { console.log(JSON.stringify(value, null, 2)); }
|
|
21
|
+
function help() {
|
|
22
|
+
console.log(`Shizai Agent MCP installer\n\nUsage:\n shizai-agent-mcp install [--version VERSION]\n shizai-agent-mcp register <codex|cursor|claude> [--with-browsermcp]\n shizai-agent-mcp register <codex|cursor|claude> [--with-playwright]\n shizai-agent-mcp register <codex|cursor|claude> --with-browsers\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 SHIZAI_BROWSERMCP_VERSION BrowserMCP version (default: ${BROWSERMCP_VERSION})\n SHIZAI_PLAYWRIGHT_VERSION Bundled Playwright MCP version (default: ${PLAYWRIGHT_VERSION})\n`);
|
|
23
|
+
}
|
|
24
|
+
function binaryPath() { return path.join(installRoot, 'shizai-agent-mcp.exe'); }
|
|
25
|
+
function psQuote(value) { return `'${String(value).replaceAll("'", "''")}'`; }
|
|
26
|
+
function runPowerShell(script, args = []) {
|
|
27
|
+
return execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script, ...args], { encoding: 'utf8' });
|
|
28
|
+
}
|
|
29
|
+
function assetName(version) {
|
|
30
|
+
if (process.platform !== 'win32' || process.arch !== 'x64') {
|
|
31
|
+
throw new Error(`当前 npm 安装器暂时只提供 Windows x64,检测到 ${process.platform}/${process.arch}`);
|
|
32
|
+
}
|
|
33
|
+
return process.env.SHIZAI_AGENT_ASSET_NAME || `shizai-agent-v${version}-windows-x64.zip`;
|
|
34
|
+
}
|
|
35
|
+
function releaseUrl(version, asset) {
|
|
36
|
+
const base = process.env.SHIZAI_AGENT_RELEASE_BASE_URL || `https://github.com/${REPO}/releases/download/v${version}`;
|
|
37
|
+
return `${base.replace(/\/$/, '')}/${asset}`;
|
|
38
|
+
}
|
|
39
|
+
function verifySha256(file, expected) {
|
|
40
|
+
const actual = crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex').toLowerCase();
|
|
41
|
+
if (actual !== expected.toLowerCase()) throw new Error(`SHA256 校验失败:期望 ${expected},实际 ${actual}`);
|
|
42
|
+
}
|
|
43
|
+
function download(url, destination) {
|
|
44
|
+
const script = `Invoke-WebRequest -UseBasicParsing -Uri ${psQuote(url)} -OutFile ${psQuote(destination)}`;
|
|
45
|
+
runPowerShell(script);
|
|
46
|
+
}
|
|
47
|
+
function install(version) {
|
|
48
|
+
const asset = assetName(version);
|
|
49
|
+
const url = releaseUrl(version, asset);
|
|
50
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'shizai-agent-'));
|
|
51
|
+
const archive = path.join(temp, asset);
|
|
52
|
+
const checksum = path.join(temp, `${asset}.sha256`);
|
|
53
|
+
try {
|
|
54
|
+
console.log(`下载 ${url}`);
|
|
55
|
+
download(url, archive);
|
|
56
|
+
try {
|
|
57
|
+
download(`${url}.sha256`, checksum);
|
|
58
|
+
const text = fs.readFileSync(checksum, 'utf8');
|
|
59
|
+
const expected = (text.match(/[a-f0-9]{64}/i) || [])[0];
|
|
60
|
+
if (expected) verifySha256(archive, expected);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (process.env.SHIZAI_AGENT_REQUIRE_CHECKSUM === '1') throw error;
|
|
63
|
+
console.warn('未找到校验文件,继续解压;发布正式版本时建议上传 .sha256。');
|
|
64
|
+
}
|
|
65
|
+
fs.mkdirSync(installRoot, { recursive: true });
|
|
66
|
+
runPowerShell(`if (Test-Path -LiteralPath ${psQuote(installRoot)}) { Remove-Item -LiteralPath ${psQuote(installRoot)} -Recurse -Force }; New-Item -ItemType Directory -Force -Path ${psQuote(installRoot)} | Out-Null; Expand-Archive -LiteralPath ${psQuote(archive)} -DestinationPath ${psQuote(installRoot)} -Force; $nested = Get-ChildItem -LiteralPath ${psQuote(installRoot)} -Directory | Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName 'shizai-agent-mcp.exe') } | Select-Object -First 1; if ($nested) { Get-ChildItem -LiteralPath $nested.FullName -Force | Move-Item -Destination ${psQuote(installRoot)} -Force }`);
|
|
67
|
+
if (!fs.existsSync(binaryPath())) throw new Error(`安装包中未找到 ${binaryPath()}`);
|
|
68
|
+
fs.writeFileSync(path.join(installRoot, 'version.json'), JSON.stringify({ version, asset, installedAt: new Date().toISOString() }, null, 2));
|
|
69
|
+
json({ installed: true, version, directory: installRoot, command: binaryPath() });
|
|
70
|
+
} finally {
|
|
71
|
+
fs.rmSync(temp, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function replaceTomlSection(content, section, replacement) {
|
|
75
|
+
const escaped = section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
76
|
+
const pattern = new RegExp(`^\\[${escaped}\\]\\r?\\n[\\s\\S]*?(?=^\\[|$)`, 'm');
|
|
77
|
+
const next = content.replace(pattern, '').trimEnd();
|
|
78
|
+
return `${next}${next ? '\r\n\r\n' : ''}${replacement.trim()}\r\n`;
|
|
79
|
+
}
|
|
80
|
+
function playwrightSpec() {
|
|
81
|
+
try {
|
|
82
|
+
const packageJSON = require.resolve('@playwright/mcp/package.json');
|
|
83
|
+
const packageRoot = path.dirname(packageJSON);
|
|
84
|
+
return { command: process.execPath, args: [path.join(packageRoot, 'cli.js')] };
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return { command: 'npx', args: ['-y', `@playwright/mcp@${PLAYWRIGHT_VERSION}`] };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function browserMcpSpec() {
|
|
90
|
+
try {
|
|
91
|
+
const packageJSON = require.resolve('@browsermcp/mcp/package.json');
|
|
92
|
+
const packageRoot = path.dirname(packageJSON);
|
|
93
|
+
return { command: process.execPath, args: [path.join(packageRoot, 'dist', 'index.js')] };
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return { command: 'npx', args: ['-y', `@browsermcp/mcp@${BROWSERMCP_VERSION}`] };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function tomlString(value) { return JSON.stringify(String(value)); }
|
|
99
|
+
function tomlArray(values) { return `[${values.map(tomlString).join(', ')}]`; }
|
|
100
|
+
function registerCodex(withPlaywright = false, withBrowserMcp = true) {
|
|
101
|
+
const config = process.env.SHIZAI_CODEX_CONFIG || path.join(os.homedir(), '.codex', 'config.toml');
|
|
102
|
+
fs.mkdirSync(path.dirname(config), { recursive: true });
|
|
103
|
+
const old = fs.existsSync(config) ? fs.readFileSync(config, 'utf8') : '';
|
|
104
|
+
if (old) fs.copyFileSync(config, `${config}.shizai-agent.bak`);
|
|
105
|
+
const section = `[mcp_servers.${SERVER}]\ncommand = ${psQuote(binaryPath())}\nargs = []\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
|
|
106
|
+
let next = replaceTomlSection(old, `mcp_servers.${SERVER}`, section);
|
|
107
|
+
if (withPlaywright) {
|
|
108
|
+
const spec = playwrightSpec();
|
|
109
|
+
const browserSection = `[mcp_servers.playwright]\ncommand = ${tomlString(spec.command)}\nargs = ${tomlArray(spec.args)}\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
|
|
110
|
+
next = replaceTomlSection(next, 'mcp_servers.playwright', browserSection);
|
|
111
|
+
}
|
|
112
|
+
if (withBrowserMcp) {
|
|
113
|
+
const spec = browserMcpSpec();
|
|
114
|
+
const browserMcpSection = `[mcp_servers.browsermcp]\ncommand = ${tomlString(spec.command)}\nargs = ${tomlArray(spec.args)}\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
|
|
115
|
+
next = replaceTomlSection(next, 'mcp_servers.browsermcp', browserMcpSection);
|
|
116
|
+
}
|
|
117
|
+
fs.writeFileSync(config, next, 'utf8');
|
|
118
|
+
json({ registered: true, client: 'codex', config, command: binaryPath(), browsermcp: withBrowserMcp ? browserMcpSpec() : null, playwright: withPlaywright ? playwrightSpec() : null });
|
|
119
|
+
}
|
|
120
|
+
function registerJsonClient(client, withPlaywright = false, withBrowserMcp = true) {
|
|
121
|
+
const locations = {
|
|
122
|
+
cursor: process.env.SHIZAI_CURSOR_CONFIG || path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Cursor', 'User', 'globalStorage', 'mcp.json'),
|
|
123
|
+
claude: process.env.SHIZAI_CLAUDE_CONFIG || path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json')
|
|
124
|
+
};
|
|
125
|
+
const file = locations[client];
|
|
126
|
+
if (!file) throw new Error(`未知客户端:${client}`);
|
|
127
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
128
|
+
const data = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : {};
|
|
129
|
+
data.mcpServers = data.mcpServers || {};
|
|
130
|
+
data.mcpServers[SERVER] = { command: binaryPath(), args: [] };
|
|
131
|
+
if (withPlaywright) {
|
|
132
|
+
const spec = playwrightSpec();
|
|
133
|
+
data.mcpServers.playwright = { command: spec.command, args: spec.args };
|
|
134
|
+
}
|
|
135
|
+
if (withBrowserMcp) {
|
|
136
|
+
const spec = browserMcpSpec();
|
|
137
|
+
data.mcpServers.browsermcp = { command: spec.command, args: spec.args };
|
|
138
|
+
}
|
|
139
|
+
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
140
|
+
json({ registered: true, client, config: file, command: binaryPath(), browsermcp: withBrowserMcp ? browserMcpSpec() : null, playwright: withPlaywright ? playwrightSpec() : null });
|
|
141
|
+
}
|
|
142
|
+
function launch() {
|
|
143
|
+
if (!fs.existsSync(binaryPath())) install(DEFAULT_VERSION);
|
|
144
|
+
const child = spawn(binaryPath(), process.argv.slice(2), { stdio: 'inherit', windowsHide: true });
|
|
145
|
+
child.on('exit', code => { process.exitCode = code ?? 0; });
|
|
146
|
+
}
|
|
147
|
+
function main() {
|
|
148
|
+
const [command, target, ...rest] = process.argv.slice(2);
|
|
149
|
+
if (!command) return launch();
|
|
150
|
+
if (command === '--help' || command === '-h') return help();
|
|
151
|
+
if (command === 'install') {
|
|
152
|
+
const index = rest.indexOf('--version');
|
|
153
|
+
return install(index >= 0 ? rest[index + 1] : (target && !target.startsWith('-') ? target : DEFAULT_VERSION));
|
|
154
|
+
}
|
|
155
|
+
if (command === 'register') {
|
|
156
|
+
if (!fs.existsSync(binaryPath())) throw new Error('尚未安装,请先运行 install');
|
|
157
|
+
const withPlaywright = rest.includes('--with-playwright') || rest.includes('--with-browsers');
|
|
158
|
+
const withBrowserMcp = !rest.includes('--playwright-only');
|
|
159
|
+
if (target === 'codex') return registerCodex(withPlaywright, withBrowserMcp);
|
|
160
|
+
if (target === 'cursor' || target === 'claude') return registerJsonClient(target, withPlaywright, withBrowserMcp);
|
|
161
|
+
throw new Error('请指定 codex、cursor 或 claude');
|
|
162
|
+
}
|
|
163
|
+
if (command === 'status') return json({ installed: fs.existsSync(binaryPath()), directory: installRoot, command: binaryPath(), versionFile: path.join(installRoot, 'version.json'), browsermcp: browserMcpSpec(), playwright: playwrightSpec() });
|
|
164
|
+
if (command === 'path') return console.log(binaryPath());
|
|
165
|
+
if (command === 'uninstall') { if (fs.existsSync(installRoot)) fs.rmSync(installRoot, { recursive: true, force: true }); return json({ uninstalled: true, directory: installRoot }); }
|
|
166
|
+
throw new Error(`未知命令:${command}`);
|
|
167
|
+
}
|
|
168
|
+
try { main(); } catch (error) { fail(error.message); }
|