@opentiny/webmcp-cli 0.0.1-alpha.2 → 0.0.1-alpha.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/dist/bin.cjs +331 -279
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +331 -285
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +127 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +127 -173
- package/dist/index.js.map +1 -1
- package/dist/inject-bundle.js +3 -3
- package/dist/webmcp-tools/juejin.cn.js +72 -0
- package/package.json +3 -2
package/dist/bin.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
2
|
|
|
9
3
|
// src/bin.ts
|
|
10
4
|
import { Command } from "commander";
|
|
11
5
|
import pc2 from "picocolors";
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
import { resolve } from "path";
|
|
12
8
|
|
|
13
9
|
// src/browser.ts
|
|
14
10
|
import puppeteer from "puppeteer-core";
|
|
@@ -16,20 +12,34 @@ import pc from "picocolors";
|
|
|
16
12
|
import fs from "fs";
|
|
17
13
|
import os from "os";
|
|
18
14
|
import path from "path";
|
|
15
|
+
import http from "http";
|
|
19
16
|
import { spawn } from "child_process";
|
|
20
17
|
import { fileURLToPath } from "url";
|
|
21
18
|
var __filename = fileURLToPath(import.meta.url);
|
|
22
19
|
var __dirname = path.dirname(__filename);
|
|
23
20
|
var CDP_PORT = 9222;
|
|
24
21
|
var CDP_URL = `http://localhost:${CDP_PORT}`;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
function httpGet(url, timeoutMs = 1500) {
|
|
23
|
+
return new Promise((resolve2, reject) => {
|
|
24
|
+
const req = http.get(url, { timeout: timeoutMs }, (res) => {
|
|
25
|
+
let data = "";
|
|
26
|
+
res.on("data", (chunk) => {
|
|
27
|
+
data += chunk.toString();
|
|
28
|
+
});
|
|
29
|
+
res.on("end", () => {
|
|
30
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
31
|
+
resolve2(data);
|
|
32
|
+
} else {
|
|
33
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
req.on("error", reject);
|
|
38
|
+
req.on("timeout", () => {
|
|
39
|
+
req.destroy();
|
|
40
|
+
reject(new Error("request timeout"));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
33
43
|
}
|
|
34
44
|
function promiseWithTimeout(promise, timeoutMs = 5e3, errorMsg = "Operation timed out") {
|
|
35
45
|
let timeoutId;
|
|
@@ -45,8 +55,8 @@ function promiseWithTimeout(promise, timeoutMs = 5e3, errorMsg = "Operation time
|
|
|
45
55
|
async function checkCdpReady(url, retries = 3) {
|
|
46
56
|
for (let i = 0; i < retries; i++) {
|
|
47
57
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
await httpGet(url, 1500);
|
|
59
|
+
return true;
|
|
50
60
|
} catch {
|
|
51
61
|
}
|
|
52
62
|
if (i < retries - 1) {
|
|
@@ -55,70 +65,54 @@ async function checkCdpReady(url, retries = 3) {
|
|
|
55
65
|
}
|
|
56
66
|
return false;
|
|
57
67
|
}
|
|
58
|
-
|
|
59
|
-
const isResponding = await checkCdpReady(`http://127.0.0.1:${port}/json/version`, 1);
|
|
60
|
-
if (isResponding) {
|
|
61
|
-
console.log(pc.green(`connectBrowser: \u7AEF\u53E3 ${port} \u4E0A\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B\u4ECD\u5728\u6B63\u5E38\u54CD\u5E94\uFF0C\u8DF3\u8FC7\u5F3A\u6740\uFF0C\u5C1D\u8BD5\u76F4\u63A5\u63A5\u7BA1\u3002`));
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
try {
|
|
65
|
-
const platform = os.platform();
|
|
66
|
-
const { execSync } = __require("child_process");
|
|
67
|
-
if (platform === "darwin" || platform === "linux") {
|
|
68
|
-
console.log(pc.yellow(`\u6B63\u5728\u68C0\u6D4B\u5E76\u6E05\u7406\u5360\u7528 ${port} \u7AEF\u53E3\u7684\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B...`));
|
|
69
|
-
const pids = execSync(`lsof -t -i :${port}`).toString().trim();
|
|
70
|
-
if (pids) {
|
|
71
|
-
console.log(pc.yellow(`\u53D1\u73B0\u50F5\u5C38 PID: ${pids.split("\n").join(", ")}\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`));
|
|
72
|
-
execSync(`kill -9 ${pids.split("\n").join(" ")}`);
|
|
73
|
-
console.log(pc.green(`\u6210\u529F\u6E05\u7406\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B`));
|
|
74
|
-
}
|
|
75
|
-
} else if (platform === "win32") {
|
|
76
|
-
console.log(pc.yellow(`\u6B63\u5728\u68C0\u6D4B\u5E76\u6E05\u7406 Windows \u4E0A\u5360\u7528 ${port} \u7AEF\u53E3\u7684\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B...`));
|
|
77
|
-
const output = execSync(`netstat -ano | findstr :${port}`).toString().trim();
|
|
78
|
-
if (output) {
|
|
79
|
-
const lines = output.split("\n");
|
|
80
|
-
const pids = /* @__PURE__ */ new Set();
|
|
81
|
-
lines.forEach((line) => {
|
|
82
|
-
const parts = line.trim().split(/\s+/);
|
|
83
|
-
const pid = parts[parts.length - 1];
|
|
84
|
-
if (pid && /^\d+$/.test(pid) && pid !== "0") {
|
|
85
|
-
pids.add(pid);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
pids.forEach((pid) => {
|
|
89
|
-
console.log(pc.yellow(`\u53D1\u73B0 Windows \u6B8B\u7559\u50F5\u5C38 PID: ${pid}\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`));
|
|
90
|
-
execSync(`taskkill /F /PID ${pid}`);
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
} catch (e) {
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function getDefaultChromePath() {
|
|
68
|
+
function getDefaultBrowserPath() {
|
|
98
69
|
const platform = os.platform();
|
|
99
70
|
if (platform === "darwin") {
|
|
100
|
-
|
|
71
|
+
const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
72
|
+
if (fs.existsSync(chromePath)) return { path: chromePath, name: "Chrome" };
|
|
73
|
+
const edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
|
|
74
|
+
if (fs.existsSync(edgePath)) return { path: edgePath, name: "Edge" };
|
|
75
|
+
return null;
|
|
101
76
|
} else if (platform === "win32") {
|
|
102
|
-
const
|
|
77
|
+
const chromePaths = [
|
|
103
78
|
process.env.LOCALAPPDATA + "\\Google\\Chrome\\Application\\chrome.exe",
|
|
104
79
|
process.env.PROGRAMFILES + "\\Google\\Chrome\\Application\\chrome.exe",
|
|
105
80
|
process.env["PROGRAMFILES(X86)"] + "\\Google\\Chrome\\Application\\chrome.exe"
|
|
106
81
|
];
|
|
107
|
-
|
|
82
|
+
const foundChrome = chromePaths.find((p) => fs.existsSync(p));
|
|
83
|
+
if (foundChrome) return { path: foundChrome, name: "Chrome" };
|
|
84
|
+
const edgePaths = [
|
|
85
|
+
process.env.LOCALAPPDATA + "\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
86
|
+
process.env.PROGRAMFILES + "\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
87
|
+
process.env["PROGRAMFILES(X86)"] + "\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
88
|
+
"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
89
|
+
"C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe"
|
|
90
|
+
];
|
|
91
|
+
const foundEdge = edgePaths.find((p) => fs.existsSync(p));
|
|
92
|
+
if (foundEdge) return { path: foundEdge, name: "Edge" };
|
|
93
|
+
return null;
|
|
108
94
|
} else {
|
|
109
|
-
const paths = [
|
|
110
|
-
|
|
95
|
+
const paths = [
|
|
96
|
+
{ p: "/usr/bin/google-chrome", name: "Chrome" },
|
|
97
|
+
{ p: "/usr/bin/google-chrome-stable", name: "Chrome" },
|
|
98
|
+
{ p: "/usr/bin/chromium", name: "Chromium" },
|
|
99
|
+
{ p: "/usr/bin/chromium-browser", name: "Chromium" },
|
|
100
|
+
{ p: "/usr/bin/microsoft-edge", name: "Edge" },
|
|
101
|
+
{ p: "/usr/bin/microsoft-edge-stable", name: "Edge" }
|
|
102
|
+
];
|
|
103
|
+
const found = paths.find(({ p }) => fs.existsSync(p));
|
|
104
|
+
return found ? { path: found.p, name: found.name } : null;
|
|
111
105
|
}
|
|
112
106
|
}
|
|
113
|
-
async function
|
|
114
|
-
const
|
|
115
|
-
if (!
|
|
116
|
-
throw new Error("\u65E0\u6CD5\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 Chrome \u6D4F\u89C8\u5668\u7684\u9ED8\u8BA4\u5B89\u88C5\u8DEF\u5F84\u3002");
|
|
107
|
+
async function startBrowserInBackground() {
|
|
108
|
+
const browserInfo = getDefaultBrowserPath();
|
|
109
|
+
if (!browserInfo || !fs.existsSync(browserInfo.path)) {
|
|
110
|
+
throw new Error("\u65E0\u6CD5\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 Chrome \u6216 Edge \u6D4F\u89C8\u5668\u7684\u9ED8\u8BA4\u5B89\u88C5\u8DEF\u5F84\u3002");
|
|
117
111
|
}
|
|
118
|
-
console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0
|
|
112
|
+
console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
|
|
119
113
|
const userDataDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), ".webmcp_chrome_profile");
|
|
120
114
|
const child = spawn(
|
|
121
|
-
|
|
115
|
+
browserInfo.path,
|
|
122
116
|
[
|
|
123
117
|
`--remote-debugging-port=${CDP_PORT}`,
|
|
124
118
|
`--user-data-dir=${userDataDir}`,
|
|
@@ -133,22 +127,20 @@ async function startChromeInBackground() {
|
|
|
133
127
|
child.unref();
|
|
134
128
|
for (let i = 0; i < 20; i++) {
|
|
135
129
|
try {
|
|
136
|
-
const urls = [`http://
|
|
130
|
+
const urls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`];
|
|
137
131
|
for (const url of urls) {
|
|
138
132
|
try {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
133
|
+
await httpGet(url, 1e3);
|
|
134
|
+
console.log(pc.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
|
|
135
|
+
return;
|
|
144
136
|
} catch (err) {
|
|
145
137
|
}
|
|
146
138
|
}
|
|
147
139
|
} catch (e) {
|
|
148
140
|
}
|
|
149
|
-
await new Promise((
|
|
141
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
150
142
|
}
|
|
151
|
-
throw new Error(
|
|
143
|
+
throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
|
|
152
144
|
}
|
|
153
145
|
async function connectBrowser() {
|
|
154
146
|
const targetFilter = (target) => {
|
|
@@ -167,93 +159,54 @@ async function connectBrowser() {
|
|
|
167
159
|
return false;
|
|
168
160
|
}
|
|
169
161
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
162
|
+
const portAddresses = [
|
|
163
|
+
`http://127.0.0.1:${CDP_PORT}/json/version`,
|
|
164
|
+
`http://localhost:${CDP_PORT}/json/version`
|
|
165
|
+
];
|
|
166
|
+
for (const addr of portAddresses) {
|
|
167
|
+
const isReady = await checkCdpReady(addr, 2);
|
|
168
|
+
if (!isReady) continue;
|
|
169
|
+
const browserURL = addr.replace("/json/version", "");
|
|
170
|
+
try {
|
|
171
|
+
console.log(pc.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
172
|
+
const browser2 = await promiseWithTimeout(
|
|
173
|
+
puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
174
|
+
1e4,
|
|
175
|
+
`puppeteer.connect to ${browserURL} timed out`
|
|
176
|
+
);
|
|
177
|
+
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
178
|
+
return browser2;
|
|
179
|
+
} catch {
|
|
174
180
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
181
|
+
}
|
|
182
|
+
console.log(pc.yellow(`connectBrowser: \u7AEF\u53E3 ${CDP_PORT} \u65E0\u54CD\u5E94\uFF0C\u6B63\u5728\u542F\u52A8\u65B0\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B...`));
|
|
183
|
+
try {
|
|
184
|
+
await startBrowserInBackground();
|
|
185
|
+
} catch (launchError) {
|
|
186
|
+
const msg = launchError instanceof Error ? launchError.message : String(launchError);
|
|
187
|
+
console.error(pc.red(`\u65E0\u6CD5\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
188
|
+
throw new Error("Browser launch failed.");
|
|
189
|
+
}
|
|
190
|
+
for (const addr of portAddresses) {
|
|
191
|
+
const isReady = await checkCdpReady(addr, 3);
|
|
192
|
+
if (!isReady) continue;
|
|
193
|
+
const browserURL = addr.replace("/json/version", "");
|
|
188
194
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
194
|
-
const browser = await promiseWithTimeout(
|
|
195
|
-
puppeteer.connect({
|
|
196
|
-
browserURL: `http://localhost:${CDP_PORT}`,
|
|
197
|
-
defaultViewport: null,
|
|
198
|
-
targetFilter
|
|
199
|
-
}),
|
|
195
|
+
console.log(pc.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
196
|
+
const browser2 = await promiseWithTimeout(
|
|
197
|
+
puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
200
198
|
1e4,
|
|
201
|
-
|
|
199
|
+
`puppeteer.connect to ${browserURL} after launch timed out`
|
|
202
200
|
);
|
|
203
|
-
console.log(pc.green(
|
|
204
|
-
return
|
|
205
|
-
} catch
|
|
206
|
-
console.log(pc.yellow(`connectBrowser: \u8FDE\u63A5\u5931\u8D25\uFF0C\u5C06\u5C1D\u8BD5\u5524\u8D77\u6D4F\u89C8\u5668\u3002\u9519\u8BEF\u539F\u56E0: ${error2 instanceof Error ? error2.message : String(error2)}`));
|
|
207
|
-
try {
|
|
208
|
-
await killProcessOnPortIfZombie(CDP_PORT);
|
|
209
|
-
await startChromeInBackground();
|
|
210
|
-
try {
|
|
211
|
-
const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
|
|
212
|
-
if (!is127Ready) {
|
|
213
|
-
throw new Error("127.0.0.1 CDP port not responding after launch");
|
|
214
|
-
}
|
|
215
|
-
console.log(pc.yellow("connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
|
|
216
|
-
const browser = await promiseWithTimeout(
|
|
217
|
-
puppeteer.connect({
|
|
218
|
-
browserURL: `http://127.0.0.1:${CDP_PORT}`,
|
|
219
|
-
defaultViewport: null,
|
|
220
|
-
targetFilter
|
|
221
|
-
}),
|
|
222
|
-
1e4,
|
|
223
|
-
"puppeteer.connect to 127.0.0.1 after launch timed out"
|
|
224
|
-
);
|
|
225
|
-
console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
|
|
226
|
-
return browser;
|
|
227
|
-
} catch (e) {
|
|
228
|
-
const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
|
|
229
|
-
if (!isLocalhostReady) {
|
|
230
|
-
throw new Error("localhost CDP port not responding after launch");
|
|
231
|
-
}
|
|
232
|
-
console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
233
|
-
const browser = await promiseWithTimeout(
|
|
234
|
-
puppeteer.connect({
|
|
235
|
-
browserURL: `http://localhost:${CDP_PORT}`,
|
|
236
|
-
defaultViewport: null,
|
|
237
|
-
targetFilter
|
|
238
|
-
}),
|
|
239
|
-
1e4,
|
|
240
|
-
"puppeteer.connect to localhost after launch timed out"
|
|
241
|
-
);
|
|
242
|
-
console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
|
|
243
|
-
return browser;
|
|
244
|
-
}
|
|
245
|
-
} catch (launchError) {
|
|
246
|
-
const msg = launchError instanceof Error ? launchError.message : String(launchError);
|
|
247
|
-
console.error(pc.red(`\u65E0\u6CD5\u8FDE\u63A5\u6216\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
248
|
-
console.error(pc.yellow(`\u{1F4A1} \u63D0\u793A\uFF1A\u7531\u4E8E\u6211\u4EEC\u8981\u4F7F\u7528\u4F60\u65E5\u5E38\u7684\u9ED8\u8BA4\u6D4F\u89C8\u5668\uFF08\u5305\u542B\u4F60\u7684\u4E66\u7B7E and \u767B\u5F55\u6001\uFF09\uFF0C\u5982\u679C\u4F60\u7684 Chrome \u76EE\u524D\u6B63\u5904\u4E8E\u6253\u5F00\u72B6\u6001\uFF0C\u5B83\u4F1A\u62D2\u7EDD\u4F7F\u7528\u5E26\u6709\u8C03\u8BD5\u7AEF\u53E3\u7684\u65B0\u53C2\u6570\u542F\u52A8\u3002`));
|
|
249
|
-
console.error(pc.yellow(`\u{1F449} \u89E3\u51B3\u529E\u6CD5\uFF1A\u8BF7\u5148\u5B8C\u5168\u9000\u51FA\u5F53\u524D\u7684 Chrome \u6D4F\u89C8\u5668\uFF08\u5728 Mac \u4E0A\u6309 Cmd+Q\uFF09\uFF0C\u7136\u540E\u518D\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u3002`));
|
|
250
|
-
throw new Error("Browser connection failed.");
|
|
251
|
-
}
|
|
201
|
+
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
202
|
+
return browser2;
|
|
203
|
+
} catch {
|
|
252
204
|
}
|
|
253
205
|
}
|
|
206
|
+
throw new Error(`\u65E0\u6CD5\u8FDE\u63A5\u5230\u6D4F\u89C8\u5668\uFF08\u7AEF\u53E3 ${CDP_PORT}\uFF09\uFF0C\u8BF7\u68C0\u67E5 Chrome/Edge \u662F\u5426\u5DF2\u5B89\u88C5\u3002`);
|
|
254
207
|
}
|
|
255
|
-
async function getPageTargetId(
|
|
256
|
-
const session = await
|
|
208
|
+
async function getPageTargetId(page2) {
|
|
209
|
+
const session = await page2.target().createCDPSession();
|
|
257
210
|
try {
|
|
258
211
|
const { targetInfo } = await session.send("Target.getTargetInfo");
|
|
259
212
|
return targetInfo.targetId;
|
|
@@ -262,8 +215,8 @@ async function getPageTargetId(page) {
|
|
|
262
215
|
});
|
|
263
216
|
}
|
|
264
217
|
}
|
|
265
|
-
function getPageTargets(
|
|
266
|
-
return
|
|
218
|
+
function getPageTargets(browser2) {
|
|
219
|
+
return browser2.targets().filter((t) => {
|
|
267
220
|
try {
|
|
268
221
|
const type = (typeof t.type === "function" ? t.type() : t.type) || "";
|
|
269
222
|
const url = (typeof t.url === "function" ? t.url() : t.url) || "";
|
|
@@ -276,26 +229,26 @@ function getPageTargets(browser) {
|
|
|
276
229
|
function getTargetIdFromTarget(target) {
|
|
277
230
|
return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
|
|
278
231
|
}
|
|
279
|
-
function findPageTargetByTabId(
|
|
280
|
-
const pageTargets = getPageTargets(
|
|
232
|
+
function findPageTargetByTabId(browser2, tabid2) {
|
|
233
|
+
const pageTargets = getPageTargets(browser2);
|
|
281
234
|
return pageTargets.find((t) => {
|
|
282
235
|
const tid = getTargetIdFromTarget(t);
|
|
283
|
-
return tid ===
|
|
236
|
+
return tid === tabid2 || tid.includes(tabid2);
|
|
284
237
|
}) ?? null;
|
|
285
238
|
}
|
|
286
|
-
async function activateTabById(
|
|
287
|
-
const target = findPageTargetByTabId(
|
|
239
|
+
async function activateTabById(browser2, tabid2) {
|
|
240
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
288
241
|
if (!target) {
|
|
289
|
-
throw new Error(`Tab with targetId "${
|
|
242
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
290
243
|
}
|
|
291
244
|
const realTabId = getTargetIdFromTarget(target);
|
|
292
|
-
const pages = await
|
|
245
|
+
const pages = await browser2.pages();
|
|
293
246
|
let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
|
|
294
247
|
if (!sessionPage && pages.length > 0) {
|
|
295
248
|
sessionPage = pages[0];
|
|
296
249
|
}
|
|
297
250
|
if (!sessionPage) {
|
|
298
|
-
sessionPage = await
|
|
251
|
+
sessionPage = await browser2.newPage();
|
|
299
252
|
}
|
|
300
253
|
const session = await sessionPage.createCDPSession();
|
|
301
254
|
try {
|
|
@@ -305,18 +258,18 @@ async function activateTabById(browser, tabid) {
|
|
|
305
258
|
});
|
|
306
259
|
}
|
|
307
260
|
}
|
|
308
|
-
async function getTargetPage(
|
|
309
|
-
const pageTargets = getPageTargets(
|
|
261
|
+
async function getTargetPage(browser2, tabid2) {
|
|
262
|
+
const pageTargets = getPageTargets(browser2);
|
|
310
263
|
if (pageTargets.length === 0) {
|
|
311
|
-
const newPage = await
|
|
264
|
+
const newPage = await browser2.newPage();
|
|
312
265
|
await injectWebMCPPolyfillAndTools(newPage);
|
|
313
266
|
return newPage;
|
|
314
267
|
}
|
|
315
268
|
let targetPage = null;
|
|
316
|
-
if (
|
|
317
|
-
const target = findPageTargetByTabId(
|
|
269
|
+
if (tabid2 !== void 0) {
|
|
270
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
318
271
|
if (!target) {
|
|
319
|
-
throw new Error(`Tab with targetId "${
|
|
272
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
320
273
|
}
|
|
321
274
|
targetPage = await target.page();
|
|
322
275
|
} else {
|
|
@@ -328,14 +281,12 @@ async function getTargetPage(browser, tabid) {
|
|
|
328
281
|
let activeTargetId = null;
|
|
329
282
|
for (const url of urls) {
|
|
330
283
|
try {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
284
|
+
const body = await httpGet(url, 1e3);
|
|
285
|
+
const targetsData = JSON.parse(body);
|
|
286
|
+
const active = targetsData.find((t) => t.type === "page" && !t.url.startsWith("devtools://"));
|
|
287
|
+
if (active) {
|
|
288
|
+
activeTargetId = active.id;
|
|
289
|
+
break;
|
|
339
290
|
}
|
|
340
291
|
} catch {
|
|
341
292
|
}
|
|
@@ -361,11 +312,11 @@ async function getTargetPage(browser, tabid) {
|
|
|
361
312
|
await injectWebMCPPolyfillAndTools(targetPage);
|
|
362
313
|
return targetPage;
|
|
363
314
|
}
|
|
364
|
-
async function injectIntoPage(
|
|
365
|
-
await injectWebMCPPolyfillAndTools(
|
|
315
|
+
async function injectIntoPage(page2) {
|
|
316
|
+
await injectWebMCPPolyfillAndTools(page2, true);
|
|
366
317
|
}
|
|
367
|
-
async function injectWebMCPPolyfillAndTools(
|
|
368
|
-
const polyfillReady = !force && await
|
|
318
|
+
async function injectWebMCPPolyfillAndTools(page2, force = false) {
|
|
319
|
+
const polyfillReady = !force && await page2.evaluate(() => {
|
|
369
320
|
return !!window.__webmcpcli_init;
|
|
370
321
|
}).catch(() => false);
|
|
371
322
|
if (!polyfillReady) {
|
|
@@ -376,19 +327,19 @@ async function injectWebMCPPolyfillAndTools(page, force = false) {
|
|
|
376
327
|
}
|
|
377
328
|
const scriptContent = fs.readFileSync(injectScriptPath, "utf-8");
|
|
378
329
|
try {
|
|
379
|
-
await
|
|
330
|
+
await page2.evaluate(scriptContent);
|
|
380
331
|
} catch (err) {
|
|
381
332
|
const msg = err instanceof Error ? err.message : String(err);
|
|
382
333
|
throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
|
|
383
334
|
}
|
|
384
|
-
await new Promise((
|
|
335
|
+
await new Promise((resolve2) => setTimeout(resolve2, 300));
|
|
385
336
|
}
|
|
386
|
-
await injectDomainTools(
|
|
337
|
+
await injectDomainTools(page2);
|
|
387
338
|
}
|
|
388
|
-
async function injectDomainTools(
|
|
339
|
+
async function injectDomainTools(page2) {
|
|
389
340
|
let hostname;
|
|
390
341
|
try {
|
|
391
|
-
const url = new URL(
|
|
342
|
+
const url = new URL(page2.url());
|
|
392
343
|
hostname = url.hostname;
|
|
393
344
|
} catch {
|
|
394
345
|
return;
|
|
@@ -400,7 +351,7 @@ async function injectDomainTools(page) {
|
|
|
400
351
|
console.log(pc.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
|
|
401
352
|
const toolScript = fs.readFileSync(toolBundlePath, "utf-8");
|
|
402
353
|
try {
|
|
403
|
-
await
|
|
354
|
+
await page2.evaluate(toolScript);
|
|
404
355
|
console.log(pc.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
|
|
405
356
|
} catch (err) {
|
|
406
357
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -409,11 +360,12 @@ async function injectDomainTools(page) {
|
|
|
409
360
|
}
|
|
410
361
|
|
|
411
362
|
// src/commands/state.ts
|
|
412
|
-
async function stateCommand({ tabid }) {
|
|
413
|
-
const
|
|
363
|
+
async function stateCommand({ tabid: tabid2 }) {
|
|
364
|
+
const browser2 = await connectBrowser();
|
|
414
365
|
try {
|
|
415
|
-
const
|
|
416
|
-
|
|
366
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
367
|
+
await injectIntoPage(page2);
|
|
368
|
+
const state = await page2.evaluate(async () => {
|
|
417
369
|
const url = document.URL;
|
|
418
370
|
const title = document.title;
|
|
419
371
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
@@ -461,27 +413,29 @@ async function stateCommand({ tabid }) {
|
|
|
461
413
|
webmcpTools
|
|
462
414
|
};
|
|
463
415
|
});
|
|
464
|
-
const pages = await
|
|
465
|
-
const tabs2 = await Promise.all(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
416
|
+
const pages = await browser2.pages();
|
|
417
|
+
const tabs2 = await Promise.all(
|
|
418
|
+
pages.map(async (p) => {
|
|
419
|
+
const pUrl = p.url();
|
|
420
|
+
if (pUrl.startsWith("devtools://")) return null;
|
|
421
|
+
const pTitle = await Promise.race([
|
|
422
|
+
p.title().catch(() => "Unknown"),
|
|
423
|
+
new Promise((resolve2) => setTimeout(() => resolve2("Unknown"), 500))
|
|
424
|
+
]);
|
|
425
|
+
return {
|
|
426
|
+
// 使用真实的 Chrome target ID,而非数组下标
|
|
427
|
+
tabid: await getPageTargetId(p).catch(() => pUrl),
|
|
428
|
+
title: pTitle,
|
|
429
|
+
url: pUrl
|
|
430
|
+
};
|
|
431
|
+
})
|
|
432
|
+
);
|
|
479
433
|
return {
|
|
480
434
|
...state,
|
|
481
435
|
tabs: tabs2.filter(Boolean)
|
|
482
436
|
};
|
|
483
437
|
} finally {
|
|
484
|
-
await
|
|
438
|
+
await browser2.disconnect();
|
|
485
439
|
}
|
|
486
440
|
}
|
|
487
441
|
|
|
@@ -489,17 +443,17 @@ async function stateCommand({ tabid }) {
|
|
|
489
443
|
async function runCommand({
|
|
490
444
|
toolName,
|
|
491
445
|
argsJson,
|
|
492
|
-
tabid
|
|
446
|
+
tabid: tabid2
|
|
493
447
|
}) {
|
|
494
|
-
const
|
|
448
|
+
const browser2 = await connectBrowser();
|
|
495
449
|
try {
|
|
496
|
-
const
|
|
450
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
497
451
|
try {
|
|
498
452
|
JSON.parse(argsJson);
|
|
499
453
|
} catch (e) {
|
|
500
454
|
throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
|
|
501
455
|
}
|
|
502
|
-
const
|
|
456
|
+
const result2 = await page2.evaluate(async (name, inputString) => {
|
|
503
457
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
504
458
|
if (!mcp || typeof mcp.executeTool !== "function") {
|
|
505
459
|
throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
|
|
@@ -513,12 +467,51 @@ async function runCommand({
|
|
|
513
467
|
}
|
|
514
468
|
return res;
|
|
515
469
|
}, toolName, argsJson);
|
|
470
|
+
return result2;
|
|
471
|
+
} finally {
|
|
472
|
+
await browser2.disconnect();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/commands/evaluate.ts
|
|
477
|
+
async function evaluateCommand({
|
|
478
|
+
jsScript,
|
|
479
|
+
tabid
|
|
480
|
+
}) {
|
|
481
|
+
const browser = await connectBrowser();
|
|
482
|
+
try {
|
|
483
|
+
const page = await getTargetPage(browser, tabid);
|
|
484
|
+
const result = await page.evaluate(async (script) => {
|
|
485
|
+
try {
|
|
486
|
+
const result = eval(script);
|
|
487
|
+
if (result instanceof Promise) {
|
|
488
|
+
return await result;
|
|
489
|
+
}
|
|
490
|
+
return result;
|
|
491
|
+
} catch (error) {
|
|
492
|
+
return {
|
|
493
|
+
error: true,
|
|
494
|
+
message: error.message,
|
|
495
|
+
stack: error.stack
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
}, jsScript);
|
|
516
499
|
return result;
|
|
517
500
|
} finally {
|
|
518
501
|
await browser.disconnect();
|
|
519
502
|
}
|
|
520
503
|
}
|
|
521
504
|
|
|
505
|
+
// src/commands/clipboard.ts
|
|
506
|
+
import clipboard from "clipboardy";
|
|
507
|
+
async function setClipboard(content) {
|
|
508
|
+
try {
|
|
509
|
+
await clipboard.write(content);
|
|
510
|
+
} catch (error) {
|
|
511
|
+
throw new Error(`\u8BBE\u7F6E\u526A\u8D34\u677F\u5931\u8D25: ${error.message}`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
522
515
|
// src/commands/tabs.ts
|
|
523
516
|
function normalizeUrl(url) {
|
|
524
517
|
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
@@ -527,114 +520,114 @@ function normalizeUrl(url) {
|
|
|
527
520
|
return url;
|
|
528
521
|
}
|
|
529
522
|
async function tabsOpenCommand(url) {
|
|
530
|
-
const
|
|
523
|
+
const browser2 = await connectBrowser();
|
|
531
524
|
try {
|
|
532
|
-
const
|
|
525
|
+
const page2 = await browser2.newPage();
|
|
533
526
|
url = normalizeUrl(url);
|
|
534
|
-
await
|
|
535
|
-
await injectIntoPage(
|
|
536
|
-
await new Promise((
|
|
527
|
+
await page2.goto(url, { waitUntil: "domcontentloaded" });
|
|
528
|
+
await injectIntoPage(page2);
|
|
529
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
537
530
|
return {
|
|
538
531
|
success: true,
|
|
539
|
-
tabid: await getPageTargetId(
|
|
540
|
-
url:
|
|
541
|
-
title: await
|
|
532
|
+
tabid: await getPageTargetId(page2),
|
|
533
|
+
url: page2.url(),
|
|
534
|
+
title: await page2.title()
|
|
542
535
|
};
|
|
543
536
|
} finally {
|
|
544
|
-
await
|
|
537
|
+
await browser2.disconnect();
|
|
545
538
|
}
|
|
546
539
|
}
|
|
547
|
-
async function tabsCloseCommand(
|
|
548
|
-
const
|
|
540
|
+
async function tabsCloseCommand(tabid2) {
|
|
541
|
+
const browser2 = await connectBrowser();
|
|
549
542
|
try {
|
|
550
|
-
const target = findPageTargetByTabId(
|
|
543
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
551
544
|
if (!target) {
|
|
552
|
-
throw new Error(`Tab with targetId "${
|
|
545
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
553
546
|
}
|
|
554
|
-
const
|
|
555
|
-
if (!
|
|
556
|
-
throw new Error(`Tab with targetId "${
|
|
547
|
+
const page2 = await target.page();
|
|
548
|
+
if (!page2) {
|
|
549
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
557
550
|
}
|
|
558
|
-
const closedTabid = await getPageTargetId(
|
|
559
|
-
await
|
|
551
|
+
const closedTabid = await getPageTargetId(page2).catch(() => tabid2);
|
|
552
|
+
await page2.close();
|
|
560
553
|
return {
|
|
561
554
|
success: true,
|
|
562
555
|
tabid: closedTabid
|
|
563
556
|
};
|
|
564
557
|
} finally {
|
|
565
|
-
await
|
|
558
|
+
await browser2.disconnect();
|
|
566
559
|
}
|
|
567
560
|
}
|
|
568
|
-
async function tabsSwitchCommand(
|
|
569
|
-
const
|
|
561
|
+
async function tabsSwitchCommand(tabid2) {
|
|
562
|
+
const browser2 = await connectBrowser();
|
|
570
563
|
try {
|
|
571
|
-
await activateTabById(
|
|
572
|
-
const
|
|
564
|
+
await activateTabById(browser2, tabid2);
|
|
565
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
573
566
|
return {
|
|
574
567
|
success: true,
|
|
575
|
-
tabid: await getPageTargetId(
|
|
576
|
-
url:
|
|
577
|
-
title: await
|
|
568
|
+
tabid: await getPageTargetId(page2),
|
|
569
|
+
url: page2.url(),
|
|
570
|
+
title: await page2.title()
|
|
578
571
|
};
|
|
579
572
|
} finally {
|
|
580
|
-
await
|
|
573
|
+
await browser2.disconnect();
|
|
581
574
|
}
|
|
582
575
|
}
|
|
583
|
-
async function tabsBackCommand(
|
|
584
|
-
const
|
|
576
|
+
async function tabsBackCommand(tabid2) {
|
|
577
|
+
const browser2 = await connectBrowser();
|
|
585
578
|
try {
|
|
586
|
-
const
|
|
587
|
-
const response = await
|
|
579
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
580
|
+
const response = await page2.goBack();
|
|
588
581
|
if (!response) {
|
|
589
582
|
return {
|
|
590
583
|
success: false,
|
|
591
584
|
error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
|
|
592
|
-
tabid: await getPageTargetId(
|
|
593
|
-
url:
|
|
594
|
-
title: await
|
|
585
|
+
tabid: await getPageTargetId(page2).catch(() => tabid2),
|
|
586
|
+
url: page2.url(),
|
|
587
|
+
title: await page2.title()
|
|
595
588
|
};
|
|
596
589
|
}
|
|
597
|
-
await injectIntoPage(
|
|
590
|
+
await injectIntoPage(page2);
|
|
598
591
|
return {
|
|
599
592
|
success: true,
|
|
600
|
-
tabid: await getPageTargetId(
|
|
601
|
-
url:
|
|
602
|
-
title: await
|
|
593
|
+
tabid: await getPageTargetId(page2),
|
|
594
|
+
url: page2.url(),
|
|
595
|
+
title: await page2.title()
|
|
603
596
|
};
|
|
604
597
|
} finally {
|
|
605
|
-
await
|
|
598
|
+
await browser2.disconnect();
|
|
606
599
|
}
|
|
607
600
|
}
|
|
608
|
-
async function tabsForwardCommand(
|
|
609
|
-
const
|
|
601
|
+
async function tabsForwardCommand(tabid2) {
|
|
602
|
+
const browser2 = await connectBrowser();
|
|
610
603
|
try {
|
|
611
|
-
const
|
|
612
|
-
const response = await
|
|
604
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
605
|
+
const response = await page2.goForward();
|
|
613
606
|
if (!response) {
|
|
614
607
|
return {
|
|
615
608
|
success: false,
|
|
616
609
|
error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
|
|
617
|
-
tabid: await getPageTargetId(
|
|
618
|
-
url:
|
|
619
|
-
title: await
|
|
610
|
+
tabid: await getPageTargetId(page2).catch(() => tabid2),
|
|
611
|
+
url: page2.url(),
|
|
612
|
+
title: await page2.title()
|
|
620
613
|
};
|
|
621
614
|
}
|
|
622
|
-
await injectIntoPage(
|
|
615
|
+
await injectIntoPage(page2);
|
|
623
616
|
return {
|
|
624
617
|
success: true,
|
|
625
|
-
tabid: await getPageTargetId(
|
|
626
|
-
url:
|
|
627
|
-
title: await
|
|
618
|
+
tabid: await getPageTargetId(page2),
|
|
619
|
+
url: page2.url(),
|
|
620
|
+
title: await page2.title()
|
|
628
621
|
};
|
|
629
622
|
} finally {
|
|
630
|
-
await
|
|
623
|
+
await browser2.disconnect();
|
|
631
624
|
}
|
|
632
625
|
}
|
|
633
626
|
|
|
634
627
|
// package.json
|
|
635
628
|
var package_default = {
|
|
636
629
|
name: "@opentiny/webmcp-cli",
|
|
637
|
-
version: "0.0.1-alpha.
|
|
630
|
+
version: "0.0.1-alpha.4",
|
|
638
631
|
type: "module",
|
|
639
632
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
640
633
|
main: "dist/index.js",
|
|
@@ -671,6 +664,7 @@ var package_default = {
|
|
|
671
664
|
"@mcp-b/webmcp-polyfill": "catalog:",
|
|
672
665
|
"@opentiny/next-sdk": "workspace:^",
|
|
673
666
|
"@page-agent/page-controller": "catalog:",
|
|
667
|
+
clipboardy: "^5.3.1",
|
|
674
668
|
commander: "^11.1.0",
|
|
675
669
|
picocolors: "^1.0.0",
|
|
676
670
|
"puppeteer-core": "^22.0.0"
|
|
@@ -695,6 +689,19 @@ function handleCommandError(error, commandName) {
|
|
|
695
689
|
console.error(pc2.red(`Error executing ${commandName} command: ${msg}`));
|
|
696
690
|
process.exit(1);
|
|
697
691
|
}
|
|
692
|
+
function expandFileRefs(argsJson) {
|
|
693
|
+
return argsJson.replace(/"@(base64)?file:([^"]+)"/g, (_match, base64Flag, filePath) => {
|
|
694
|
+
const absPath = resolve(process.cwd(), filePath);
|
|
695
|
+
let content;
|
|
696
|
+
try {
|
|
697
|
+
content = readFileSync(absPath, "utf-8");
|
|
698
|
+
} catch (e) {
|
|
699
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}": ${e.message}`);
|
|
700
|
+
}
|
|
701
|
+
const value = base64Flag ? Buffer.from(content, "utf-8").toString("base64") : content;
|
|
702
|
+
return JSON.stringify(value);
|
|
703
|
+
});
|
|
704
|
+
}
|
|
698
705
|
program.name("webmcp-cli").description("WebMCP CLI for interacting with browser via CDP").version(package_default.version).option("-w, --workspace <path>", "\u6307\u5B9A\u81EA\u5B9A\u4E49\u7684\u6D4F\u89C8\u5668\u5DE5\u4F5C\u7A7A\u95F4\uFF08\u7528\u6237\u914D\u7F6E\u76EE\u5F55\uFF09\u8DEF\u5F84").hook("preAction", (thisCommand) => {
|
|
699
706
|
const opts = thisCommand.opts();
|
|
700
707
|
if (opts.workspace) {
|
|
@@ -703,61 +710,100 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
|
|
|
703
710
|
});
|
|
704
711
|
program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\u72B6\u6001\uFF08\u5185\u5BB9\u3001\u6240\u6709\u9875\u7B7E\u5217\u8868\u3001\u53EF\u7528 WebMCP \u5DE5\u5177\u5217\u8868\uFF09").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (options) => {
|
|
705
712
|
try {
|
|
706
|
-
const
|
|
707
|
-
console.log(JSON.stringify(
|
|
713
|
+
const result2 = await stateCommand({ tabid: parseTabId(options.tabid) });
|
|
714
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
708
715
|
} catch (error) {
|
|
709
716
|
handleCommandError(error, "state");
|
|
710
717
|
}
|
|
711
718
|
});
|
|
712
|
-
program.command("run <toolName>
|
|
719
|
+
program.command("run <toolName> [argsJson]").description("\u5411\u6307\u5B9A\u9875\u7B7E\u8C03\u7528\u6307\u5B9A\u7684 WebMCP \u5DE5\u5177\u6267\u884C\u64CD\u4F5C").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").option(
|
|
720
|
+
"-f, --file <path>",
|
|
721
|
+
`\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A argsJson\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
|
|
722
|
+
\u5982\u9700\u5728 argsJson \u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
|
|
723
|
+
@file:<path> \u8BFB\u53D6\u6587\u4EF6\u539F\u59CB\u6587\u672C
|
|
724
|
+
@base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
|
|
725
|
+
\u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
|
|
726
|
+
).action(async (toolName, argsJson, options) => {
|
|
713
727
|
try {
|
|
714
|
-
|
|
728
|
+
let finalArgsJson = argsJson;
|
|
729
|
+
if (options.file) {
|
|
730
|
+
const filePath = resolve(process.cwd(), options.file);
|
|
731
|
+
try {
|
|
732
|
+
finalArgsJson = readFileSync(filePath, "utf-8").trim();
|
|
733
|
+
} catch (e) {
|
|
734
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (!finalArgsJson) {
|
|
738
|
+
throw new Error("\u5FC5\u987B\u63D0\u4F9B argsJson \u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
|
|
739
|
+
}
|
|
740
|
+
finalArgsJson = expandFileRefs(finalArgsJson);
|
|
741
|
+
const result2 = await runCommand({
|
|
715
742
|
toolName,
|
|
716
|
-
argsJson,
|
|
743
|
+
argsJson: finalArgsJson,
|
|
717
744
|
tabid: parseTabId(options.tabid)
|
|
718
745
|
});
|
|
719
|
-
console.log(JSON.stringify(
|
|
746
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
720
747
|
} catch (error) {
|
|
721
748
|
handleCommandError(error, "run");
|
|
722
749
|
}
|
|
723
750
|
});
|
|
751
|
+
program.command("evaluate <jsScript>").description("\u5411\u5F53\u524D\u7F51\u9875\u6216\u6307\u5B9A\u9875\u7B7E\u6CE8\u5165\u5E76\u6267\u884C JavaScript \u4EE3\u7801").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (jsScript2, options) => {
|
|
752
|
+
try {
|
|
753
|
+
const result2 = await evaluateCommand({
|
|
754
|
+
jsScript: jsScript2,
|
|
755
|
+
tabid: parseTabId(options.tabid)
|
|
756
|
+
});
|
|
757
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
758
|
+
} catch (error) {
|
|
759
|
+
handleCommandError(error, "evaluate");
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
|
|
763
|
+
try {
|
|
764
|
+
await setClipboard(content);
|
|
765
|
+
console.log(JSON.stringify({ success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" }, null, 2));
|
|
766
|
+
} catch (error) {
|
|
767
|
+
handleCommandError(error, "clipboard");
|
|
768
|
+
}
|
|
769
|
+
});
|
|
724
770
|
var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
|
|
725
771
|
tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
|
|
726
772
|
try {
|
|
727
|
-
const
|
|
728
|
-
console.log(JSON.stringify(
|
|
773
|
+
const result2 = await tabsOpenCommand(url);
|
|
774
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
729
775
|
} catch (error) {
|
|
730
776
|
handleCommandError(error, "tabs open");
|
|
731
777
|
}
|
|
732
778
|
});
|
|
733
|
-
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
779
|
+
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
|
|
734
780
|
try {
|
|
735
|
-
const
|
|
736
|
-
console.log(JSON.stringify(
|
|
781
|
+
const result2 = await tabsCloseCommand(tabid2);
|
|
782
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
737
783
|
} catch (error) {
|
|
738
784
|
handleCommandError(error, "tabs close");
|
|
739
785
|
}
|
|
740
786
|
});
|
|
741
|
-
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
787
|
+
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
|
|
742
788
|
try {
|
|
743
|
-
const
|
|
744
|
-
console.log(JSON.stringify(
|
|
789
|
+
const result2 = await tabsSwitchCommand(tabid2);
|
|
790
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
745
791
|
} catch (error) {
|
|
746
792
|
handleCommandError(error, "tabs switch");
|
|
747
793
|
}
|
|
748
794
|
});
|
|
749
|
-
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (
|
|
795
|
+
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid2) => {
|
|
750
796
|
try {
|
|
751
|
-
const
|
|
752
|
-
console.log(JSON.stringify(
|
|
797
|
+
const result2 = await tabsBackCommand(parseTabId(tabid2));
|
|
798
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
753
799
|
} catch (error) {
|
|
754
800
|
handleCommandError(error, "tabs back");
|
|
755
801
|
}
|
|
756
802
|
});
|
|
757
|
-
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (
|
|
803
|
+
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid2) => {
|
|
758
804
|
try {
|
|
759
|
-
const
|
|
760
|
-
console.log(JSON.stringify(
|
|
805
|
+
const result2 = await tabsForwardCommand(parseTabId(tabid2));
|
|
806
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
761
807
|
} catch (error) {
|
|
762
808
|
handleCommandError(error, "tabs forward");
|
|
763
809
|
}
|