@opentiny/webmcp-cli 0.0.1-alpha.3 → 0.0.1-alpha.5
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 +153 -150
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +153 -156
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +80 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +80 -150
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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,45 +65,6 @@ async function checkCdpReady(url, retries = 3) {
|
|
|
55
65
|
}
|
|
56
66
|
return false;
|
|
57
67
|
}
|
|
58
|
-
async function killProcessOnPortIfZombie(port) {
|
|
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
68
|
function getDefaultBrowserPath() {
|
|
98
69
|
const platform = os.platform();
|
|
99
70
|
if (platform === "darwin") {
|
|
@@ -156,20 +127,18 @@ async function startBrowserInBackground() {
|
|
|
156
127
|
child.unref();
|
|
157
128
|
for (let i = 0; i < 20; i++) {
|
|
158
129
|
try {
|
|
159
|
-
const urls = [`http://
|
|
130
|
+
const urls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`];
|
|
160
131
|
for (const url of urls) {
|
|
161
132
|
try {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
133
|
+
await httpGet(url, 1e3);
|
|
134
|
+
console.log(pc.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
|
|
135
|
+
return;
|
|
167
136
|
} catch (err) {
|
|
168
137
|
}
|
|
169
138
|
}
|
|
170
139
|
} catch (e) {
|
|
171
140
|
}
|
|
172
|
-
await new Promise((
|
|
141
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
173
142
|
}
|
|
174
143
|
throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
|
|
175
144
|
}
|
|
@@ -190,90 +159,51 @@ async function connectBrowser() {
|
|
|
190
159
|
return false;
|
|
191
160
|
}
|
|
192
161
|
};
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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 {
|
|
197
180
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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", "");
|
|
211
194
|
try {
|
|
212
|
-
|
|
213
|
-
if (!isLocalhostReady) {
|
|
214
|
-
throw new Error("localhost CDP port not responding");
|
|
215
|
-
}
|
|
216
|
-
console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
195
|
+
console.log(pc.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
217
196
|
const browser2 = await promiseWithTimeout(
|
|
218
|
-
puppeteer.connect({
|
|
219
|
-
browserURL: `http://localhost:${CDP_PORT}`,
|
|
220
|
-
defaultViewport: null,
|
|
221
|
-
targetFilter
|
|
222
|
-
}),
|
|
197
|
+
puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
223
198
|
1e4,
|
|
224
|
-
|
|
199
|
+
`puppeteer.connect to ${browserURL} after launch timed out`
|
|
225
200
|
);
|
|
226
|
-
console.log(pc.green(
|
|
201
|
+
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
227
202
|
return browser2;
|
|
228
|
-
} catch
|
|
229
|
-
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)}`));
|
|
230
|
-
try {
|
|
231
|
-
await killProcessOnPortIfZombie(CDP_PORT);
|
|
232
|
-
await startBrowserInBackground();
|
|
233
|
-
try {
|
|
234
|
-
const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
|
|
235
|
-
if (!is127Ready) {
|
|
236
|
-
throw new Error("127.0.0.1 CDP port not responding after launch");
|
|
237
|
-
}
|
|
238
|
-
console.log(pc.yellow("connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
|
|
239
|
-
const browser2 = await promiseWithTimeout(
|
|
240
|
-
puppeteer.connect({
|
|
241
|
-
browserURL: `http://127.0.0.1:${CDP_PORT}`,
|
|
242
|
-
defaultViewport: null,
|
|
243
|
-
targetFilter
|
|
244
|
-
}),
|
|
245
|
-
1e4,
|
|
246
|
-
"puppeteer.connect to 127.0.0.1 after launch timed out"
|
|
247
|
-
);
|
|
248
|
-
console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
|
|
249
|
-
return browser2;
|
|
250
|
-
} catch (e) {
|
|
251
|
-
const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
|
|
252
|
-
if (!isLocalhostReady) {
|
|
253
|
-
throw new Error("localhost CDP port not responding after launch");
|
|
254
|
-
}
|
|
255
|
-
console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
256
|
-
const browser2 = await promiseWithTimeout(
|
|
257
|
-
puppeteer.connect({
|
|
258
|
-
browserURL: `http://localhost:${CDP_PORT}`,
|
|
259
|
-
defaultViewport: null,
|
|
260
|
-
targetFilter
|
|
261
|
-
}),
|
|
262
|
-
1e4,
|
|
263
|
-
"puppeteer.connect to localhost after launch timed out"
|
|
264
|
-
);
|
|
265
|
-
console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
|
|
266
|
-
return browser2;
|
|
267
|
-
}
|
|
268
|
-
} catch (launchError) {
|
|
269
|
-
const msg = launchError instanceof Error ? launchError.message : String(launchError);
|
|
270
|
-
console.error(pc.red(`\u65E0\u6CD5\u8FDE\u63A5\u6216\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
271
|
-
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\u548C\u767B\u5F55\u6001\uFF09\uFF0C\u5982\u679C\u4F60\u7684 Chrome/Edge \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`));
|
|
272
|
-
console.error(pc.yellow(`\u{1F449} \u89E3\u51B3\u529E\u6CD5\uFF1A\u8BF7\u5148\u5B8C\u5168\u9000\u51FA\u5F53\u524D\u7684 Chrome \u6216 Edge \u6D4F\u89C8\u5668\uFF08\u5728 Mac \u4E0A\u6309 Cmd+Q\uFF0CWindows \u4E0A\u53F3\u952E\u4EFB\u52A1\u680F\u56FE\u6807\u9000\u51FA\uFF09\uFF0C\u7136\u540E\u518D\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u3002`));
|
|
273
|
-
throw new Error("Browser connection failed.");
|
|
274
|
-
}
|
|
203
|
+
} catch {
|
|
275
204
|
}
|
|
276
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`);
|
|
277
207
|
}
|
|
278
208
|
async function getPageTargetId(page2) {
|
|
279
209
|
const session = await page2.target().createCDPSession();
|
|
@@ -351,14 +281,12 @@ async function getTargetPage(browser2, tabid2) {
|
|
|
351
281
|
let activeTargetId = null;
|
|
352
282
|
for (const url of urls) {
|
|
353
283
|
try {
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
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;
|
|
362
290
|
}
|
|
363
291
|
} catch {
|
|
364
292
|
}
|
|
@@ -404,7 +332,7 @@ async function injectWebMCPPolyfillAndTools(page2, force = false) {
|
|
|
404
332
|
const msg = err instanceof Error ? err.message : String(err);
|
|
405
333
|
throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
|
|
406
334
|
}
|
|
407
|
-
await new Promise((
|
|
335
|
+
await new Promise((resolve2) => setTimeout(resolve2, 300));
|
|
408
336
|
}
|
|
409
337
|
await injectDomainTools(page2);
|
|
410
338
|
}
|
|
@@ -492,7 +420,7 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
492
420
|
if (pUrl.startsWith("devtools://")) return null;
|
|
493
421
|
const pTitle = await Promise.race([
|
|
494
422
|
p.title().catch(() => "Unknown"),
|
|
495
|
-
new Promise((
|
|
423
|
+
new Promise((resolve2) => setTimeout(() => resolve2("Unknown"), 500))
|
|
496
424
|
]);
|
|
497
425
|
return {
|
|
498
426
|
// 使用真实的 Chrome target ID,而非数组下标
|
|
@@ -512,6 +440,7 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
512
440
|
}
|
|
513
441
|
|
|
514
442
|
// src/commands/run.ts
|
|
443
|
+
import JSON5 from "json5";
|
|
515
444
|
async function runCommand({
|
|
516
445
|
toolName,
|
|
517
446
|
argsJson,
|
|
@@ -520,10 +449,15 @@ async function runCommand({
|
|
|
520
449
|
const browser2 = await connectBrowser();
|
|
521
450
|
try {
|
|
522
451
|
const page2 = await getTargetPage(browser2, tabid2);
|
|
452
|
+
let cleanedArgs = argsJson.trim();
|
|
453
|
+
if (cleanedArgs.startsWith("'") && cleanedArgs.endsWith("'")) {
|
|
454
|
+
cleanedArgs = cleanedArgs.slice(1, -1).trim();
|
|
455
|
+
}
|
|
523
456
|
try {
|
|
524
|
-
|
|
457
|
+
const obj = JSON5.parse(cleanedArgs);
|
|
458
|
+
cleanedArgs = JSON.stringify(obj);
|
|
525
459
|
} catch (e) {
|
|
526
|
-
throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
|
|
460
|
+
throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${e.message}`);
|
|
527
461
|
}
|
|
528
462
|
const result2 = await page2.evaluate(async (name, inputString) => {
|
|
529
463
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
@@ -537,8 +471,7 @@ async function runCommand({
|
|
|
537
471
|
} catch (e) {
|
|
538
472
|
}
|
|
539
473
|
}
|
|
540
|
-
|
|
541
|
-
}, toolName, argsJson);
|
|
474
|
+
}, toolName, cleanedArgs);
|
|
542
475
|
return result2;
|
|
543
476
|
} finally {
|
|
544
477
|
await browser2.disconnect();
|
|
@@ -598,7 +531,7 @@ async function tabsOpenCommand(url) {
|
|
|
598
531
|
url = normalizeUrl(url);
|
|
599
532
|
await page2.goto(url, { waitUntil: "domcontentloaded" });
|
|
600
533
|
await injectIntoPage(page2);
|
|
601
|
-
await new Promise((
|
|
534
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
602
535
|
return {
|
|
603
536
|
success: true,
|
|
604
537
|
tabid: await getPageTargetId(page2),
|
|
@@ -699,7 +632,7 @@ async function tabsForwardCommand(tabid2) {
|
|
|
699
632
|
// package.json
|
|
700
633
|
var package_default = {
|
|
701
634
|
name: "@opentiny/webmcp-cli",
|
|
702
|
-
version: "0.0.1-alpha.
|
|
635
|
+
version: "0.0.1-alpha.5",
|
|
703
636
|
type: "module",
|
|
704
637
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
705
638
|
main: "dist/index.js",
|
|
@@ -738,6 +671,7 @@ var package_default = {
|
|
|
738
671
|
"@page-agent/page-controller": "catalog:",
|
|
739
672
|
clipboardy: "^5.3.1",
|
|
740
673
|
commander: "^11.1.0",
|
|
674
|
+
json5: "^2.2.3",
|
|
741
675
|
picocolors: "^1.0.0",
|
|
742
676
|
"puppeteer-core": "^22.0.0"
|
|
743
677
|
},
|
|
@@ -752,6 +686,13 @@ var package_default = {
|
|
|
752
686
|
|
|
753
687
|
// src/bin.ts
|
|
754
688
|
var program = new Command();
|
|
689
|
+
function cleanJsonString(str) {
|
|
690
|
+
let cleaned = str.trim();
|
|
691
|
+
if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
|
|
692
|
+
cleaned = cleaned.slice(1, -1).trim();
|
|
693
|
+
}
|
|
694
|
+
return cleaned;
|
|
695
|
+
}
|
|
755
696
|
function parseTabId(id) {
|
|
756
697
|
if (!id) return void 0;
|
|
757
698
|
return id;
|
|
@@ -761,6 +702,19 @@ function handleCommandError(error, commandName) {
|
|
|
761
702
|
console.error(pc2.red(`Error executing ${commandName} command: ${msg}`));
|
|
762
703
|
process.exit(1);
|
|
763
704
|
}
|
|
705
|
+
function expandFileRefs(argsJson) {
|
|
706
|
+
return argsJson.replace(/"@(base64)?file:([^"]+)"/g, (_match, base64Flag, filePath) => {
|
|
707
|
+
const absPath = resolve(process.cwd(), filePath);
|
|
708
|
+
let content;
|
|
709
|
+
try {
|
|
710
|
+
content = readFileSync(absPath, "utf-8");
|
|
711
|
+
} catch (e) {
|
|
712
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}": ${e.message}`);
|
|
713
|
+
}
|
|
714
|
+
const value = base64Flag ? Buffer.from(content, "utf-8").toString("base64") : content;
|
|
715
|
+
return JSON.stringify(value);
|
|
716
|
+
});
|
|
717
|
+
}
|
|
764
718
|
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) => {
|
|
765
719
|
const opts = thisCommand.opts();
|
|
766
720
|
if (opts.workspace) {
|
|
@@ -775,11 +729,54 @@ program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D
|
|
|
775
729
|
handleCommandError(error, "state");
|
|
776
730
|
}
|
|
777
731
|
});
|
|
778
|
-
program.command("run <toolName>
|
|
732
|
+
program.command("run <toolName> [args...]").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(
|
|
733
|
+
"-f, --file <path>",
|
|
734
|
+
`\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A\u53C2\u6570\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
|
|
735
|
+
\u5982\u9700\u5728\u53C2\u6570\u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
|
|
736
|
+
@file:<path> \u8BFB\u53D6\u6587\u4EF6\u539F\u59CB\u6587\u672C
|
|
737
|
+
@base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
|
|
738
|
+
\u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
|
|
739
|
+
).action(async (toolName, args, options) => {
|
|
779
740
|
try {
|
|
741
|
+
let finalArgsJson = "";
|
|
742
|
+
if (options.file) {
|
|
743
|
+
const filePath = resolve(process.cwd(), options.file);
|
|
744
|
+
try {
|
|
745
|
+
finalArgsJson = readFileSync(filePath, "utf-8").trim();
|
|
746
|
+
} catch (e) {
|
|
747
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
|
|
748
|
+
}
|
|
749
|
+
} else if (args && args.length > 0) {
|
|
750
|
+
const firstArg = args[0].trim();
|
|
751
|
+
const cleanedFirstArg = cleanJsonString(firstArg);
|
|
752
|
+
if (cleanedFirstArg.startsWith("{") && cleanedFirstArg.endsWith("}")) {
|
|
753
|
+
finalArgsJson = args.map(cleanJsonString).join(" ");
|
|
754
|
+
} else {
|
|
755
|
+
const obj = {};
|
|
756
|
+
for (const pair of args) {
|
|
757
|
+
const index = pair.indexOf("=");
|
|
758
|
+
if (index === -1) {
|
|
759
|
+
throw new Error(`\u65E0\u6548\u7684\u53C2\u6570\u683C\u5F0F "${pair}"\u3002\u5FC5\u987B\u662F JSON \u5B57\u7B26\u4E32\u6216 key=value \u683C\u5F0F\u3002`);
|
|
760
|
+
}
|
|
761
|
+
const key = pair.substring(0, index).trim();
|
|
762
|
+
let val = pair.substring(index + 1).trim();
|
|
763
|
+
val = cleanJsonString(val);
|
|
764
|
+
try {
|
|
765
|
+
obj[key] = JSON.parse(val);
|
|
766
|
+
} catch {
|
|
767
|
+
obj[key] = val;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
finalArgsJson = JSON.stringify(obj);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (!finalArgsJson) {
|
|
774
|
+
throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
|
|
775
|
+
}
|
|
776
|
+
finalArgsJson = expandFileRefs(finalArgsJson);
|
|
780
777
|
const result2 = await runCommand({
|
|
781
778
|
toolName,
|
|
782
|
-
argsJson,
|
|
779
|
+
argsJson: finalArgsJson,
|
|
783
780
|
tabid: parseTabId(options.tabid)
|
|
784
781
|
});
|
|
785
782
|
console.log(JSON.stringify(result2, null, 2));
|