@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.cjs
CHANGED
|
@@ -26,6 +26,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
// src/bin.ts
|
|
27
27
|
var import_commander = require("commander");
|
|
28
28
|
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
29
|
+
var import_fs2 = require("fs");
|
|
30
|
+
var import_path2 = require("path");
|
|
29
31
|
|
|
30
32
|
// src/browser.ts
|
|
31
33
|
var import_puppeteer_core = __toESM(require("puppeteer-core"), 1);
|
|
@@ -33,6 +35,7 @@ var import_picocolors = __toESM(require("picocolors"), 1);
|
|
|
33
35
|
var import_fs = __toESM(require("fs"), 1);
|
|
34
36
|
var import_os = __toESM(require("os"), 1);
|
|
35
37
|
var import_path = __toESM(require("path"), 1);
|
|
38
|
+
var import_http = __toESM(require("http"), 1);
|
|
36
39
|
var import_child_process = require("child_process");
|
|
37
40
|
var import_url = require("url");
|
|
38
41
|
var import_meta = {};
|
|
@@ -40,14 +43,27 @@ var __filename = (0, import_url.fileURLToPath)(import_meta.url);
|
|
|
40
43
|
var __dirname = import_path.default.dirname(__filename);
|
|
41
44
|
var CDP_PORT = 9222;
|
|
42
45
|
var CDP_URL = `http://localhost:${CDP_PORT}`;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
function httpGet(url, timeoutMs = 1500) {
|
|
47
|
+
return new Promise((resolve2, reject) => {
|
|
48
|
+
const req = import_http.default.get(url, { timeout: timeoutMs }, (res) => {
|
|
49
|
+
let data = "";
|
|
50
|
+
res.on("data", (chunk) => {
|
|
51
|
+
data += chunk.toString();
|
|
52
|
+
});
|
|
53
|
+
res.on("end", () => {
|
|
54
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
55
|
+
resolve2(data);
|
|
56
|
+
} else {
|
|
57
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
req.on("error", reject);
|
|
62
|
+
req.on("timeout", () => {
|
|
63
|
+
req.destroy();
|
|
64
|
+
reject(new Error("request timeout"));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
51
67
|
}
|
|
52
68
|
function promiseWithTimeout(promise, timeoutMs = 5e3, errorMsg = "Operation timed out") {
|
|
53
69
|
let timeoutId;
|
|
@@ -63,8 +79,8 @@ function promiseWithTimeout(promise, timeoutMs = 5e3, errorMsg = "Operation time
|
|
|
63
79
|
async function checkCdpReady(url, retries = 3) {
|
|
64
80
|
for (let i = 0; i < retries; i++) {
|
|
65
81
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
await httpGet(url, 1500);
|
|
83
|
+
return true;
|
|
68
84
|
} catch {
|
|
69
85
|
}
|
|
70
86
|
if (i < retries - 1) {
|
|
@@ -73,45 +89,6 @@ async function checkCdpReady(url, retries = 3) {
|
|
|
73
89
|
}
|
|
74
90
|
return false;
|
|
75
91
|
}
|
|
76
|
-
async function killProcessOnPortIfZombie(port) {
|
|
77
|
-
const isResponding = await checkCdpReady(`http://127.0.0.1:${port}/json/version`, 1);
|
|
78
|
-
if (isResponding) {
|
|
79
|
-
console.log(import_picocolors.default.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`));
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
const platform = import_os.default.platform();
|
|
84
|
-
const { execSync } = require("child_process");
|
|
85
|
-
if (platform === "darwin" || platform === "linux") {
|
|
86
|
-
console.log(import_picocolors.default.yellow(`\u6B63\u5728\u68C0\u6D4B\u5E76\u6E05\u7406\u5360\u7528 ${port} \u7AEF\u53E3\u7684\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B...`));
|
|
87
|
-
const pids = execSync(`lsof -t -i :${port}`).toString().trim();
|
|
88
|
-
if (pids) {
|
|
89
|
-
console.log(import_picocolors.default.yellow(`\u53D1\u73B0\u50F5\u5C38 PID: ${pids.split("\n").join(", ")}\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`));
|
|
90
|
-
execSync(`kill -9 ${pids.split("\n").join(" ")}`);
|
|
91
|
-
console.log(import_picocolors.default.green(`\u6210\u529F\u6E05\u7406\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B`));
|
|
92
|
-
}
|
|
93
|
-
} else if (platform === "win32") {
|
|
94
|
-
console.log(import_picocolors.default.yellow(`\u6B63\u5728\u68C0\u6D4B\u5E76\u6E05\u7406 Windows \u4E0A\u5360\u7528 ${port} \u7AEF\u53E3\u7684\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B...`));
|
|
95
|
-
const output = execSync(`netstat -ano | findstr :${port}`).toString().trim();
|
|
96
|
-
if (output) {
|
|
97
|
-
const lines = output.split("\n");
|
|
98
|
-
const pids = /* @__PURE__ */ new Set();
|
|
99
|
-
lines.forEach((line) => {
|
|
100
|
-
const parts = line.trim().split(/\s+/);
|
|
101
|
-
const pid = parts[parts.length - 1];
|
|
102
|
-
if (pid && /^\d+$/.test(pid) && pid !== "0") {
|
|
103
|
-
pids.add(pid);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
pids.forEach((pid) => {
|
|
107
|
-
console.log(import_picocolors.default.yellow(`\u53D1\u73B0 Windows \u6B8B\u7559\u50F5\u5C38 PID: ${pid}\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`));
|
|
108
|
-
execSync(`taskkill /F /PID ${pid}`);
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} catch (e) {
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
92
|
function getDefaultBrowserPath() {
|
|
116
93
|
const platform = import_os.default.platform();
|
|
117
94
|
if (platform === "darwin") {
|
|
@@ -174,20 +151,18 @@ async function startBrowserInBackground() {
|
|
|
174
151
|
child.unref();
|
|
175
152
|
for (let i = 0; i < 20; i++) {
|
|
176
153
|
try {
|
|
177
|
-
const urls = [`http://
|
|
154
|
+
const urls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`];
|
|
178
155
|
for (const url of urls) {
|
|
179
156
|
try {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
157
|
+
await httpGet(url, 1e3);
|
|
158
|
+
console.log(import_picocolors.default.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
|
|
159
|
+
return;
|
|
185
160
|
} catch (err) {
|
|
186
161
|
}
|
|
187
162
|
}
|
|
188
163
|
} catch (e) {
|
|
189
164
|
}
|
|
190
|
-
await new Promise((
|
|
165
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
191
166
|
}
|
|
192
167
|
throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
|
|
193
168
|
}
|
|
@@ -208,90 +183,51 @@ async function connectBrowser() {
|
|
|
208
183
|
return false;
|
|
209
184
|
}
|
|
210
185
|
};
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
186
|
+
const portAddresses = [
|
|
187
|
+
`http://127.0.0.1:${CDP_PORT}/json/version`,
|
|
188
|
+
`http://localhost:${CDP_PORT}/json/version`
|
|
189
|
+
];
|
|
190
|
+
for (const addr of portAddresses) {
|
|
191
|
+
const isReady = await checkCdpReady(addr, 2);
|
|
192
|
+
if (!isReady) continue;
|
|
193
|
+
const browserURL = addr.replace("/json/version", "");
|
|
194
|
+
try {
|
|
195
|
+
console.log(import_picocolors.default.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
196
|
+
const browser2 = await promiseWithTimeout(
|
|
197
|
+
import_puppeteer_core.default.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
198
|
+
1e4,
|
|
199
|
+
`puppeteer.connect to ${browserURL} timed out`
|
|
200
|
+
);
|
|
201
|
+
console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
202
|
+
return browser2;
|
|
203
|
+
} catch {
|
|
215
204
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
205
|
+
}
|
|
206
|
+
console.log(import_picocolors.default.yellow(`connectBrowser: \u7AEF\u53E3 ${CDP_PORT} \u65E0\u54CD\u5E94\uFF0C\u6B63\u5728\u542F\u52A8\u65B0\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B...`));
|
|
207
|
+
try {
|
|
208
|
+
await startBrowserInBackground();
|
|
209
|
+
} catch (launchError) {
|
|
210
|
+
const msg = launchError instanceof Error ? launchError.message : String(launchError);
|
|
211
|
+
console.error(import_picocolors.default.red(`\u65E0\u6CD5\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
212
|
+
throw new Error("Browser launch failed.");
|
|
213
|
+
}
|
|
214
|
+
for (const addr of portAddresses) {
|
|
215
|
+
const isReady = await checkCdpReady(addr, 3);
|
|
216
|
+
if (!isReady) continue;
|
|
217
|
+
const browserURL = addr.replace("/json/version", "");
|
|
229
218
|
try {
|
|
230
|
-
|
|
231
|
-
if (!isLocalhostReady) {
|
|
232
|
-
throw new Error("localhost CDP port not responding");
|
|
233
|
-
}
|
|
234
|
-
console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
219
|
+
console.log(import_picocolors.default.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
235
220
|
const browser2 = await promiseWithTimeout(
|
|
236
|
-
import_puppeteer_core.default.connect({
|
|
237
|
-
browserURL: `http://localhost:${CDP_PORT}`,
|
|
238
|
-
defaultViewport: null,
|
|
239
|
-
targetFilter
|
|
240
|
-
}),
|
|
221
|
+
import_puppeteer_core.default.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
241
222
|
1e4,
|
|
242
|
-
|
|
223
|
+
`puppeteer.connect to ${browserURL} after launch timed out`
|
|
243
224
|
);
|
|
244
|
-
console.log(import_picocolors.default.green(
|
|
225
|
+
console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
245
226
|
return browser2;
|
|
246
|
-
} catch
|
|
247
|
-
console.log(import_picocolors.default.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)}`));
|
|
248
|
-
try {
|
|
249
|
-
await killProcessOnPortIfZombie(CDP_PORT);
|
|
250
|
-
await startBrowserInBackground();
|
|
251
|
-
try {
|
|
252
|
-
const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
|
|
253
|
-
if (!is127Ready) {
|
|
254
|
-
throw new Error("127.0.0.1 CDP port not responding after launch");
|
|
255
|
-
}
|
|
256
|
-
console.log(import_picocolors.default.yellow("connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
|
|
257
|
-
const browser2 = await promiseWithTimeout(
|
|
258
|
-
import_puppeteer_core.default.connect({
|
|
259
|
-
browserURL: `http://127.0.0.1:${CDP_PORT}`,
|
|
260
|
-
defaultViewport: null,
|
|
261
|
-
targetFilter
|
|
262
|
-
}),
|
|
263
|
-
1e4,
|
|
264
|
-
"puppeteer.connect to 127.0.0.1 after launch timed out"
|
|
265
|
-
);
|
|
266
|
-
console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
|
|
267
|
-
return browser2;
|
|
268
|
-
} catch (e) {
|
|
269
|
-
const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
|
|
270
|
-
if (!isLocalhostReady) {
|
|
271
|
-
throw new Error("localhost CDP port not responding after launch");
|
|
272
|
-
}
|
|
273
|
-
console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
274
|
-
const browser2 = await promiseWithTimeout(
|
|
275
|
-
import_puppeteer_core.default.connect({
|
|
276
|
-
browserURL: `http://localhost:${CDP_PORT}`,
|
|
277
|
-
defaultViewport: null,
|
|
278
|
-
targetFilter
|
|
279
|
-
}),
|
|
280
|
-
1e4,
|
|
281
|
-
"puppeteer.connect to localhost after launch timed out"
|
|
282
|
-
);
|
|
283
|
-
console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
|
|
284
|
-
return browser2;
|
|
285
|
-
}
|
|
286
|
-
} catch (launchError) {
|
|
287
|
-
const msg = launchError instanceof Error ? launchError.message : String(launchError);
|
|
288
|
-
console.error(import_picocolors.default.red(`\u65E0\u6CD5\u8FDE\u63A5\u6216\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
289
|
-
console.error(import_picocolors.default.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`));
|
|
290
|
-
console.error(import_picocolors.default.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`));
|
|
291
|
-
throw new Error("Browser connection failed.");
|
|
292
|
-
}
|
|
227
|
+
} catch {
|
|
293
228
|
}
|
|
294
229
|
}
|
|
230
|
+
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`);
|
|
295
231
|
}
|
|
296
232
|
async function getPageTargetId(page2) {
|
|
297
233
|
const session = await page2.target().createCDPSession();
|
|
@@ -369,14 +305,12 @@ async function getTargetPage(browser2, tabid2) {
|
|
|
369
305
|
let activeTargetId = null;
|
|
370
306
|
for (const url of urls) {
|
|
371
307
|
try {
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
308
|
+
const body = await httpGet(url, 1e3);
|
|
309
|
+
const targetsData = JSON.parse(body);
|
|
310
|
+
const active = targetsData.find((t) => t.type === "page" && !t.url.startsWith("devtools://"));
|
|
311
|
+
if (active) {
|
|
312
|
+
activeTargetId = active.id;
|
|
313
|
+
break;
|
|
380
314
|
}
|
|
381
315
|
} catch {
|
|
382
316
|
}
|
|
@@ -422,7 +356,7 @@ async function injectWebMCPPolyfillAndTools(page2, force = false) {
|
|
|
422
356
|
const msg = err instanceof Error ? err.message : String(err);
|
|
423
357
|
throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
|
|
424
358
|
}
|
|
425
|
-
await new Promise((
|
|
359
|
+
await new Promise((resolve2) => setTimeout(resolve2, 300));
|
|
426
360
|
}
|
|
427
361
|
await injectDomainTools(page2);
|
|
428
362
|
}
|
|
@@ -510,7 +444,7 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
510
444
|
if (pUrl.startsWith("devtools://")) return null;
|
|
511
445
|
const pTitle = await Promise.race([
|
|
512
446
|
p.title().catch(() => "Unknown"),
|
|
513
|
-
new Promise((
|
|
447
|
+
new Promise((resolve2) => setTimeout(() => resolve2("Unknown"), 500))
|
|
514
448
|
]);
|
|
515
449
|
return {
|
|
516
450
|
// 使用真实的 Chrome target ID,而非数组下标
|
|
@@ -530,6 +464,7 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
530
464
|
}
|
|
531
465
|
|
|
532
466
|
// src/commands/run.ts
|
|
467
|
+
var import_json5 = __toESM(require("json5"), 1);
|
|
533
468
|
async function runCommand({
|
|
534
469
|
toolName,
|
|
535
470
|
argsJson,
|
|
@@ -538,10 +473,15 @@ async function runCommand({
|
|
|
538
473
|
const browser2 = await connectBrowser();
|
|
539
474
|
try {
|
|
540
475
|
const page2 = await getTargetPage(browser2, tabid2);
|
|
476
|
+
let cleanedArgs = argsJson.trim();
|
|
477
|
+
if (cleanedArgs.startsWith("'") && cleanedArgs.endsWith("'")) {
|
|
478
|
+
cleanedArgs = cleanedArgs.slice(1, -1).trim();
|
|
479
|
+
}
|
|
541
480
|
try {
|
|
542
|
-
|
|
481
|
+
const obj = import_json5.default.parse(cleanedArgs);
|
|
482
|
+
cleanedArgs = JSON.stringify(obj);
|
|
543
483
|
} catch (e) {
|
|
544
|
-
throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
|
|
484
|
+
throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${e.message}`);
|
|
545
485
|
}
|
|
546
486
|
const result2 = await page2.evaluate(async (name, inputString) => {
|
|
547
487
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
@@ -555,8 +495,7 @@ async function runCommand({
|
|
|
555
495
|
} catch (e) {
|
|
556
496
|
}
|
|
557
497
|
}
|
|
558
|
-
|
|
559
|
-
}, toolName, argsJson);
|
|
498
|
+
}, toolName, cleanedArgs);
|
|
560
499
|
return result2;
|
|
561
500
|
} finally {
|
|
562
501
|
await browser2.disconnect();
|
|
@@ -616,7 +555,7 @@ async function tabsOpenCommand(url) {
|
|
|
616
555
|
url = normalizeUrl(url);
|
|
617
556
|
await page2.goto(url, { waitUntil: "domcontentloaded" });
|
|
618
557
|
await injectIntoPage(page2);
|
|
619
|
-
await new Promise((
|
|
558
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
620
559
|
return {
|
|
621
560
|
success: true,
|
|
622
561
|
tabid: await getPageTargetId(page2),
|
|
@@ -717,7 +656,7 @@ async function tabsForwardCommand(tabid2) {
|
|
|
717
656
|
// package.json
|
|
718
657
|
var package_default = {
|
|
719
658
|
name: "@opentiny/webmcp-cli",
|
|
720
|
-
version: "0.0.1-alpha.
|
|
659
|
+
version: "0.0.1-alpha.5",
|
|
721
660
|
type: "module",
|
|
722
661
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
723
662
|
main: "dist/index.js",
|
|
@@ -756,6 +695,7 @@ var package_default = {
|
|
|
756
695
|
"@page-agent/page-controller": "catalog:",
|
|
757
696
|
clipboardy: "^5.3.1",
|
|
758
697
|
commander: "^11.1.0",
|
|
698
|
+
json5: "^2.2.3",
|
|
759
699
|
picocolors: "^1.0.0",
|
|
760
700
|
"puppeteer-core": "^22.0.0"
|
|
761
701
|
},
|
|
@@ -770,6 +710,13 @@ var package_default = {
|
|
|
770
710
|
|
|
771
711
|
// src/bin.ts
|
|
772
712
|
var program = new import_commander.Command();
|
|
713
|
+
function cleanJsonString(str) {
|
|
714
|
+
let cleaned = str.trim();
|
|
715
|
+
if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
|
|
716
|
+
cleaned = cleaned.slice(1, -1).trim();
|
|
717
|
+
}
|
|
718
|
+
return cleaned;
|
|
719
|
+
}
|
|
773
720
|
function parseTabId(id) {
|
|
774
721
|
if (!id) return void 0;
|
|
775
722
|
return id;
|
|
@@ -779,6 +726,19 @@ function handleCommandError(error, commandName) {
|
|
|
779
726
|
console.error(import_picocolors2.default.red(`Error executing ${commandName} command: ${msg}`));
|
|
780
727
|
process.exit(1);
|
|
781
728
|
}
|
|
729
|
+
function expandFileRefs(argsJson) {
|
|
730
|
+
return argsJson.replace(/"@(base64)?file:([^"]+)"/g, (_match, base64Flag, filePath) => {
|
|
731
|
+
const absPath = (0, import_path2.resolve)(process.cwd(), filePath);
|
|
732
|
+
let content;
|
|
733
|
+
try {
|
|
734
|
+
content = (0, import_fs2.readFileSync)(absPath, "utf-8");
|
|
735
|
+
} catch (e) {
|
|
736
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}": ${e.message}`);
|
|
737
|
+
}
|
|
738
|
+
const value = base64Flag ? Buffer.from(content, "utf-8").toString("base64") : content;
|
|
739
|
+
return JSON.stringify(value);
|
|
740
|
+
});
|
|
741
|
+
}
|
|
782
742
|
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) => {
|
|
783
743
|
const opts = thisCommand.opts();
|
|
784
744
|
if (opts.workspace) {
|
|
@@ -793,11 +753,54 @@ program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D
|
|
|
793
753
|
handleCommandError(error, "state");
|
|
794
754
|
}
|
|
795
755
|
});
|
|
796
|
-
program.command("run <toolName>
|
|
756
|
+
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(
|
|
757
|
+
"-f, --file <path>",
|
|
758
|
+
`\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A\u53C2\u6570\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
|
|
759
|
+
\u5982\u9700\u5728\u53C2\u6570\u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
|
|
760
|
+
@file:<path> \u8BFB\u53D6\u6587\u4EF6\u539F\u59CB\u6587\u672C
|
|
761
|
+
@base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
|
|
762
|
+
\u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
|
|
763
|
+
).action(async (toolName, args, options) => {
|
|
797
764
|
try {
|
|
765
|
+
let finalArgsJson = "";
|
|
766
|
+
if (options.file) {
|
|
767
|
+
const filePath = (0, import_path2.resolve)(process.cwd(), options.file);
|
|
768
|
+
try {
|
|
769
|
+
finalArgsJson = (0, import_fs2.readFileSync)(filePath, "utf-8").trim();
|
|
770
|
+
} catch (e) {
|
|
771
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
|
|
772
|
+
}
|
|
773
|
+
} else if (args && args.length > 0) {
|
|
774
|
+
const firstArg = args[0].trim();
|
|
775
|
+
const cleanedFirstArg = cleanJsonString(firstArg);
|
|
776
|
+
if (cleanedFirstArg.startsWith("{") && cleanedFirstArg.endsWith("}")) {
|
|
777
|
+
finalArgsJson = args.map(cleanJsonString).join(" ");
|
|
778
|
+
} else {
|
|
779
|
+
const obj = {};
|
|
780
|
+
for (const pair of args) {
|
|
781
|
+
const index = pair.indexOf("=");
|
|
782
|
+
if (index === -1) {
|
|
783
|
+
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`);
|
|
784
|
+
}
|
|
785
|
+
const key = pair.substring(0, index).trim();
|
|
786
|
+
let val = pair.substring(index + 1).trim();
|
|
787
|
+
val = cleanJsonString(val);
|
|
788
|
+
try {
|
|
789
|
+
obj[key] = JSON.parse(val);
|
|
790
|
+
} catch {
|
|
791
|
+
obj[key] = val;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
finalArgsJson = JSON.stringify(obj);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (!finalArgsJson) {
|
|
798
|
+
throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
|
|
799
|
+
}
|
|
800
|
+
finalArgsJson = expandFileRefs(finalArgsJson);
|
|
798
801
|
const result2 = await runCommand({
|
|
799
802
|
toolName,
|
|
800
|
-
argsJson,
|
|
803
|
+
argsJson: finalArgsJson,
|
|
801
804
|
tabid: parseTabId(options.tabid)
|
|
802
805
|
});
|
|
803
806
|
console.log(JSON.stringify(result2, null, 2));
|