@opentiny/webmcp-cli 0.0.1-alpha.6 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -7
- package/dist/bin.cjs +275 -210
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +271 -206
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +38 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +38 -43
- package/dist/index.js.map +1 -1
- package/dist/inject-bundle.js +2083 -226
- package/dist/webmcp-tools/creator.xiaohongshu.com.js +315 -0
- package/dist/webmcp-tools/editor.csdn.net.js +155 -0
- package/dist/webmcp-tools/juejin.cn.js +151 -6
- package/dist/webmcp-tools/my.oschina.net.js +94 -0
- package/dist/webmcp-tools/xiaohongshu.com.js +278 -0
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin.ts
|
|
4
|
+
import fs2 from "fs";
|
|
5
|
+
import path2 from "path";
|
|
6
|
+
import os2 from "os";
|
|
4
7
|
import { Command } from "commander";
|
|
5
8
|
import pc2 from "picocolors";
|
|
6
9
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -341,13 +344,13 @@ async function connectBrowser() {
|
|
|
341
344
|
const browserURL = addr.replace("/json/version", "");
|
|
342
345
|
try {
|
|
343
346
|
console.log(pc.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
344
|
-
const
|
|
347
|
+
const browser = await promiseWithTimeout(
|
|
345
348
|
puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
346
349
|
1e4,
|
|
347
350
|
`puppeteer.connect to ${browserURL} timed out`
|
|
348
351
|
);
|
|
349
352
|
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
350
|
-
return
|
|
353
|
+
return browser;
|
|
351
354
|
} catch {
|
|
352
355
|
}
|
|
353
356
|
}
|
|
@@ -365,20 +368,20 @@ async function connectBrowser() {
|
|
|
365
368
|
const browserURL = addr.replace("/json/version", "");
|
|
366
369
|
try {
|
|
367
370
|
console.log(pc.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
368
|
-
const
|
|
371
|
+
const browser = await promiseWithTimeout(
|
|
369
372
|
puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
370
373
|
1e4,
|
|
371
374
|
`puppeteer.connect to ${browserURL} after launch timed out`
|
|
372
375
|
);
|
|
373
376
|
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
374
|
-
return
|
|
377
|
+
return browser;
|
|
375
378
|
} catch {
|
|
376
379
|
}
|
|
377
380
|
}
|
|
378
381
|
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`);
|
|
379
382
|
}
|
|
380
|
-
async function getPageTargetId(
|
|
381
|
-
const session = await
|
|
383
|
+
async function getPageTargetId(page) {
|
|
384
|
+
const session = await page.target().createCDPSession();
|
|
382
385
|
try {
|
|
383
386
|
const { targetInfo } = await session.send("Target.getTargetInfo");
|
|
384
387
|
return targetInfo.targetId;
|
|
@@ -387,8 +390,8 @@ async function getPageTargetId(page2) {
|
|
|
387
390
|
});
|
|
388
391
|
}
|
|
389
392
|
}
|
|
390
|
-
function getPageTargets(
|
|
391
|
-
return
|
|
393
|
+
function getPageTargets(browser) {
|
|
394
|
+
return browser.targets().filter((t) => {
|
|
392
395
|
try {
|
|
393
396
|
const type = (typeof t.type === "function" ? t.type() : t.type) || "";
|
|
394
397
|
const url = (typeof t.url === "function" ? t.url() : t.url) || "";
|
|
@@ -401,26 +404,26 @@ function getPageTargets(browser2) {
|
|
|
401
404
|
function getTargetIdFromTarget(target) {
|
|
402
405
|
return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
|
|
403
406
|
}
|
|
404
|
-
function findPageTargetByTabId(
|
|
405
|
-
const pageTargets = getPageTargets(
|
|
407
|
+
function findPageTargetByTabId(browser, tabid) {
|
|
408
|
+
const pageTargets = getPageTargets(browser);
|
|
406
409
|
return pageTargets.find((t) => {
|
|
407
410
|
const tid = getTargetIdFromTarget(t);
|
|
408
|
-
return tid ===
|
|
411
|
+
return tid === tabid || tid.includes(tabid);
|
|
409
412
|
}) ?? null;
|
|
410
413
|
}
|
|
411
|
-
async function activateTabById(
|
|
412
|
-
const target = findPageTargetByTabId(
|
|
414
|
+
async function activateTabById(browser, tabid) {
|
|
415
|
+
const target = findPageTargetByTabId(browser, tabid);
|
|
413
416
|
if (!target) {
|
|
414
|
-
throw new Error(`Tab with targetId "${
|
|
417
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
415
418
|
}
|
|
416
419
|
const realTabId = getTargetIdFromTarget(target);
|
|
417
|
-
const pages = await
|
|
420
|
+
const pages = await browser.pages();
|
|
418
421
|
let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
|
|
419
422
|
if (!sessionPage && pages.length > 0) {
|
|
420
423
|
sessionPage = pages[0];
|
|
421
424
|
}
|
|
422
425
|
if (!sessionPage) {
|
|
423
|
-
sessionPage = await
|
|
426
|
+
sessionPage = await browser.newPage();
|
|
424
427
|
}
|
|
425
428
|
const session = await sessionPage.createCDPSession();
|
|
426
429
|
try {
|
|
@@ -430,18 +433,18 @@ async function activateTabById(browser2, tabid2) {
|
|
|
430
433
|
});
|
|
431
434
|
}
|
|
432
435
|
}
|
|
433
|
-
async function getTargetPage(
|
|
434
|
-
const pageTargets = getPageTargets(
|
|
436
|
+
async function getTargetPage(browser, tabid) {
|
|
437
|
+
const pageTargets = getPageTargets(browser);
|
|
435
438
|
if (pageTargets.length === 0) {
|
|
436
|
-
const newPage = await
|
|
439
|
+
const newPage = await browser.newPage();
|
|
437
440
|
await injectWebMCPPolyfillAndTools(newPage);
|
|
438
441
|
return newPage;
|
|
439
442
|
}
|
|
440
443
|
let targetPage = null;
|
|
441
|
-
if (
|
|
442
|
-
const target = findPageTargetByTabId(
|
|
444
|
+
if (tabid !== void 0) {
|
|
445
|
+
const target = findPageTargetByTabId(browser, tabid);
|
|
443
446
|
if (!target) {
|
|
444
|
-
throw new Error(`Tab with targetId "${
|
|
447
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
445
448
|
}
|
|
446
449
|
targetPage = await target.page();
|
|
447
450
|
} else {
|
|
@@ -484,11 +487,11 @@ async function getTargetPage(browser2, tabid2) {
|
|
|
484
487
|
await injectWebMCPPolyfillAndTools(targetPage);
|
|
485
488
|
return targetPage;
|
|
486
489
|
}
|
|
487
|
-
async function injectIntoPage(
|
|
488
|
-
await injectWebMCPPolyfillAndTools(
|
|
490
|
+
async function injectIntoPage(page) {
|
|
491
|
+
await injectWebMCPPolyfillAndTools(page, true);
|
|
489
492
|
}
|
|
490
|
-
async function injectWebMCPPolyfillAndTools(
|
|
491
|
-
const polyfillReady = !force && await
|
|
493
|
+
async function injectWebMCPPolyfillAndTools(page, force = false) {
|
|
494
|
+
const polyfillReady = !force && await page.evaluate(() => {
|
|
492
495
|
return !!window.__webmcpcli_init;
|
|
493
496
|
}).catch(() => false);
|
|
494
497
|
if (!polyfillReady) {
|
|
@@ -499,31 +502,63 @@ async function injectWebMCPPolyfillAndTools(page2, force = false) {
|
|
|
499
502
|
}
|
|
500
503
|
const scriptContent = fs.readFileSync(injectScriptPath, "utf-8");
|
|
501
504
|
try {
|
|
502
|
-
await
|
|
505
|
+
await page.evaluate(scriptContent);
|
|
503
506
|
} catch (err) {
|
|
504
507
|
const msg = err instanceof Error ? err.message : String(err);
|
|
505
508
|
throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
|
|
506
509
|
}
|
|
507
|
-
await
|
|
510
|
+
await page.waitForFunction(() => {
|
|
511
|
+
const mcp = navigator.modelContext;
|
|
512
|
+
return mcp && typeof mcp.registerTool === "function";
|
|
513
|
+
}, { timeout: 1e4 }).catch(() => {
|
|
514
|
+
console.warn("\u7B49\u5F85 WebMCP registerTool \u63A5\u53E3\u5C31\u7EEA\u8D85\u65F6\uFF0C\u6B63\u5728\u7EE7\u7EED\u6CE8\u5165...");
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
await injectDomainTools(page);
|
|
518
|
+
}
|
|
519
|
+
function getToolsBundleName(hostname) {
|
|
520
|
+
const normalized = hostname.split(":")[0].toLowerCase();
|
|
521
|
+
const toolsDir = path.resolve(__dirname, "webmcp-tools");
|
|
522
|
+
if (!fs.existsSync(toolsDir)) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
let files = [];
|
|
526
|
+
try {
|
|
527
|
+
files = fs.readdirSync(toolsDir);
|
|
528
|
+
} catch {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
const supportedDomains = files.filter((f) => f.endsWith(".js")).map((f) => f.slice(0, -3));
|
|
532
|
+
if (supportedDomains.includes(normalized)) {
|
|
533
|
+
return `webmcp-tools/${normalized}.js`;
|
|
534
|
+
}
|
|
535
|
+
for (const domain of supportedDomains) {
|
|
536
|
+
if (normalized.endsWith("." + domain)) {
|
|
537
|
+
return `webmcp-tools/${domain}.js`;
|
|
538
|
+
}
|
|
508
539
|
}
|
|
509
|
-
|
|
540
|
+
return null;
|
|
510
541
|
}
|
|
511
|
-
async function injectDomainTools(
|
|
542
|
+
async function injectDomainTools(page) {
|
|
512
543
|
let hostname;
|
|
513
544
|
try {
|
|
514
|
-
const url = new URL(
|
|
545
|
+
const url = new URL(page.url());
|
|
515
546
|
hostname = url.hostname;
|
|
516
547
|
} catch {
|
|
517
548
|
return;
|
|
518
549
|
}
|
|
519
|
-
const
|
|
550
|
+
const bundleName = getToolsBundleName(hostname);
|
|
551
|
+
if (!bundleName) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const toolBundlePath = path.resolve(__dirname, bundleName);
|
|
520
555
|
if (!fs.existsSync(toolBundlePath)) {
|
|
521
556
|
return;
|
|
522
557
|
}
|
|
523
558
|
console.log(pc.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
|
|
524
559
|
const toolScript = fs.readFileSync(toolBundlePath, "utf-8");
|
|
525
560
|
try {
|
|
526
|
-
await
|
|
561
|
+
await page.evaluate(toolScript);
|
|
527
562
|
console.log(pc.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
|
|
528
563
|
} catch (err) {
|
|
529
564
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -532,60 +567,23 @@ async function injectDomainTools(page2) {
|
|
|
532
567
|
}
|
|
533
568
|
|
|
534
569
|
// src/commands/state.ts
|
|
535
|
-
async function stateCommand({ tabid
|
|
536
|
-
const
|
|
570
|
+
async function stateCommand({ tabid }) {
|
|
571
|
+
const browser = await connectBrowser();
|
|
537
572
|
try {
|
|
538
|
-
const
|
|
539
|
-
await injectIntoPage(
|
|
540
|
-
const state = await
|
|
573
|
+
const page = await getTargetPage(browser, tabid);
|
|
574
|
+
await injectIntoPage(page);
|
|
575
|
+
const state = await page.evaluate(async () => {
|
|
541
576
|
const url = document.URL;
|
|
542
577
|
const title = document.title;
|
|
543
578
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
544
579
|
let webmcpTools = [];
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const toolsResult = await mcp.listTools();
|
|
549
|
-
webmcpTools = toolsResult?.tools || toolsResult || [];
|
|
550
|
-
}
|
|
551
|
-
if (typeof mcp.executeTool === "function") {
|
|
552
|
-
try {
|
|
553
|
-
const argsString = JSON.stringify({ action: "browserState" });
|
|
554
|
-
let stateRes = await mcp.executeTool("page-agent-tool", argsString);
|
|
555
|
-
if (typeof stateRes === "string") {
|
|
556
|
-
try {
|
|
557
|
-
stateRes = JSON.parse(stateRes);
|
|
558
|
-
} catch (e) {
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (stateRes && stateRes.content && stateRes.content.length > 0) {
|
|
562
|
-
const textContent = stateRes.content.map((c) => c.text).join("\\n");
|
|
563
|
-
const prefix = "\u6D4F\u89C8\u5668\u72B6\u6001: ";
|
|
564
|
-
if (textContent.startsWith(prefix)) {
|
|
565
|
-
try {
|
|
566
|
-
const jsonStr = textContent.substring(prefix.length);
|
|
567
|
-
const parsedState = JSON.parse(jsonStr);
|
|
568
|
-
contentData = parsedState.content;
|
|
569
|
-
} catch (e) {
|
|
570
|
-
contentData = textContent;
|
|
571
|
-
}
|
|
572
|
-
} else {
|
|
573
|
-
contentData = textContent;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
} catch (e) {
|
|
577
|
-
console.error("Snapshot error:", e.message);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
+
if (mcp && typeof mcp.listTools === "function") {
|
|
581
|
+
const toolsResult = await mcp.listTools();
|
|
582
|
+
webmcpTools = toolsResult?.tools || toolsResult || [];
|
|
580
583
|
}
|
|
581
|
-
return {
|
|
582
|
-
content: contentData,
|
|
583
|
-
url,
|
|
584
|
-
title,
|
|
585
|
-
webmcpTools
|
|
586
|
-
};
|
|
584
|
+
return { url, title, webmcpTools };
|
|
587
585
|
});
|
|
588
|
-
const pages = await
|
|
586
|
+
const pages = await browser.pages();
|
|
589
587
|
const tabs2 = await Promise.all(
|
|
590
588
|
pages.map(async (p) => {
|
|
591
589
|
const pUrl = p.url();
|
|
@@ -607,7 +605,7 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
607
605
|
tabs: tabs2.filter(Boolean)
|
|
608
606
|
};
|
|
609
607
|
} finally {
|
|
610
|
-
await
|
|
608
|
+
await browser.disconnect();
|
|
611
609
|
}
|
|
612
610
|
}
|
|
613
611
|
|
|
@@ -615,13 +613,13 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
615
613
|
async function runCommand({
|
|
616
614
|
toolName,
|
|
617
615
|
argsJson,
|
|
618
|
-
tabid
|
|
616
|
+
tabid
|
|
619
617
|
}) {
|
|
620
|
-
const
|
|
618
|
+
const browser = await connectBrowser();
|
|
621
619
|
try {
|
|
622
|
-
const
|
|
620
|
+
const page = await getTargetPage(browser, tabid);
|
|
623
621
|
const cleanedArgs = argsJson.trim();
|
|
624
|
-
const
|
|
622
|
+
const result = await page.evaluate(async (name, inputString) => {
|
|
625
623
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
626
624
|
if (!mcp || typeof mcp.executeTool !== "function") {
|
|
627
625
|
throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
|
|
@@ -642,35 +640,6 @@ async function runCommand({
|
|
|
642
640
|
}
|
|
643
641
|
return res;
|
|
644
642
|
}, toolName, cleanedArgs);
|
|
645
|
-
return result2;
|
|
646
|
-
} finally {
|
|
647
|
-
await browser2.disconnect();
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// src/commands/evaluate.ts
|
|
652
|
-
async function evaluateCommand({
|
|
653
|
-
jsScript,
|
|
654
|
-
tabid
|
|
655
|
-
}) {
|
|
656
|
-
const browser = await connectBrowser();
|
|
657
|
-
try {
|
|
658
|
-
const page = await getTargetPage(browser, tabid);
|
|
659
|
-
const result = await page.evaluate(async (script) => {
|
|
660
|
-
try {
|
|
661
|
-
const result = eval(script);
|
|
662
|
-
if (result instanceof Promise) {
|
|
663
|
-
return await result;
|
|
664
|
-
}
|
|
665
|
-
return result;
|
|
666
|
-
} catch (error) {
|
|
667
|
-
return {
|
|
668
|
-
error: true,
|
|
669
|
-
message: error.message,
|
|
670
|
-
stack: error.stack
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
}, jsScript);
|
|
674
643
|
return result;
|
|
675
644
|
} finally {
|
|
676
645
|
await browser.disconnect();
|
|
@@ -695,114 +664,114 @@ function normalizeUrl(url) {
|
|
|
695
664
|
return url;
|
|
696
665
|
}
|
|
697
666
|
async function tabsOpenCommand(url) {
|
|
698
|
-
const
|
|
667
|
+
const browser = await connectBrowser();
|
|
699
668
|
try {
|
|
700
|
-
const
|
|
669
|
+
const page = await browser.newPage();
|
|
701
670
|
url = normalizeUrl(url);
|
|
702
|
-
await
|
|
703
|
-
await injectIntoPage(
|
|
671
|
+
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
672
|
+
await injectIntoPage(page);
|
|
704
673
|
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
705
674
|
return {
|
|
706
675
|
success: true,
|
|
707
|
-
tabid: await getPageTargetId(
|
|
708
|
-
url:
|
|
709
|
-
title: await
|
|
676
|
+
tabid: await getPageTargetId(page),
|
|
677
|
+
url: page.url(),
|
|
678
|
+
title: await page.title()
|
|
710
679
|
};
|
|
711
680
|
} finally {
|
|
712
|
-
await
|
|
681
|
+
await browser.disconnect();
|
|
713
682
|
}
|
|
714
683
|
}
|
|
715
|
-
async function tabsCloseCommand(
|
|
716
|
-
const
|
|
684
|
+
async function tabsCloseCommand(tabid) {
|
|
685
|
+
const browser = await connectBrowser();
|
|
717
686
|
try {
|
|
718
|
-
const target = findPageTargetByTabId(
|
|
687
|
+
const target = findPageTargetByTabId(browser, tabid);
|
|
719
688
|
if (!target) {
|
|
720
|
-
throw new Error(`Tab with targetId "${
|
|
689
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
721
690
|
}
|
|
722
|
-
const
|
|
723
|
-
if (!
|
|
724
|
-
throw new Error(`Tab with targetId "${
|
|
691
|
+
const page = await target.page();
|
|
692
|
+
if (!page) {
|
|
693
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
725
694
|
}
|
|
726
|
-
const closedTabid = await getPageTargetId(
|
|
727
|
-
await
|
|
695
|
+
const closedTabid = await getPageTargetId(page).catch(() => tabid);
|
|
696
|
+
await page.close();
|
|
728
697
|
return {
|
|
729
698
|
success: true,
|
|
730
699
|
tabid: closedTabid
|
|
731
700
|
};
|
|
732
701
|
} finally {
|
|
733
|
-
await
|
|
702
|
+
await browser.disconnect();
|
|
734
703
|
}
|
|
735
704
|
}
|
|
736
|
-
async function tabsSwitchCommand(
|
|
737
|
-
const
|
|
705
|
+
async function tabsSwitchCommand(tabid) {
|
|
706
|
+
const browser = await connectBrowser();
|
|
738
707
|
try {
|
|
739
|
-
await activateTabById(
|
|
740
|
-
const
|
|
708
|
+
await activateTabById(browser, tabid);
|
|
709
|
+
const page = await getTargetPage(browser, tabid);
|
|
741
710
|
return {
|
|
742
711
|
success: true,
|
|
743
|
-
tabid: await getPageTargetId(
|
|
744
|
-
url:
|
|
745
|
-
title: await
|
|
712
|
+
tabid: await getPageTargetId(page),
|
|
713
|
+
url: page.url(),
|
|
714
|
+
title: await page.title()
|
|
746
715
|
};
|
|
747
716
|
} finally {
|
|
748
|
-
await
|
|
717
|
+
await browser.disconnect();
|
|
749
718
|
}
|
|
750
719
|
}
|
|
751
|
-
async function tabsBackCommand(
|
|
752
|
-
const
|
|
720
|
+
async function tabsBackCommand(tabid) {
|
|
721
|
+
const browser = await connectBrowser();
|
|
753
722
|
try {
|
|
754
|
-
const
|
|
755
|
-
const response = await
|
|
723
|
+
const page = await getTargetPage(browser, tabid);
|
|
724
|
+
const response = await page.goBack();
|
|
756
725
|
if (!response) {
|
|
757
726
|
return {
|
|
758
727
|
success: false,
|
|
759
728
|
error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
|
|
760
|
-
tabid: await getPageTargetId(
|
|
761
|
-
url:
|
|
762
|
-
title: await
|
|
729
|
+
tabid: await getPageTargetId(page).catch(() => tabid),
|
|
730
|
+
url: page.url(),
|
|
731
|
+
title: await page.title()
|
|
763
732
|
};
|
|
764
733
|
}
|
|
765
|
-
await injectIntoPage(
|
|
734
|
+
await injectIntoPage(page);
|
|
766
735
|
return {
|
|
767
736
|
success: true,
|
|
768
|
-
tabid: await getPageTargetId(
|
|
769
|
-
url:
|
|
770
|
-
title: await
|
|
737
|
+
tabid: await getPageTargetId(page),
|
|
738
|
+
url: page.url(),
|
|
739
|
+
title: await page.title()
|
|
771
740
|
};
|
|
772
741
|
} finally {
|
|
773
|
-
await
|
|
742
|
+
await browser.disconnect();
|
|
774
743
|
}
|
|
775
744
|
}
|
|
776
|
-
async function tabsForwardCommand(
|
|
777
|
-
const
|
|
745
|
+
async function tabsForwardCommand(tabid) {
|
|
746
|
+
const browser = await connectBrowser();
|
|
778
747
|
try {
|
|
779
|
-
const
|
|
780
|
-
const response = await
|
|
748
|
+
const page = await getTargetPage(browser, tabid);
|
|
749
|
+
const response = await page.goForward();
|
|
781
750
|
if (!response) {
|
|
782
751
|
return {
|
|
783
752
|
success: false,
|
|
784
753
|
error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
|
|
785
|
-
tabid: await getPageTargetId(
|
|
786
|
-
url:
|
|
787
|
-
title: await
|
|
754
|
+
tabid: await getPageTargetId(page).catch(() => tabid),
|
|
755
|
+
url: page.url(),
|
|
756
|
+
title: await page.title()
|
|
788
757
|
};
|
|
789
758
|
}
|
|
790
|
-
await injectIntoPage(
|
|
759
|
+
await injectIntoPage(page);
|
|
791
760
|
return {
|
|
792
761
|
success: true,
|
|
793
|
-
tabid: await getPageTargetId(
|
|
794
|
-
url:
|
|
795
|
-
title: await
|
|
762
|
+
tabid: await getPageTargetId(page),
|
|
763
|
+
url: page.url(),
|
|
764
|
+
title: await page.title()
|
|
796
765
|
};
|
|
797
766
|
} finally {
|
|
798
|
-
await
|
|
767
|
+
await browser.disconnect();
|
|
799
768
|
}
|
|
800
769
|
}
|
|
801
770
|
|
|
802
771
|
// package.json
|
|
803
772
|
var package_default = {
|
|
804
773
|
name: "@opentiny/webmcp-cli",
|
|
805
|
-
version: "0.0.
|
|
774
|
+
version: "0.0.2",
|
|
806
775
|
type: "module",
|
|
807
776
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
808
777
|
main: "dist/index.js",
|
|
@@ -860,8 +829,97 @@ function parseTabId(id) {
|
|
|
860
829
|
if (!id) return void 0;
|
|
861
830
|
return id;
|
|
862
831
|
}
|
|
863
|
-
function
|
|
832
|
+
function cleanOldLogs(baseDir, logDir) {
|
|
833
|
+
try {
|
|
834
|
+
const oldLogFile = path2.join(baseDir, "webmcp-cli.log");
|
|
835
|
+
if (fs2.existsSync(oldLogFile)) {
|
|
836
|
+
try {
|
|
837
|
+
fs2.unlinkSync(oldLogFile);
|
|
838
|
+
} catch {
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (fs2.existsSync(baseDir)) {
|
|
842
|
+
const baseFiles = fs2.readdirSync(baseDir);
|
|
843
|
+
for (const file of baseFiles) {
|
|
844
|
+
if (/^webmcp-cli-\d{4}-\d{2}-\d{2}\.log$/.test(file)) {
|
|
845
|
+
try {
|
|
846
|
+
fs2.unlinkSync(path2.join(baseDir, file));
|
|
847
|
+
} catch {
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (!fs2.existsSync(logDir)) return;
|
|
853
|
+
const files = fs2.readdirSync(logDir);
|
|
854
|
+
const logFilePattern = /^webmcp-cli-(\d{4}-\d{2}-\d{2})\.log$/;
|
|
855
|
+
const now = Date.now();
|
|
856
|
+
const maxAgeMs = 7 * 24 * 60 * 60 * 1e3;
|
|
857
|
+
for (const file of files) {
|
|
858
|
+
const match = file.match(logFilePattern);
|
|
859
|
+
if (match) {
|
|
860
|
+
const fileDateStr = match[1];
|
|
861
|
+
const fileDate = new Date(fileDateStr).getTime();
|
|
862
|
+
if (isNaN(fileDate)) continue;
|
|
863
|
+
if (now - fileDate > maxAgeMs) {
|
|
864
|
+
try {
|
|
865
|
+
fs2.unlinkSync(path2.join(logDir, file));
|
|
866
|
+
} catch {
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
} catch (e) {
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function writeLog(commandName, args, result, error) {
|
|
875
|
+
try {
|
|
876
|
+
const baseDir = process.env.WEBMCP_WORKSPACE || path2.join(os2.homedir(), ".webmcp_chrome_profile");
|
|
877
|
+
const logDir = path2.join(baseDir, "logs");
|
|
878
|
+
if (!fs2.existsSync(logDir)) {
|
|
879
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
880
|
+
}
|
|
881
|
+
const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
882
|
+
const logFile = path2.join(logDir, `webmcp-cli-${todayStr}.log`);
|
|
883
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
884
|
+
let logText = `========================================
|
|
885
|
+
`;
|
|
886
|
+
logText += `[${timestamp}] COMMAND: ${commandName}
|
|
887
|
+
`;
|
|
888
|
+
logText += `ARGS:
|
|
889
|
+
${JSON.stringify(args, null, 2)}
|
|
890
|
+
|
|
891
|
+
`;
|
|
892
|
+
if (error) {
|
|
893
|
+
logText += `ERROR:
|
|
894
|
+
${error instanceof Error ? error.stack : String(error)}
|
|
895
|
+
`;
|
|
896
|
+
} else if (result) {
|
|
897
|
+
logText += `RESULT:
|
|
898
|
+
`;
|
|
899
|
+
try {
|
|
900
|
+
if (result && typeof result === "object") {
|
|
901
|
+
logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
|
|
902
|
+
} else {
|
|
903
|
+
logText += String(result).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
|
|
904
|
+
}
|
|
905
|
+
} catch {
|
|
906
|
+
logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
|
|
907
|
+
}
|
|
908
|
+
} else {
|
|
909
|
+
logText += `RESULT: null
|
|
910
|
+
`;
|
|
911
|
+
}
|
|
912
|
+
logText += `========================================
|
|
913
|
+
|
|
914
|
+
`;
|
|
915
|
+
fs2.appendFileSync(logFile, logText, "utf-8");
|
|
916
|
+
cleanOldLogs(baseDir, logDir);
|
|
917
|
+
} catch (e) {
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function handleCommandError(error, commandName, args) {
|
|
864
921
|
const msg = error instanceof Error ? error.message : String(error);
|
|
922
|
+
writeLog(commandName, args, null, error);
|
|
865
923
|
console.error(pc2.red(`Error executing ${commandName} command: ${msg}`));
|
|
866
924
|
process.exit(1);
|
|
867
925
|
}
|
|
@@ -871,12 +929,14 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
|
|
|
871
929
|
process.env.WEBMCP_WORKSPACE = opts.workspace;
|
|
872
930
|
}
|
|
873
931
|
});
|
|
874
|
-
program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\
|
|
932
|
+
program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\u5BFC\u822A\u5143\u6570\u636E\uFF08url\u3001title\u3001webmcpTools\u3001\u6240\u6709\u9875\u7B7E\u5217\u8868\uFF09").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (options) => {
|
|
933
|
+
const args = { tabid: parseTabId(options.tabid) };
|
|
875
934
|
try {
|
|
876
|
-
const
|
|
877
|
-
|
|
935
|
+
const result = await stateCommand(args);
|
|
936
|
+
writeLog("state", args, result);
|
|
937
|
+
console.log(JSON.stringify(result, null, 2));
|
|
878
938
|
} catch (error) {
|
|
879
|
-
handleCommandError(error, "state");
|
|
939
|
+
handleCommandError(error, "state", args);
|
|
880
940
|
}
|
|
881
941
|
});
|
|
882
942
|
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(
|
|
@@ -887,6 +947,8 @@ program.command("run <toolName> [args...]").description("\u5411\u6307\u5B9A\u987
|
|
|
887
947
|
@base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
|
|
888
948
|
\u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
|
|
889
949
|
).action(async (toolName, args, options) => {
|
|
950
|
+
let finalArgsJson = "";
|
|
951
|
+
const rawArgs = { toolName, args, file: options.file, tabid: options.tabid };
|
|
890
952
|
try {
|
|
891
953
|
let fileContent;
|
|
892
954
|
let fileBaseDir = process.cwd();
|
|
@@ -899,75 +961,78 @@ program.command("run <toolName> [args...]").description("\u5411\u6307\u5B9A\u987
|
|
|
899
961
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
|
|
900
962
|
}
|
|
901
963
|
}
|
|
902
|
-
|
|
903
|
-
const
|
|
964
|
+
finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir);
|
|
965
|
+
const result = await runCommand({
|
|
904
966
|
toolName,
|
|
905
967
|
argsJson: finalArgsJson,
|
|
906
968
|
tabid: parseTabId(options.tabid)
|
|
907
969
|
});
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
handleCommandError(error, "run");
|
|
911
|
-
}
|
|
912
|
-
});
|
|
913
|
-
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) => {
|
|
914
|
-
try {
|
|
915
|
-
const result2 = await evaluateCommand({
|
|
916
|
-
jsScript: jsScript2,
|
|
917
|
-
tabid: parseTabId(options.tabid)
|
|
918
|
-
});
|
|
919
|
-
console.log(JSON.stringify(result2, null, 2));
|
|
970
|
+
writeLog("run", { ...rawArgs, finalArgsJson }, result);
|
|
971
|
+
console.log(JSON.stringify(result, null, 2));
|
|
920
972
|
} catch (error) {
|
|
921
|
-
handleCommandError(error, "
|
|
973
|
+
handleCommandError(error, "run", { ...rawArgs, finalArgsJson });
|
|
922
974
|
}
|
|
923
975
|
});
|
|
924
976
|
program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
|
|
977
|
+
const args = { content };
|
|
925
978
|
try {
|
|
926
979
|
await setClipboard(content);
|
|
927
|
-
|
|
980
|
+
const result = { success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" };
|
|
981
|
+
writeLog("clipboard", args, result);
|
|
982
|
+
console.log(JSON.stringify(result, null, 2));
|
|
928
983
|
} catch (error) {
|
|
929
|
-
handleCommandError(error, "clipboard");
|
|
984
|
+
handleCommandError(error, "clipboard", args);
|
|
930
985
|
}
|
|
931
986
|
});
|
|
932
987
|
var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
|
|
933
988
|
tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
|
|
989
|
+
const args = { url };
|
|
934
990
|
try {
|
|
935
|
-
const
|
|
936
|
-
|
|
991
|
+
const result = await tabsOpenCommand(url);
|
|
992
|
+
writeLog("tabs open", args, result);
|
|
993
|
+
console.log(JSON.stringify(result, null, 2));
|
|
937
994
|
} catch (error) {
|
|
938
|
-
handleCommandError(error, "tabs open");
|
|
995
|
+
handleCommandError(error, "tabs open", args);
|
|
939
996
|
}
|
|
940
997
|
});
|
|
941
|
-
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
998
|
+
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
|
|
999
|
+
const args = { tabid };
|
|
942
1000
|
try {
|
|
943
|
-
const
|
|
944
|
-
|
|
1001
|
+
const result = await tabsCloseCommand(tabid);
|
|
1002
|
+
writeLog("tabs close", args, result);
|
|
1003
|
+
console.log(JSON.stringify(result, null, 2));
|
|
945
1004
|
} catch (error) {
|
|
946
|
-
handleCommandError(error, "tabs close");
|
|
1005
|
+
handleCommandError(error, "tabs close", args);
|
|
947
1006
|
}
|
|
948
1007
|
});
|
|
949
|
-
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
1008
|
+
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
|
|
1009
|
+
const args = { tabid };
|
|
950
1010
|
try {
|
|
951
|
-
const
|
|
952
|
-
|
|
1011
|
+
const result = await tabsSwitchCommand(tabid);
|
|
1012
|
+
writeLog("tabs switch", args, result);
|
|
1013
|
+
console.log(JSON.stringify(result, null, 2));
|
|
953
1014
|
} catch (error) {
|
|
954
|
-
handleCommandError(error, "tabs switch");
|
|
1015
|
+
handleCommandError(error, "tabs switch", args);
|
|
955
1016
|
}
|
|
956
1017
|
});
|
|
957
|
-
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (
|
|
1018
|
+
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid) => {
|
|
1019
|
+
const args = { tabid: parseTabId(tabid) };
|
|
958
1020
|
try {
|
|
959
|
-
const
|
|
960
|
-
|
|
1021
|
+
const result = await tabsBackCommand(args.tabid);
|
|
1022
|
+
writeLog("tabs back", args, result);
|
|
1023
|
+
console.log(JSON.stringify(result, null, 2));
|
|
961
1024
|
} catch (error) {
|
|
962
|
-
handleCommandError(error, "tabs back");
|
|
1025
|
+
handleCommandError(error, "tabs back", args);
|
|
963
1026
|
}
|
|
964
1027
|
});
|
|
965
|
-
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (
|
|
1028
|
+
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid) => {
|
|
1029
|
+
const args = { tabid: parseTabId(tabid) };
|
|
966
1030
|
try {
|
|
967
|
-
const
|
|
968
|
-
|
|
1031
|
+
const result = await tabsForwardCommand(args.tabid);
|
|
1032
|
+
writeLog("tabs forward", args, result);
|
|
1033
|
+
console.log(JSON.stringify(result, null, 2));
|
|
969
1034
|
} catch (error) {
|
|
970
|
-
handleCommandError(error, "tabs forward");
|
|
1035
|
+
handleCommandError(error, "tabs forward", args);
|
|
971
1036
|
}
|
|
972
1037
|
});
|
|
973
1038
|
program.parse(process.argv);
|