@opentiny/webmcp-cli 0.0.1-alpha.2 → 0.0.1-alpha.3
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 +230 -145
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +230 -145
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +60 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +60 -31
- 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.cjs
CHANGED
|
@@ -112,31 +112,54 @@ async function killProcessOnPortIfZombie(port) {
|
|
|
112
112
|
} catch (e) {
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
function
|
|
115
|
+
function getDefaultBrowserPath() {
|
|
116
116
|
const platform = import_os.default.platform();
|
|
117
117
|
if (platform === "darwin") {
|
|
118
|
-
|
|
118
|
+
const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
119
|
+
if (import_fs.default.existsSync(chromePath)) return { path: chromePath, name: "Chrome" };
|
|
120
|
+
const edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
|
|
121
|
+
if (import_fs.default.existsSync(edgePath)) return { path: edgePath, name: "Edge" };
|
|
122
|
+
return null;
|
|
119
123
|
} else if (platform === "win32") {
|
|
120
|
-
const
|
|
124
|
+
const chromePaths = [
|
|
121
125
|
process.env.LOCALAPPDATA + "\\Google\\Chrome\\Application\\chrome.exe",
|
|
122
126
|
process.env.PROGRAMFILES + "\\Google\\Chrome\\Application\\chrome.exe",
|
|
123
127
|
process.env["PROGRAMFILES(X86)"] + "\\Google\\Chrome\\Application\\chrome.exe"
|
|
124
128
|
];
|
|
125
|
-
|
|
129
|
+
const foundChrome = chromePaths.find((p) => import_fs.default.existsSync(p));
|
|
130
|
+
if (foundChrome) return { path: foundChrome, name: "Chrome" };
|
|
131
|
+
const edgePaths = [
|
|
132
|
+
process.env.LOCALAPPDATA + "\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
133
|
+
process.env.PROGRAMFILES + "\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
134
|
+
process.env["PROGRAMFILES(X86)"] + "\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
135
|
+
"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
136
|
+
"C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe"
|
|
137
|
+
];
|
|
138
|
+
const foundEdge = edgePaths.find((p) => import_fs.default.existsSync(p));
|
|
139
|
+
if (foundEdge) return { path: foundEdge, name: "Edge" };
|
|
140
|
+
return null;
|
|
126
141
|
} else {
|
|
127
|
-
const paths = [
|
|
128
|
-
|
|
142
|
+
const paths = [
|
|
143
|
+
{ p: "/usr/bin/google-chrome", name: "Chrome" },
|
|
144
|
+
{ p: "/usr/bin/google-chrome-stable", name: "Chrome" },
|
|
145
|
+
{ p: "/usr/bin/chromium", name: "Chromium" },
|
|
146
|
+
{ p: "/usr/bin/chromium-browser", name: "Chromium" },
|
|
147
|
+
{ p: "/usr/bin/microsoft-edge", name: "Edge" },
|
|
148
|
+
{ p: "/usr/bin/microsoft-edge-stable", name: "Edge" }
|
|
149
|
+
];
|
|
150
|
+
const found = paths.find(({ p }) => import_fs.default.existsSync(p));
|
|
151
|
+
return found ? { path: found.p, name: found.name } : null;
|
|
129
152
|
}
|
|
130
153
|
}
|
|
131
|
-
async function
|
|
132
|
-
const
|
|
133
|
-
if (!
|
|
134
|
-
throw new Error("\u65E0\u6CD5\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 Chrome \u6D4F\u89C8\u5668\u7684\u9ED8\u8BA4\u5B89\u88C5\u8DEF\u5F84\u3002");
|
|
154
|
+
async function startBrowserInBackground() {
|
|
155
|
+
const browserInfo = getDefaultBrowserPath();
|
|
156
|
+
if (!browserInfo || !import_fs.default.existsSync(browserInfo.path)) {
|
|
157
|
+
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");
|
|
135
158
|
}
|
|
136
|
-
console.log(import_picocolors.default.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0
|
|
159
|
+
console.log(import_picocolors.default.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
|
|
137
160
|
const userDataDir = process.env.WEBMCP_WORKSPACE || import_path.default.join(import_os.default.homedir(), ".webmcp_chrome_profile");
|
|
138
161
|
const child = (0, import_child_process.spawn)(
|
|
139
|
-
|
|
162
|
+
browserInfo.path,
|
|
140
163
|
[
|
|
141
164
|
`--remote-debugging-port=${CDP_PORT}`,
|
|
142
165
|
`--user-data-dir=${userDataDir}`,
|
|
@@ -156,7 +179,7 @@ async function startChromeInBackground() {
|
|
|
156
179
|
try {
|
|
157
180
|
const response = await fetchWithTimeout(url, 1e3);
|
|
158
181
|
if (response.ok) {
|
|
159
|
-
console.log(import_picocolors.default.green(
|
|
182
|
+
console.log(import_picocolors.default.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
|
|
160
183
|
return;
|
|
161
184
|
}
|
|
162
185
|
} catch (err) {
|
|
@@ -166,7 +189,7 @@ async function startChromeInBackground() {
|
|
|
166
189
|
}
|
|
167
190
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
168
191
|
}
|
|
169
|
-
throw new Error(
|
|
192
|
+
throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
|
|
170
193
|
}
|
|
171
194
|
async function connectBrowser() {
|
|
172
195
|
const targetFilter = (target) => {
|
|
@@ -191,7 +214,7 @@ async function connectBrowser() {
|
|
|
191
214
|
throw new Error("127.0.0.1 CDP port not responding");
|
|
192
215
|
}
|
|
193
216
|
console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
|
|
194
|
-
const
|
|
217
|
+
const browser2 = await promiseWithTimeout(
|
|
195
218
|
import_puppeteer_core.default.connect({
|
|
196
219
|
browserURL: `http://127.0.0.1:${CDP_PORT}`,
|
|
197
220
|
defaultViewport: null,
|
|
@@ -201,7 +224,7 @@ async function connectBrowser() {
|
|
|
201
224
|
"puppeteer.connect to 127.0.0.1 timed out"
|
|
202
225
|
);
|
|
203
226
|
console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
|
|
204
|
-
return
|
|
227
|
+
return browser2;
|
|
205
228
|
} catch (error) {
|
|
206
229
|
try {
|
|
207
230
|
const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
|
|
@@ -209,7 +232,7 @@ async function connectBrowser() {
|
|
|
209
232
|
throw new Error("localhost CDP port not responding");
|
|
210
233
|
}
|
|
211
234
|
console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
212
|
-
const
|
|
235
|
+
const browser2 = await promiseWithTimeout(
|
|
213
236
|
import_puppeteer_core.default.connect({
|
|
214
237
|
browserURL: `http://localhost:${CDP_PORT}`,
|
|
215
238
|
defaultViewport: null,
|
|
@@ -219,19 +242,19 @@ async function connectBrowser() {
|
|
|
219
242
|
"puppeteer.connect to localhost timed out"
|
|
220
243
|
);
|
|
221
244
|
console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
|
|
222
|
-
return
|
|
245
|
+
return browser2;
|
|
223
246
|
} catch (error2) {
|
|
224
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)}`));
|
|
225
248
|
try {
|
|
226
249
|
await killProcessOnPortIfZombie(CDP_PORT);
|
|
227
|
-
await
|
|
250
|
+
await startBrowserInBackground();
|
|
228
251
|
try {
|
|
229
252
|
const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
|
|
230
253
|
if (!is127Ready) {
|
|
231
254
|
throw new Error("127.0.0.1 CDP port not responding after launch");
|
|
232
255
|
}
|
|
233
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..."));
|
|
234
|
-
const
|
|
257
|
+
const browser2 = await promiseWithTimeout(
|
|
235
258
|
import_puppeteer_core.default.connect({
|
|
236
259
|
browserURL: `http://127.0.0.1:${CDP_PORT}`,
|
|
237
260
|
defaultViewport: null,
|
|
@@ -241,14 +264,14 @@ async function connectBrowser() {
|
|
|
241
264
|
"puppeteer.connect to 127.0.0.1 after launch timed out"
|
|
242
265
|
);
|
|
243
266
|
console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
|
|
244
|
-
return
|
|
267
|
+
return browser2;
|
|
245
268
|
} catch (e) {
|
|
246
269
|
const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
|
|
247
270
|
if (!isLocalhostReady) {
|
|
248
271
|
throw new Error("localhost CDP port not responding after launch");
|
|
249
272
|
}
|
|
250
273
|
console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
|
|
251
|
-
const
|
|
274
|
+
const browser2 = await promiseWithTimeout(
|
|
252
275
|
import_puppeteer_core.default.connect({
|
|
253
276
|
browserURL: `http://localhost:${CDP_PORT}`,
|
|
254
277
|
defaultViewport: null,
|
|
@@ -258,20 +281,20 @@ async function connectBrowser() {
|
|
|
258
281
|
"puppeteer.connect to localhost after launch timed out"
|
|
259
282
|
);
|
|
260
283
|
console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
|
|
261
|
-
return
|
|
284
|
+
return browser2;
|
|
262
285
|
}
|
|
263
286
|
} catch (launchError) {
|
|
264
287
|
const msg = launchError instanceof Error ? launchError.message : String(launchError);
|
|
265
288
|
console.error(import_picocolors.default.red(`\u65E0\u6CD5\u8FDE\u63A5\u6216\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
266
|
-
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
|
|
267
|
-
console.error(import_picocolors.default.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`));
|
|
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`));
|
|
268
291
|
throw new Error("Browser connection failed.");
|
|
269
292
|
}
|
|
270
293
|
}
|
|
271
294
|
}
|
|
272
295
|
}
|
|
273
|
-
async function getPageTargetId(
|
|
274
|
-
const session = await
|
|
296
|
+
async function getPageTargetId(page2) {
|
|
297
|
+
const session = await page2.target().createCDPSession();
|
|
275
298
|
try {
|
|
276
299
|
const { targetInfo } = await session.send("Target.getTargetInfo");
|
|
277
300
|
return targetInfo.targetId;
|
|
@@ -280,8 +303,8 @@ async function getPageTargetId(page) {
|
|
|
280
303
|
});
|
|
281
304
|
}
|
|
282
305
|
}
|
|
283
|
-
function getPageTargets(
|
|
284
|
-
return
|
|
306
|
+
function getPageTargets(browser2) {
|
|
307
|
+
return browser2.targets().filter((t) => {
|
|
285
308
|
try {
|
|
286
309
|
const type = (typeof t.type === "function" ? t.type() : t.type) || "";
|
|
287
310
|
const url = (typeof t.url === "function" ? t.url() : t.url) || "";
|
|
@@ -294,26 +317,26 @@ function getPageTargets(browser) {
|
|
|
294
317
|
function getTargetIdFromTarget(target) {
|
|
295
318
|
return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
|
|
296
319
|
}
|
|
297
|
-
function findPageTargetByTabId(
|
|
298
|
-
const pageTargets = getPageTargets(
|
|
320
|
+
function findPageTargetByTabId(browser2, tabid2) {
|
|
321
|
+
const pageTargets = getPageTargets(browser2);
|
|
299
322
|
return pageTargets.find((t) => {
|
|
300
323
|
const tid = getTargetIdFromTarget(t);
|
|
301
|
-
return tid ===
|
|
324
|
+
return tid === tabid2 || tid.includes(tabid2);
|
|
302
325
|
}) ?? null;
|
|
303
326
|
}
|
|
304
|
-
async function activateTabById(
|
|
305
|
-
const target = findPageTargetByTabId(
|
|
327
|
+
async function activateTabById(browser2, tabid2) {
|
|
328
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
306
329
|
if (!target) {
|
|
307
|
-
throw new Error(`Tab with targetId "${
|
|
330
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
308
331
|
}
|
|
309
332
|
const realTabId = getTargetIdFromTarget(target);
|
|
310
|
-
const pages = await
|
|
333
|
+
const pages = await browser2.pages();
|
|
311
334
|
let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
|
|
312
335
|
if (!sessionPage && pages.length > 0) {
|
|
313
336
|
sessionPage = pages[0];
|
|
314
337
|
}
|
|
315
338
|
if (!sessionPage) {
|
|
316
|
-
sessionPage = await
|
|
339
|
+
sessionPage = await browser2.newPage();
|
|
317
340
|
}
|
|
318
341
|
const session = await sessionPage.createCDPSession();
|
|
319
342
|
try {
|
|
@@ -323,18 +346,18 @@ async function activateTabById(browser, tabid) {
|
|
|
323
346
|
});
|
|
324
347
|
}
|
|
325
348
|
}
|
|
326
|
-
async function getTargetPage(
|
|
327
|
-
const pageTargets = getPageTargets(
|
|
349
|
+
async function getTargetPage(browser2, tabid2) {
|
|
350
|
+
const pageTargets = getPageTargets(browser2);
|
|
328
351
|
if (pageTargets.length === 0) {
|
|
329
|
-
const newPage = await
|
|
352
|
+
const newPage = await browser2.newPage();
|
|
330
353
|
await injectWebMCPPolyfillAndTools(newPage);
|
|
331
354
|
return newPage;
|
|
332
355
|
}
|
|
333
356
|
let targetPage = null;
|
|
334
|
-
if (
|
|
335
|
-
const target = findPageTargetByTabId(
|
|
357
|
+
if (tabid2 !== void 0) {
|
|
358
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
336
359
|
if (!target) {
|
|
337
|
-
throw new Error(`Tab with targetId "${
|
|
360
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
338
361
|
}
|
|
339
362
|
targetPage = await target.page();
|
|
340
363
|
} else {
|
|
@@ -379,11 +402,11 @@ async function getTargetPage(browser, tabid) {
|
|
|
379
402
|
await injectWebMCPPolyfillAndTools(targetPage);
|
|
380
403
|
return targetPage;
|
|
381
404
|
}
|
|
382
|
-
async function injectIntoPage(
|
|
383
|
-
await injectWebMCPPolyfillAndTools(
|
|
405
|
+
async function injectIntoPage(page2) {
|
|
406
|
+
await injectWebMCPPolyfillAndTools(page2, true);
|
|
384
407
|
}
|
|
385
|
-
async function injectWebMCPPolyfillAndTools(
|
|
386
|
-
const polyfillReady = !force && await
|
|
408
|
+
async function injectWebMCPPolyfillAndTools(page2, force = false) {
|
|
409
|
+
const polyfillReady = !force && await page2.evaluate(() => {
|
|
387
410
|
return !!window.__webmcpcli_init;
|
|
388
411
|
}).catch(() => false);
|
|
389
412
|
if (!polyfillReady) {
|
|
@@ -394,19 +417,19 @@ async function injectWebMCPPolyfillAndTools(page, force = false) {
|
|
|
394
417
|
}
|
|
395
418
|
const scriptContent = import_fs.default.readFileSync(injectScriptPath, "utf-8");
|
|
396
419
|
try {
|
|
397
|
-
await
|
|
420
|
+
await page2.evaluate(scriptContent);
|
|
398
421
|
} catch (err) {
|
|
399
422
|
const msg = err instanceof Error ? err.message : String(err);
|
|
400
423
|
throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
|
|
401
424
|
}
|
|
402
425
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
403
426
|
}
|
|
404
|
-
await injectDomainTools(
|
|
427
|
+
await injectDomainTools(page2);
|
|
405
428
|
}
|
|
406
|
-
async function injectDomainTools(
|
|
429
|
+
async function injectDomainTools(page2) {
|
|
407
430
|
let hostname;
|
|
408
431
|
try {
|
|
409
|
-
const url = new URL(
|
|
432
|
+
const url = new URL(page2.url());
|
|
410
433
|
hostname = url.hostname;
|
|
411
434
|
} catch {
|
|
412
435
|
return;
|
|
@@ -418,7 +441,7 @@ async function injectDomainTools(page) {
|
|
|
418
441
|
console.log(import_picocolors.default.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
|
|
419
442
|
const toolScript = import_fs.default.readFileSync(toolBundlePath, "utf-8");
|
|
420
443
|
try {
|
|
421
|
-
await
|
|
444
|
+
await page2.evaluate(toolScript);
|
|
422
445
|
console.log(import_picocolors.default.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
|
|
423
446
|
} catch (err) {
|
|
424
447
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -427,11 +450,12 @@ async function injectDomainTools(page) {
|
|
|
427
450
|
}
|
|
428
451
|
|
|
429
452
|
// src/commands/state.ts
|
|
430
|
-
async function stateCommand({ tabid }) {
|
|
431
|
-
const
|
|
453
|
+
async function stateCommand({ tabid: tabid2 }) {
|
|
454
|
+
const browser2 = await connectBrowser();
|
|
432
455
|
try {
|
|
433
|
-
const
|
|
434
|
-
|
|
456
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
457
|
+
await injectIntoPage(page2);
|
|
458
|
+
const state = await page2.evaluate(async () => {
|
|
435
459
|
const url = document.URL;
|
|
436
460
|
const title = document.title;
|
|
437
461
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
@@ -479,27 +503,29 @@ async function stateCommand({ tabid }) {
|
|
|
479
503
|
webmcpTools
|
|
480
504
|
};
|
|
481
505
|
});
|
|
482
|
-
const pages = await
|
|
483
|
-
const tabs2 = await Promise.all(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
506
|
+
const pages = await browser2.pages();
|
|
507
|
+
const tabs2 = await Promise.all(
|
|
508
|
+
pages.map(async (p) => {
|
|
509
|
+
const pUrl = p.url();
|
|
510
|
+
if (pUrl.startsWith("devtools://")) return null;
|
|
511
|
+
const pTitle = await Promise.race([
|
|
512
|
+
p.title().catch(() => "Unknown"),
|
|
513
|
+
new Promise((resolve) => setTimeout(() => resolve("Unknown"), 500))
|
|
514
|
+
]);
|
|
515
|
+
return {
|
|
516
|
+
// 使用真实的 Chrome target ID,而非数组下标
|
|
517
|
+
tabid: await getPageTargetId(p).catch(() => pUrl),
|
|
518
|
+
title: pTitle,
|
|
519
|
+
url: pUrl
|
|
520
|
+
};
|
|
521
|
+
})
|
|
522
|
+
);
|
|
497
523
|
return {
|
|
498
524
|
...state,
|
|
499
525
|
tabs: tabs2.filter(Boolean)
|
|
500
526
|
};
|
|
501
527
|
} finally {
|
|
502
|
-
await
|
|
528
|
+
await browser2.disconnect();
|
|
503
529
|
}
|
|
504
530
|
}
|
|
505
531
|
|
|
@@ -507,17 +533,17 @@ async function stateCommand({ tabid }) {
|
|
|
507
533
|
async function runCommand({
|
|
508
534
|
toolName,
|
|
509
535
|
argsJson,
|
|
510
|
-
tabid
|
|
536
|
+
tabid: tabid2
|
|
511
537
|
}) {
|
|
512
|
-
const
|
|
538
|
+
const browser2 = await connectBrowser();
|
|
513
539
|
try {
|
|
514
|
-
const
|
|
540
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
515
541
|
try {
|
|
516
542
|
JSON.parse(argsJson);
|
|
517
543
|
} catch (e) {
|
|
518
544
|
throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
|
|
519
545
|
}
|
|
520
|
-
const
|
|
546
|
+
const result2 = await page2.evaluate(async (name, inputString) => {
|
|
521
547
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
522
548
|
if (!mcp || typeof mcp.executeTool !== "function") {
|
|
523
549
|
throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
|
|
@@ -531,12 +557,51 @@ async function runCommand({
|
|
|
531
557
|
}
|
|
532
558
|
return res;
|
|
533
559
|
}, toolName, argsJson);
|
|
560
|
+
return result2;
|
|
561
|
+
} finally {
|
|
562
|
+
await browser2.disconnect();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// src/commands/evaluate.ts
|
|
567
|
+
async function evaluateCommand({
|
|
568
|
+
jsScript,
|
|
569
|
+
tabid
|
|
570
|
+
}) {
|
|
571
|
+
const browser = await connectBrowser();
|
|
572
|
+
try {
|
|
573
|
+
const page = await getTargetPage(browser, tabid);
|
|
574
|
+
const result = await page.evaluate(async (script) => {
|
|
575
|
+
try {
|
|
576
|
+
const result = eval(script);
|
|
577
|
+
if (result instanceof Promise) {
|
|
578
|
+
return await result;
|
|
579
|
+
}
|
|
580
|
+
return result;
|
|
581
|
+
} catch (error) {
|
|
582
|
+
return {
|
|
583
|
+
error: true,
|
|
584
|
+
message: error.message,
|
|
585
|
+
stack: error.stack
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
}, jsScript);
|
|
534
589
|
return result;
|
|
535
590
|
} finally {
|
|
536
591
|
await browser.disconnect();
|
|
537
592
|
}
|
|
538
593
|
}
|
|
539
594
|
|
|
595
|
+
// src/commands/clipboard.ts
|
|
596
|
+
var import_clipboardy = __toESM(require("clipboardy"), 1);
|
|
597
|
+
async function setClipboard(content) {
|
|
598
|
+
try {
|
|
599
|
+
await import_clipboardy.default.write(content);
|
|
600
|
+
} catch (error) {
|
|
601
|
+
throw new Error(`\u8BBE\u7F6E\u526A\u8D34\u677F\u5931\u8D25: ${error.message}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
540
605
|
// src/commands/tabs.ts
|
|
541
606
|
function normalizeUrl(url) {
|
|
542
607
|
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
@@ -545,114 +610,114 @@ function normalizeUrl(url) {
|
|
|
545
610
|
return url;
|
|
546
611
|
}
|
|
547
612
|
async function tabsOpenCommand(url) {
|
|
548
|
-
const
|
|
613
|
+
const browser2 = await connectBrowser();
|
|
549
614
|
try {
|
|
550
|
-
const
|
|
615
|
+
const page2 = await browser2.newPage();
|
|
551
616
|
url = normalizeUrl(url);
|
|
552
|
-
await
|
|
553
|
-
await injectIntoPage(
|
|
617
|
+
await page2.goto(url, { waitUntil: "domcontentloaded" });
|
|
618
|
+
await injectIntoPage(page2);
|
|
554
619
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
555
620
|
return {
|
|
556
621
|
success: true,
|
|
557
|
-
tabid: await getPageTargetId(
|
|
558
|
-
url:
|
|
559
|
-
title: await
|
|
622
|
+
tabid: await getPageTargetId(page2),
|
|
623
|
+
url: page2.url(),
|
|
624
|
+
title: await page2.title()
|
|
560
625
|
};
|
|
561
626
|
} finally {
|
|
562
|
-
await
|
|
627
|
+
await browser2.disconnect();
|
|
563
628
|
}
|
|
564
629
|
}
|
|
565
|
-
async function tabsCloseCommand(
|
|
566
|
-
const
|
|
630
|
+
async function tabsCloseCommand(tabid2) {
|
|
631
|
+
const browser2 = await connectBrowser();
|
|
567
632
|
try {
|
|
568
|
-
const target = findPageTargetByTabId(
|
|
633
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
569
634
|
if (!target) {
|
|
570
|
-
throw new Error(`Tab with targetId "${
|
|
635
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
571
636
|
}
|
|
572
|
-
const
|
|
573
|
-
if (!
|
|
574
|
-
throw new Error(`Tab with targetId "${
|
|
637
|
+
const page2 = await target.page();
|
|
638
|
+
if (!page2) {
|
|
639
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
575
640
|
}
|
|
576
|
-
const closedTabid = await getPageTargetId(
|
|
577
|
-
await
|
|
641
|
+
const closedTabid = await getPageTargetId(page2).catch(() => tabid2);
|
|
642
|
+
await page2.close();
|
|
578
643
|
return {
|
|
579
644
|
success: true,
|
|
580
645
|
tabid: closedTabid
|
|
581
646
|
};
|
|
582
647
|
} finally {
|
|
583
|
-
await
|
|
648
|
+
await browser2.disconnect();
|
|
584
649
|
}
|
|
585
650
|
}
|
|
586
|
-
async function tabsSwitchCommand(
|
|
587
|
-
const
|
|
651
|
+
async function tabsSwitchCommand(tabid2) {
|
|
652
|
+
const browser2 = await connectBrowser();
|
|
588
653
|
try {
|
|
589
|
-
await activateTabById(
|
|
590
|
-
const
|
|
654
|
+
await activateTabById(browser2, tabid2);
|
|
655
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
591
656
|
return {
|
|
592
657
|
success: true,
|
|
593
|
-
tabid: await getPageTargetId(
|
|
594
|
-
url:
|
|
595
|
-
title: await
|
|
658
|
+
tabid: await getPageTargetId(page2),
|
|
659
|
+
url: page2.url(),
|
|
660
|
+
title: await page2.title()
|
|
596
661
|
};
|
|
597
662
|
} finally {
|
|
598
|
-
await
|
|
663
|
+
await browser2.disconnect();
|
|
599
664
|
}
|
|
600
665
|
}
|
|
601
|
-
async function tabsBackCommand(
|
|
602
|
-
const
|
|
666
|
+
async function tabsBackCommand(tabid2) {
|
|
667
|
+
const browser2 = await connectBrowser();
|
|
603
668
|
try {
|
|
604
|
-
const
|
|
605
|
-
const response = await
|
|
669
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
670
|
+
const response = await page2.goBack();
|
|
606
671
|
if (!response) {
|
|
607
672
|
return {
|
|
608
673
|
success: false,
|
|
609
674
|
error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
|
|
610
|
-
tabid: await getPageTargetId(
|
|
611
|
-
url:
|
|
612
|
-
title: await
|
|
675
|
+
tabid: await getPageTargetId(page2).catch(() => tabid2),
|
|
676
|
+
url: page2.url(),
|
|
677
|
+
title: await page2.title()
|
|
613
678
|
};
|
|
614
679
|
}
|
|
615
|
-
await injectIntoPage(
|
|
680
|
+
await injectIntoPage(page2);
|
|
616
681
|
return {
|
|
617
682
|
success: true,
|
|
618
|
-
tabid: await getPageTargetId(
|
|
619
|
-
url:
|
|
620
|
-
title: await
|
|
683
|
+
tabid: await getPageTargetId(page2),
|
|
684
|
+
url: page2.url(),
|
|
685
|
+
title: await page2.title()
|
|
621
686
|
};
|
|
622
687
|
} finally {
|
|
623
|
-
await
|
|
688
|
+
await browser2.disconnect();
|
|
624
689
|
}
|
|
625
690
|
}
|
|
626
|
-
async function tabsForwardCommand(
|
|
627
|
-
const
|
|
691
|
+
async function tabsForwardCommand(tabid2) {
|
|
692
|
+
const browser2 = await connectBrowser();
|
|
628
693
|
try {
|
|
629
|
-
const
|
|
630
|
-
const response = await
|
|
694
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
695
|
+
const response = await page2.goForward();
|
|
631
696
|
if (!response) {
|
|
632
697
|
return {
|
|
633
698
|
success: false,
|
|
634
699
|
error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
|
|
635
|
-
tabid: await getPageTargetId(
|
|
636
|
-
url:
|
|
637
|
-
title: await
|
|
700
|
+
tabid: await getPageTargetId(page2).catch(() => tabid2),
|
|
701
|
+
url: page2.url(),
|
|
702
|
+
title: await page2.title()
|
|
638
703
|
};
|
|
639
704
|
}
|
|
640
|
-
await injectIntoPage(
|
|
705
|
+
await injectIntoPage(page2);
|
|
641
706
|
return {
|
|
642
707
|
success: true,
|
|
643
|
-
tabid: await getPageTargetId(
|
|
644
|
-
url:
|
|
645
|
-
title: await
|
|
708
|
+
tabid: await getPageTargetId(page2),
|
|
709
|
+
url: page2.url(),
|
|
710
|
+
title: await page2.title()
|
|
646
711
|
};
|
|
647
712
|
} finally {
|
|
648
|
-
await
|
|
713
|
+
await browser2.disconnect();
|
|
649
714
|
}
|
|
650
715
|
}
|
|
651
716
|
|
|
652
717
|
// package.json
|
|
653
718
|
var package_default = {
|
|
654
719
|
name: "@opentiny/webmcp-cli",
|
|
655
|
-
version: "0.0.1-alpha.
|
|
720
|
+
version: "0.0.1-alpha.3",
|
|
656
721
|
type: "module",
|
|
657
722
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
658
723
|
main: "dist/index.js",
|
|
@@ -689,6 +754,7 @@ var package_default = {
|
|
|
689
754
|
"@mcp-b/webmcp-polyfill": "catalog:",
|
|
690
755
|
"@opentiny/next-sdk": "workspace:^",
|
|
691
756
|
"@page-agent/page-controller": "catalog:",
|
|
757
|
+
clipboardy: "^5.3.1",
|
|
692
758
|
commander: "^11.1.0",
|
|
693
759
|
picocolors: "^1.0.0",
|
|
694
760
|
"puppeteer-core": "^22.0.0"
|
|
@@ -721,61 +787,80 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
|
|
|
721
787
|
});
|
|
722
788
|
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) => {
|
|
723
789
|
try {
|
|
724
|
-
const
|
|
725
|
-
console.log(JSON.stringify(
|
|
790
|
+
const result2 = await stateCommand({ tabid: parseTabId(options.tabid) });
|
|
791
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
726
792
|
} catch (error) {
|
|
727
793
|
handleCommandError(error, "state");
|
|
728
794
|
}
|
|
729
795
|
});
|
|
730
796
|
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").action(async (toolName, argsJson, options) => {
|
|
731
797
|
try {
|
|
732
|
-
const
|
|
798
|
+
const result2 = await runCommand({
|
|
733
799
|
toolName,
|
|
734
800
|
argsJson,
|
|
735
801
|
tabid: parseTabId(options.tabid)
|
|
736
802
|
});
|
|
737
|
-
console.log(JSON.stringify(
|
|
803
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
738
804
|
} catch (error) {
|
|
739
805
|
handleCommandError(error, "run");
|
|
740
806
|
}
|
|
741
807
|
});
|
|
808
|
+
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) => {
|
|
809
|
+
try {
|
|
810
|
+
const result2 = await evaluateCommand({
|
|
811
|
+
jsScript: jsScript2,
|
|
812
|
+
tabid: parseTabId(options.tabid)
|
|
813
|
+
});
|
|
814
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
815
|
+
} catch (error) {
|
|
816
|
+
handleCommandError(error, "evaluate");
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
|
|
820
|
+
try {
|
|
821
|
+
await setClipboard(content);
|
|
822
|
+
console.log(JSON.stringify({ success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" }, null, 2));
|
|
823
|
+
} catch (error) {
|
|
824
|
+
handleCommandError(error, "clipboard");
|
|
825
|
+
}
|
|
826
|
+
});
|
|
742
827
|
var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
|
|
743
828
|
tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
|
|
744
829
|
try {
|
|
745
|
-
const
|
|
746
|
-
console.log(JSON.stringify(
|
|
830
|
+
const result2 = await tabsOpenCommand(url);
|
|
831
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
747
832
|
} catch (error) {
|
|
748
833
|
handleCommandError(error, "tabs open");
|
|
749
834
|
}
|
|
750
835
|
});
|
|
751
|
-
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
836
|
+
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
|
|
752
837
|
try {
|
|
753
|
-
const
|
|
754
|
-
console.log(JSON.stringify(
|
|
838
|
+
const result2 = await tabsCloseCommand(tabid2);
|
|
839
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
755
840
|
} catch (error) {
|
|
756
841
|
handleCommandError(error, "tabs close");
|
|
757
842
|
}
|
|
758
843
|
});
|
|
759
|
-
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
844
|
+
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
|
|
760
845
|
try {
|
|
761
|
-
const
|
|
762
|
-
console.log(JSON.stringify(
|
|
846
|
+
const result2 = await tabsSwitchCommand(tabid2);
|
|
847
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
763
848
|
} catch (error) {
|
|
764
849
|
handleCommandError(error, "tabs switch");
|
|
765
850
|
}
|
|
766
851
|
});
|
|
767
|
-
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (
|
|
852
|
+
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid2) => {
|
|
768
853
|
try {
|
|
769
|
-
const
|
|
770
|
-
console.log(JSON.stringify(
|
|
854
|
+
const result2 = await tabsBackCommand(parseTabId(tabid2));
|
|
855
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
771
856
|
} catch (error) {
|
|
772
857
|
handleCommandError(error, "tabs back");
|
|
773
858
|
}
|
|
774
859
|
});
|
|
775
|
-
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (
|
|
860
|
+
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid2) => {
|
|
776
861
|
try {
|
|
777
|
-
const
|
|
778
|
-
console.log(JSON.stringify(
|
|
862
|
+
const result2 = await tabsForwardCommand(parseTabId(tabid2));
|
|
863
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
779
864
|
} catch (error) {
|
|
780
865
|
handleCommandError(error, "tabs forward");
|
|
781
866
|
}
|