@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.cjs
CHANGED
|
@@ -24,10 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/bin.ts
|
|
27
|
+
var import_fs3 = __toESM(require("fs"), 1);
|
|
28
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
29
|
+
var import_os2 = __toESM(require("os"), 1);
|
|
27
30
|
var import_commander = require("commander");
|
|
28
31
|
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
29
|
-
var
|
|
30
|
-
var
|
|
32
|
+
var import_fs4 = require("fs");
|
|
33
|
+
var import_path4 = require("path");
|
|
31
34
|
|
|
32
35
|
// src/expand-file-refs.ts
|
|
33
36
|
var import_fs = require("fs");
|
|
@@ -365,13 +368,13 @@ async function connectBrowser() {
|
|
|
365
368
|
const browserURL = addr.replace("/json/version", "");
|
|
366
369
|
try {
|
|
367
370
|
console.log(import_picocolors.default.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
368
|
-
const
|
|
371
|
+
const browser = await promiseWithTimeout(
|
|
369
372
|
import_puppeteer_core.default.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
370
373
|
1e4,
|
|
371
374
|
`puppeteer.connect to ${browserURL} timed out`
|
|
372
375
|
);
|
|
373
376
|
console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
374
|
-
return
|
|
377
|
+
return browser;
|
|
375
378
|
} catch {
|
|
376
379
|
}
|
|
377
380
|
}
|
|
@@ -389,20 +392,20 @@ async function connectBrowser() {
|
|
|
389
392
|
const browserURL = addr.replace("/json/version", "");
|
|
390
393
|
try {
|
|
391
394
|
console.log(import_picocolors.default.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
|
|
392
|
-
const
|
|
395
|
+
const browser = await promiseWithTimeout(
|
|
393
396
|
import_puppeteer_core.default.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
394
397
|
1e4,
|
|
395
398
|
`puppeteer.connect to ${browserURL} after launch timed out`
|
|
396
399
|
);
|
|
397
400
|
console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
398
|
-
return
|
|
401
|
+
return browser;
|
|
399
402
|
} catch {
|
|
400
403
|
}
|
|
401
404
|
}
|
|
402
405
|
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`);
|
|
403
406
|
}
|
|
404
|
-
async function getPageTargetId(
|
|
405
|
-
const session = await
|
|
407
|
+
async function getPageTargetId(page) {
|
|
408
|
+
const session = await page.target().createCDPSession();
|
|
406
409
|
try {
|
|
407
410
|
const { targetInfo } = await session.send("Target.getTargetInfo");
|
|
408
411
|
return targetInfo.targetId;
|
|
@@ -411,8 +414,8 @@ async function getPageTargetId(page2) {
|
|
|
411
414
|
});
|
|
412
415
|
}
|
|
413
416
|
}
|
|
414
|
-
function getPageTargets(
|
|
415
|
-
return
|
|
417
|
+
function getPageTargets(browser) {
|
|
418
|
+
return browser.targets().filter((t) => {
|
|
416
419
|
try {
|
|
417
420
|
const type = (typeof t.type === "function" ? t.type() : t.type) || "";
|
|
418
421
|
const url = (typeof t.url === "function" ? t.url() : t.url) || "";
|
|
@@ -425,26 +428,26 @@ function getPageTargets(browser2) {
|
|
|
425
428
|
function getTargetIdFromTarget(target) {
|
|
426
429
|
return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
|
|
427
430
|
}
|
|
428
|
-
function findPageTargetByTabId(
|
|
429
|
-
const pageTargets = getPageTargets(
|
|
431
|
+
function findPageTargetByTabId(browser, tabid) {
|
|
432
|
+
const pageTargets = getPageTargets(browser);
|
|
430
433
|
return pageTargets.find((t) => {
|
|
431
434
|
const tid = getTargetIdFromTarget(t);
|
|
432
|
-
return tid ===
|
|
435
|
+
return tid === tabid || tid.includes(tabid);
|
|
433
436
|
}) ?? null;
|
|
434
437
|
}
|
|
435
|
-
async function activateTabById(
|
|
436
|
-
const target = findPageTargetByTabId(
|
|
438
|
+
async function activateTabById(browser, tabid) {
|
|
439
|
+
const target = findPageTargetByTabId(browser, tabid);
|
|
437
440
|
if (!target) {
|
|
438
|
-
throw new Error(`Tab with targetId "${
|
|
441
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
439
442
|
}
|
|
440
443
|
const realTabId = getTargetIdFromTarget(target);
|
|
441
|
-
const pages = await
|
|
444
|
+
const pages = await browser.pages();
|
|
442
445
|
let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
|
|
443
446
|
if (!sessionPage && pages.length > 0) {
|
|
444
447
|
sessionPage = pages[0];
|
|
445
448
|
}
|
|
446
449
|
if (!sessionPage) {
|
|
447
|
-
sessionPage = await
|
|
450
|
+
sessionPage = await browser.newPage();
|
|
448
451
|
}
|
|
449
452
|
const session = await sessionPage.createCDPSession();
|
|
450
453
|
try {
|
|
@@ -454,18 +457,18 @@ async function activateTabById(browser2, tabid2) {
|
|
|
454
457
|
});
|
|
455
458
|
}
|
|
456
459
|
}
|
|
457
|
-
async function getTargetPage(
|
|
458
|
-
const pageTargets = getPageTargets(
|
|
460
|
+
async function getTargetPage(browser, tabid) {
|
|
461
|
+
const pageTargets = getPageTargets(browser);
|
|
459
462
|
if (pageTargets.length === 0) {
|
|
460
|
-
const newPage = await
|
|
463
|
+
const newPage = await browser.newPage();
|
|
461
464
|
await injectWebMCPPolyfillAndTools(newPage);
|
|
462
465
|
return newPage;
|
|
463
466
|
}
|
|
464
467
|
let targetPage = null;
|
|
465
|
-
if (
|
|
466
|
-
const target = findPageTargetByTabId(
|
|
468
|
+
if (tabid !== void 0) {
|
|
469
|
+
const target = findPageTargetByTabId(browser, tabid);
|
|
467
470
|
if (!target) {
|
|
468
|
-
throw new Error(`Tab with targetId "${
|
|
471
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
469
472
|
}
|
|
470
473
|
targetPage = await target.page();
|
|
471
474
|
} else {
|
|
@@ -508,11 +511,11 @@ async function getTargetPage(browser2, tabid2) {
|
|
|
508
511
|
await injectWebMCPPolyfillAndTools(targetPage);
|
|
509
512
|
return targetPage;
|
|
510
513
|
}
|
|
511
|
-
async function injectIntoPage(
|
|
512
|
-
await injectWebMCPPolyfillAndTools(
|
|
514
|
+
async function injectIntoPage(page) {
|
|
515
|
+
await injectWebMCPPolyfillAndTools(page, true);
|
|
513
516
|
}
|
|
514
|
-
async function injectWebMCPPolyfillAndTools(
|
|
515
|
-
const polyfillReady = !force && await
|
|
517
|
+
async function injectWebMCPPolyfillAndTools(page, force = false) {
|
|
518
|
+
const polyfillReady = !force && await page.evaluate(() => {
|
|
516
519
|
return !!window.__webmcpcli_init;
|
|
517
520
|
}).catch(() => false);
|
|
518
521
|
if (!polyfillReady) {
|
|
@@ -523,31 +526,63 @@ async function injectWebMCPPolyfillAndTools(page2, force = false) {
|
|
|
523
526
|
}
|
|
524
527
|
const scriptContent = import_fs2.default.readFileSync(injectScriptPath, "utf-8");
|
|
525
528
|
try {
|
|
526
|
-
await
|
|
529
|
+
await page.evaluate(scriptContent);
|
|
527
530
|
} catch (err) {
|
|
528
531
|
const msg = err instanceof Error ? err.message : String(err);
|
|
529
532
|
throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
|
|
530
533
|
}
|
|
531
|
-
await
|
|
534
|
+
await page.waitForFunction(() => {
|
|
535
|
+
const mcp = navigator.modelContext;
|
|
536
|
+
return mcp && typeof mcp.registerTool === "function";
|
|
537
|
+
}, { timeout: 1e4 }).catch(() => {
|
|
538
|
+
console.warn("\u7B49\u5F85 WebMCP registerTool \u63A5\u53E3\u5C31\u7EEA\u8D85\u65F6\uFF0C\u6B63\u5728\u7EE7\u7EED\u6CE8\u5165...");
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
await injectDomainTools(page);
|
|
542
|
+
}
|
|
543
|
+
function getToolsBundleName(hostname) {
|
|
544
|
+
const normalized = hostname.split(":")[0].toLowerCase();
|
|
545
|
+
const toolsDir = import_path2.default.resolve(__dirname, "webmcp-tools");
|
|
546
|
+
if (!import_fs2.default.existsSync(toolsDir)) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
let files = [];
|
|
550
|
+
try {
|
|
551
|
+
files = import_fs2.default.readdirSync(toolsDir);
|
|
552
|
+
} catch {
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
const supportedDomains = files.filter((f) => f.endsWith(".js")).map((f) => f.slice(0, -3));
|
|
556
|
+
if (supportedDomains.includes(normalized)) {
|
|
557
|
+
return `webmcp-tools/${normalized}.js`;
|
|
558
|
+
}
|
|
559
|
+
for (const domain of supportedDomains) {
|
|
560
|
+
if (normalized.endsWith("." + domain)) {
|
|
561
|
+
return `webmcp-tools/${domain}.js`;
|
|
562
|
+
}
|
|
532
563
|
}
|
|
533
|
-
|
|
564
|
+
return null;
|
|
534
565
|
}
|
|
535
|
-
async function injectDomainTools(
|
|
566
|
+
async function injectDomainTools(page) {
|
|
536
567
|
let hostname;
|
|
537
568
|
try {
|
|
538
|
-
const url = new URL(
|
|
569
|
+
const url = new URL(page.url());
|
|
539
570
|
hostname = url.hostname;
|
|
540
571
|
} catch {
|
|
541
572
|
return;
|
|
542
573
|
}
|
|
543
|
-
const
|
|
574
|
+
const bundleName = getToolsBundleName(hostname);
|
|
575
|
+
if (!bundleName) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const toolBundlePath = import_path2.default.resolve(__dirname, bundleName);
|
|
544
579
|
if (!import_fs2.default.existsSync(toolBundlePath)) {
|
|
545
580
|
return;
|
|
546
581
|
}
|
|
547
582
|
console.log(import_picocolors.default.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
|
|
548
583
|
const toolScript = import_fs2.default.readFileSync(toolBundlePath, "utf-8");
|
|
549
584
|
try {
|
|
550
|
-
await
|
|
585
|
+
await page.evaluate(toolScript);
|
|
551
586
|
console.log(import_picocolors.default.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
|
|
552
587
|
} catch (err) {
|
|
553
588
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -556,60 +591,23 @@ async function injectDomainTools(page2) {
|
|
|
556
591
|
}
|
|
557
592
|
|
|
558
593
|
// src/commands/state.ts
|
|
559
|
-
async function stateCommand({ tabid
|
|
560
|
-
const
|
|
594
|
+
async function stateCommand({ tabid }) {
|
|
595
|
+
const browser = await connectBrowser();
|
|
561
596
|
try {
|
|
562
|
-
const
|
|
563
|
-
await injectIntoPage(
|
|
564
|
-
const state = await
|
|
597
|
+
const page = await getTargetPage(browser, tabid);
|
|
598
|
+
await injectIntoPage(page);
|
|
599
|
+
const state = await page.evaluate(async () => {
|
|
565
600
|
const url = document.URL;
|
|
566
601
|
const title = document.title;
|
|
567
602
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
568
603
|
let webmcpTools = [];
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
const toolsResult = await mcp.listTools();
|
|
573
|
-
webmcpTools = toolsResult?.tools || toolsResult || [];
|
|
574
|
-
}
|
|
575
|
-
if (typeof mcp.executeTool === "function") {
|
|
576
|
-
try {
|
|
577
|
-
const argsString = JSON.stringify({ action: "browserState" });
|
|
578
|
-
let stateRes = await mcp.executeTool("page-agent-tool", argsString);
|
|
579
|
-
if (typeof stateRes === "string") {
|
|
580
|
-
try {
|
|
581
|
-
stateRes = JSON.parse(stateRes);
|
|
582
|
-
} catch (e) {
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
if (stateRes && stateRes.content && stateRes.content.length > 0) {
|
|
586
|
-
const textContent = stateRes.content.map((c) => c.text).join("\\n");
|
|
587
|
-
const prefix = "\u6D4F\u89C8\u5668\u72B6\u6001: ";
|
|
588
|
-
if (textContent.startsWith(prefix)) {
|
|
589
|
-
try {
|
|
590
|
-
const jsonStr = textContent.substring(prefix.length);
|
|
591
|
-
const parsedState = JSON.parse(jsonStr);
|
|
592
|
-
contentData = parsedState.content;
|
|
593
|
-
} catch (e) {
|
|
594
|
-
contentData = textContent;
|
|
595
|
-
}
|
|
596
|
-
} else {
|
|
597
|
-
contentData = textContent;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
} catch (e) {
|
|
601
|
-
console.error("Snapshot error:", e.message);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
+
if (mcp && typeof mcp.listTools === "function") {
|
|
605
|
+
const toolsResult = await mcp.listTools();
|
|
606
|
+
webmcpTools = toolsResult?.tools || toolsResult || [];
|
|
604
607
|
}
|
|
605
|
-
return {
|
|
606
|
-
content: contentData,
|
|
607
|
-
url,
|
|
608
|
-
title,
|
|
609
|
-
webmcpTools
|
|
610
|
-
};
|
|
608
|
+
return { url, title, webmcpTools };
|
|
611
609
|
});
|
|
612
|
-
const pages = await
|
|
610
|
+
const pages = await browser.pages();
|
|
613
611
|
const tabs2 = await Promise.all(
|
|
614
612
|
pages.map(async (p) => {
|
|
615
613
|
const pUrl = p.url();
|
|
@@ -631,7 +629,7 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
631
629
|
tabs: tabs2.filter(Boolean)
|
|
632
630
|
};
|
|
633
631
|
} finally {
|
|
634
|
-
await
|
|
632
|
+
await browser.disconnect();
|
|
635
633
|
}
|
|
636
634
|
}
|
|
637
635
|
|
|
@@ -639,13 +637,13 @@ async function stateCommand({ tabid: tabid2 }) {
|
|
|
639
637
|
async function runCommand({
|
|
640
638
|
toolName,
|
|
641
639
|
argsJson,
|
|
642
|
-
tabid
|
|
640
|
+
tabid
|
|
643
641
|
}) {
|
|
644
|
-
const
|
|
642
|
+
const browser = await connectBrowser();
|
|
645
643
|
try {
|
|
646
|
-
const
|
|
644
|
+
const page = await getTargetPage(browser, tabid);
|
|
647
645
|
const cleanedArgs = argsJson.trim();
|
|
648
|
-
const
|
|
646
|
+
const result = await page.evaluate(async (name, inputString) => {
|
|
649
647
|
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
650
648
|
if (!mcp || typeof mcp.executeTool !== "function") {
|
|
651
649
|
throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
|
|
@@ -666,35 +664,6 @@ async function runCommand({
|
|
|
666
664
|
}
|
|
667
665
|
return res;
|
|
668
666
|
}, toolName, cleanedArgs);
|
|
669
|
-
return result2;
|
|
670
|
-
} finally {
|
|
671
|
-
await browser2.disconnect();
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// src/commands/evaluate.ts
|
|
676
|
-
async function evaluateCommand({
|
|
677
|
-
jsScript,
|
|
678
|
-
tabid
|
|
679
|
-
}) {
|
|
680
|
-
const browser = await connectBrowser();
|
|
681
|
-
try {
|
|
682
|
-
const page = await getTargetPage(browser, tabid);
|
|
683
|
-
const result = await page.evaluate(async (script) => {
|
|
684
|
-
try {
|
|
685
|
-
const result = eval(script);
|
|
686
|
-
if (result instanceof Promise) {
|
|
687
|
-
return await result;
|
|
688
|
-
}
|
|
689
|
-
return result;
|
|
690
|
-
} catch (error) {
|
|
691
|
-
return {
|
|
692
|
-
error: true,
|
|
693
|
-
message: error.message,
|
|
694
|
-
stack: error.stack
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
}, jsScript);
|
|
698
667
|
return result;
|
|
699
668
|
} finally {
|
|
700
669
|
await browser.disconnect();
|
|
@@ -719,114 +688,114 @@ function normalizeUrl(url) {
|
|
|
719
688
|
return url;
|
|
720
689
|
}
|
|
721
690
|
async function tabsOpenCommand(url) {
|
|
722
|
-
const
|
|
691
|
+
const browser = await connectBrowser();
|
|
723
692
|
try {
|
|
724
|
-
const
|
|
693
|
+
const page = await browser.newPage();
|
|
725
694
|
url = normalizeUrl(url);
|
|
726
|
-
await
|
|
727
|
-
await injectIntoPage(
|
|
695
|
+
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
696
|
+
await injectIntoPage(page);
|
|
728
697
|
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
729
698
|
return {
|
|
730
699
|
success: true,
|
|
731
|
-
tabid: await getPageTargetId(
|
|
732
|
-
url:
|
|
733
|
-
title: await
|
|
700
|
+
tabid: await getPageTargetId(page),
|
|
701
|
+
url: page.url(),
|
|
702
|
+
title: await page.title()
|
|
734
703
|
};
|
|
735
704
|
} finally {
|
|
736
|
-
await
|
|
705
|
+
await browser.disconnect();
|
|
737
706
|
}
|
|
738
707
|
}
|
|
739
|
-
async function tabsCloseCommand(
|
|
740
|
-
const
|
|
708
|
+
async function tabsCloseCommand(tabid) {
|
|
709
|
+
const browser = await connectBrowser();
|
|
741
710
|
try {
|
|
742
|
-
const target = findPageTargetByTabId(
|
|
711
|
+
const target = findPageTargetByTabId(browser, tabid);
|
|
743
712
|
if (!target) {
|
|
744
|
-
throw new Error(`Tab with targetId "${
|
|
713
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
745
714
|
}
|
|
746
|
-
const
|
|
747
|
-
if (!
|
|
748
|
-
throw new Error(`Tab with targetId "${
|
|
715
|
+
const page = await target.page();
|
|
716
|
+
if (!page) {
|
|
717
|
+
throw new Error(`Tab with targetId "${tabid}" not found.`);
|
|
749
718
|
}
|
|
750
|
-
const closedTabid = await getPageTargetId(
|
|
751
|
-
await
|
|
719
|
+
const closedTabid = await getPageTargetId(page).catch(() => tabid);
|
|
720
|
+
await page.close();
|
|
752
721
|
return {
|
|
753
722
|
success: true,
|
|
754
723
|
tabid: closedTabid
|
|
755
724
|
};
|
|
756
725
|
} finally {
|
|
757
|
-
await
|
|
726
|
+
await browser.disconnect();
|
|
758
727
|
}
|
|
759
728
|
}
|
|
760
|
-
async function tabsSwitchCommand(
|
|
761
|
-
const
|
|
729
|
+
async function tabsSwitchCommand(tabid) {
|
|
730
|
+
const browser = await connectBrowser();
|
|
762
731
|
try {
|
|
763
|
-
await activateTabById(
|
|
764
|
-
const
|
|
732
|
+
await activateTabById(browser, tabid);
|
|
733
|
+
const page = await getTargetPage(browser, tabid);
|
|
765
734
|
return {
|
|
766
735
|
success: true,
|
|
767
|
-
tabid: await getPageTargetId(
|
|
768
|
-
url:
|
|
769
|
-
title: await
|
|
736
|
+
tabid: await getPageTargetId(page),
|
|
737
|
+
url: page.url(),
|
|
738
|
+
title: await page.title()
|
|
770
739
|
};
|
|
771
740
|
} finally {
|
|
772
|
-
await
|
|
741
|
+
await browser.disconnect();
|
|
773
742
|
}
|
|
774
743
|
}
|
|
775
|
-
async function tabsBackCommand(
|
|
776
|
-
const
|
|
744
|
+
async function tabsBackCommand(tabid) {
|
|
745
|
+
const browser = await connectBrowser();
|
|
777
746
|
try {
|
|
778
|
-
const
|
|
779
|
-
const response = await
|
|
747
|
+
const page = await getTargetPage(browser, tabid);
|
|
748
|
+
const response = await page.goBack();
|
|
780
749
|
if (!response) {
|
|
781
750
|
return {
|
|
782
751
|
success: false,
|
|
783
752
|
error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
|
|
784
|
-
tabid: await getPageTargetId(
|
|
785
|
-
url:
|
|
786
|
-
title: await
|
|
753
|
+
tabid: await getPageTargetId(page).catch(() => tabid),
|
|
754
|
+
url: page.url(),
|
|
755
|
+
title: await page.title()
|
|
787
756
|
};
|
|
788
757
|
}
|
|
789
|
-
await injectIntoPage(
|
|
758
|
+
await injectIntoPage(page);
|
|
790
759
|
return {
|
|
791
760
|
success: true,
|
|
792
|
-
tabid: await getPageTargetId(
|
|
793
|
-
url:
|
|
794
|
-
title: await
|
|
761
|
+
tabid: await getPageTargetId(page),
|
|
762
|
+
url: page.url(),
|
|
763
|
+
title: await page.title()
|
|
795
764
|
};
|
|
796
765
|
} finally {
|
|
797
|
-
await
|
|
766
|
+
await browser.disconnect();
|
|
798
767
|
}
|
|
799
768
|
}
|
|
800
|
-
async function tabsForwardCommand(
|
|
801
|
-
const
|
|
769
|
+
async function tabsForwardCommand(tabid) {
|
|
770
|
+
const browser = await connectBrowser();
|
|
802
771
|
try {
|
|
803
|
-
const
|
|
804
|
-
const response = await
|
|
772
|
+
const page = await getTargetPage(browser, tabid);
|
|
773
|
+
const response = await page.goForward();
|
|
805
774
|
if (!response) {
|
|
806
775
|
return {
|
|
807
776
|
success: false,
|
|
808
777
|
error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
|
|
809
|
-
tabid: await getPageTargetId(
|
|
810
|
-
url:
|
|
811
|
-
title: await
|
|
778
|
+
tabid: await getPageTargetId(page).catch(() => tabid),
|
|
779
|
+
url: page.url(),
|
|
780
|
+
title: await page.title()
|
|
812
781
|
};
|
|
813
782
|
}
|
|
814
|
-
await injectIntoPage(
|
|
783
|
+
await injectIntoPage(page);
|
|
815
784
|
return {
|
|
816
785
|
success: true,
|
|
817
|
-
tabid: await getPageTargetId(
|
|
818
|
-
url:
|
|
819
|
-
title: await
|
|
786
|
+
tabid: await getPageTargetId(page),
|
|
787
|
+
url: page.url(),
|
|
788
|
+
title: await page.title()
|
|
820
789
|
};
|
|
821
790
|
} finally {
|
|
822
|
-
await
|
|
791
|
+
await browser.disconnect();
|
|
823
792
|
}
|
|
824
793
|
}
|
|
825
794
|
|
|
826
795
|
// package.json
|
|
827
796
|
var package_default = {
|
|
828
797
|
name: "@opentiny/webmcp-cli",
|
|
829
|
-
version: "0.0.
|
|
798
|
+
version: "0.0.2",
|
|
830
799
|
type: "module",
|
|
831
800
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
832
801
|
main: "dist/index.js",
|
|
@@ -884,8 +853,97 @@ function parseTabId(id) {
|
|
|
884
853
|
if (!id) return void 0;
|
|
885
854
|
return id;
|
|
886
855
|
}
|
|
887
|
-
function
|
|
856
|
+
function cleanOldLogs(baseDir, logDir) {
|
|
857
|
+
try {
|
|
858
|
+
const oldLogFile = import_path3.default.join(baseDir, "webmcp-cli.log");
|
|
859
|
+
if (import_fs3.default.existsSync(oldLogFile)) {
|
|
860
|
+
try {
|
|
861
|
+
import_fs3.default.unlinkSync(oldLogFile);
|
|
862
|
+
} catch {
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (import_fs3.default.existsSync(baseDir)) {
|
|
866
|
+
const baseFiles = import_fs3.default.readdirSync(baseDir);
|
|
867
|
+
for (const file of baseFiles) {
|
|
868
|
+
if (/^webmcp-cli-\d{4}-\d{2}-\d{2}\.log$/.test(file)) {
|
|
869
|
+
try {
|
|
870
|
+
import_fs3.default.unlinkSync(import_path3.default.join(baseDir, file));
|
|
871
|
+
} catch {
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
if (!import_fs3.default.existsSync(logDir)) return;
|
|
877
|
+
const files = import_fs3.default.readdirSync(logDir);
|
|
878
|
+
const logFilePattern = /^webmcp-cli-(\d{4}-\d{2}-\d{2})\.log$/;
|
|
879
|
+
const now = Date.now();
|
|
880
|
+
const maxAgeMs = 7 * 24 * 60 * 60 * 1e3;
|
|
881
|
+
for (const file of files) {
|
|
882
|
+
const match = file.match(logFilePattern);
|
|
883
|
+
if (match) {
|
|
884
|
+
const fileDateStr = match[1];
|
|
885
|
+
const fileDate = new Date(fileDateStr).getTime();
|
|
886
|
+
if (isNaN(fileDate)) continue;
|
|
887
|
+
if (now - fileDate > maxAgeMs) {
|
|
888
|
+
try {
|
|
889
|
+
import_fs3.default.unlinkSync(import_path3.default.join(logDir, file));
|
|
890
|
+
} catch {
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
} catch (e) {
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function writeLog(commandName, args, result, error) {
|
|
899
|
+
try {
|
|
900
|
+
const baseDir = process.env.WEBMCP_WORKSPACE || import_path3.default.join(import_os2.default.homedir(), ".webmcp_chrome_profile");
|
|
901
|
+
const logDir = import_path3.default.join(baseDir, "logs");
|
|
902
|
+
if (!import_fs3.default.existsSync(logDir)) {
|
|
903
|
+
import_fs3.default.mkdirSync(logDir, { recursive: true });
|
|
904
|
+
}
|
|
905
|
+
const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
906
|
+
const logFile = import_path3.default.join(logDir, `webmcp-cli-${todayStr}.log`);
|
|
907
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
908
|
+
let logText = `========================================
|
|
909
|
+
`;
|
|
910
|
+
logText += `[${timestamp}] COMMAND: ${commandName}
|
|
911
|
+
`;
|
|
912
|
+
logText += `ARGS:
|
|
913
|
+
${JSON.stringify(args, null, 2)}
|
|
914
|
+
|
|
915
|
+
`;
|
|
916
|
+
if (error) {
|
|
917
|
+
logText += `ERROR:
|
|
918
|
+
${error instanceof Error ? error.stack : String(error)}
|
|
919
|
+
`;
|
|
920
|
+
} else if (result) {
|
|
921
|
+
logText += `RESULT:
|
|
922
|
+
`;
|
|
923
|
+
try {
|
|
924
|
+
if (result && typeof result === "object") {
|
|
925
|
+
logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
|
|
926
|
+
} else {
|
|
927
|
+
logText += String(result).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
|
|
928
|
+
}
|
|
929
|
+
} catch {
|
|
930
|
+
logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
|
|
931
|
+
}
|
|
932
|
+
} else {
|
|
933
|
+
logText += `RESULT: null
|
|
934
|
+
`;
|
|
935
|
+
}
|
|
936
|
+
logText += `========================================
|
|
937
|
+
|
|
938
|
+
`;
|
|
939
|
+
import_fs3.default.appendFileSync(logFile, logText, "utf-8");
|
|
940
|
+
cleanOldLogs(baseDir, logDir);
|
|
941
|
+
} catch (e) {
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
function handleCommandError(error, commandName, args) {
|
|
888
945
|
const msg = error instanceof Error ? error.message : String(error);
|
|
946
|
+
writeLog(commandName, args, null, error);
|
|
889
947
|
console.error(import_picocolors2.default.red(`Error executing ${commandName} command: ${msg}`));
|
|
890
948
|
process.exit(1);
|
|
891
949
|
}
|
|
@@ -895,12 +953,14 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
|
|
|
895
953
|
process.env.WEBMCP_WORKSPACE = opts.workspace;
|
|
896
954
|
}
|
|
897
955
|
});
|
|
898
|
-
program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\
|
|
956
|
+
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) => {
|
|
957
|
+
const args = { tabid: parseTabId(options.tabid) };
|
|
899
958
|
try {
|
|
900
|
-
const
|
|
901
|
-
|
|
959
|
+
const result = await stateCommand(args);
|
|
960
|
+
writeLog("state", args, result);
|
|
961
|
+
console.log(JSON.stringify(result, null, 2));
|
|
902
962
|
} catch (error) {
|
|
903
|
-
handleCommandError(error, "state");
|
|
963
|
+
handleCommandError(error, "state", args);
|
|
904
964
|
}
|
|
905
965
|
});
|
|
906
966
|
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(
|
|
@@ -911,87 +971,92 @@ program.command("run <toolName> [args...]").description("\u5411\u6307\u5B9A\u987
|
|
|
911
971
|
@base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
|
|
912
972
|
\u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
|
|
913
973
|
).action(async (toolName, args, options) => {
|
|
974
|
+
let finalArgsJson = "";
|
|
975
|
+
const rawArgs = { toolName, args, file: options.file, tabid: options.tabid };
|
|
914
976
|
try {
|
|
915
977
|
let fileContent;
|
|
916
978
|
let fileBaseDir = process.cwd();
|
|
917
979
|
if (options.file) {
|
|
918
|
-
const filePath = (0,
|
|
980
|
+
const filePath = (0, import_path4.resolve)(process.cwd(), options.file);
|
|
919
981
|
fileBaseDir = getFileBaseDir(options.file);
|
|
920
982
|
try {
|
|
921
|
-
fileContent = (0,
|
|
983
|
+
fileContent = (0, import_fs4.readFileSync)(filePath, "utf-8");
|
|
922
984
|
} catch (e) {
|
|
923
985
|
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
|
|
924
986
|
}
|
|
925
987
|
}
|
|
926
|
-
|
|
927
|
-
const
|
|
988
|
+
finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir);
|
|
989
|
+
const result = await runCommand({
|
|
928
990
|
toolName,
|
|
929
991
|
argsJson: finalArgsJson,
|
|
930
992
|
tabid: parseTabId(options.tabid)
|
|
931
993
|
});
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
handleCommandError(error, "run");
|
|
935
|
-
}
|
|
936
|
-
});
|
|
937
|
-
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) => {
|
|
938
|
-
try {
|
|
939
|
-
const result2 = await evaluateCommand({
|
|
940
|
-
jsScript: jsScript2,
|
|
941
|
-
tabid: parseTabId(options.tabid)
|
|
942
|
-
});
|
|
943
|
-
console.log(JSON.stringify(result2, null, 2));
|
|
994
|
+
writeLog("run", { ...rawArgs, finalArgsJson }, result);
|
|
995
|
+
console.log(JSON.stringify(result, null, 2));
|
|
944
996
|
} catch (error) {
|
|
945
|
-
handleCommandError(error, "
|
|
997
|
+
handleCommandError(error, "run", { ...rawArgs, finalArgsJson });
|
|
946
998
|
}
|
|
947
999
|
});
|
|
948
1000
|
program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
|
|
1001
|
+
const args = { content };
|
|
949
1002
|
try {
|
|
950
1003
|
await setClipboard(content);
|
|
951
|
-
|
|
1004
|
+
const result = { success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" };
|
|
1005
|
+
writeLog("clipboard", args, result);
|
|
1006
|
+
console.log(JSON.stringify(result, null, 2));
|
|
952
1007
|
} catch (error) {
|
|
953
|
-
handleCommandError(error, "clipboard");
|
|
1008
|
+
handleCommandError(error, "clipboard", args);
|
|
954
1009
|
}
|
|
955
1010
|
});
|
|
956
1011
|
var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
|
|
957
1012
|
tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
|
|
1013
|
+
const args = { url };
|
|
958
1014
|
try {
|
|
959
|
-
const
|
|
960
|
-
|
|
1015
|
+
const result = await tabsOpenCommand(url);
|
|
1016
|
+
writeLog("tabs open", args, result);
|
|
1017
|
+
console.log(JSON.stringify(result, null, 2));
|
|
961
1018
|
} catch (error) {
|
|
962
|
-
handleCommandError(error, "tabs open");
|
|
1019
|
+
handleCommandError(error, "tabs open", args);
|
|
963
1020
|
}
|
|
964
1021
|
});
|
|
965
|
-
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
1022
|
+
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
|
|
1023
|
+
const args = { tabid };
|
|
966
1024
|
try {
|
|
967
|
-
const
|
|
968
|
-
|
|
1025
|
+
const result = await tabsCloseCommand(tabid);
|
|
1026
|
+
writeLog("tabs close", args, result);
|
|
1027
|
+
console.log(JSON.stringify(result, null, 2));
|
|
969
1028
|
} catch (error) {
|
|
970
|
-
handleCommandError(error, "tabs close");
|
|
1029
|
+
handleCommandError(error, "tabs close", args);
|
|
971
1030
|
}
|
|
972
1031
|
});
|
|
973
|
-
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (
|
|
1032
|
+
tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
|
|
1033
|
+
const args = { tabid };
|
|
974
1034
|
try {
|
|
975
|
-
const
|
|
976
|
-
|
|
1035
|
+
const result = await tabsSwitchCommand(tabid);
|
|
1036
|
+
writeLog("tabs switch", args, result);
|
|
1037
|
+
console.log(JSON.stringify(result, null, 2));
|
|
977
1038
|
} catch (error) {
|
|
978
|
-
handleCommandError(error, "tabs switch");
|
|
1039
|
+
handleCommandError(error, "tabs switch", args);
|
|
979
1040
|
}
|
|
980
1041
|
});
|
|
981
|
-
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (
|
|
1042
|
+
tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid) => {
|
|
1043
|
+
const args = { tabid: parseTabId(tabid) };
|
|
982
1044
|
try {
|
|
983
|
-
const
|
|
984
|
-
|
|
1045
|
+
const result = await tabsBackCommand(args.tabid);
|
|
1046
|
+
writeLog("tabs back", args, result);
|
|
1047
|
+
console.log(JSON.stringify(result, null, 2));
|
|
985
1048
|
} catch (error) {
|
|
986
|
-
handleCommandError(error, "tabs back");
|
|
1049
|
+
handleCommandError(error, "tabs back", args);
|
|
987
1050
|
}
|
|
988
1051
|
});
|
|
989
|
-
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (
|
|
1052
|
+
tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid) => {
|
|
1053
|
+
const args = { tabid: parseTabId(tabid) };
|
|
990
1054
|
try {
|
|
991
|
-
const
|
|
992
|
-
|
|
1055
|
+
const result = await tabsForwardCommand(args.tabid);
|
|
1056
|
+
writeLog("tabs forward", args, result);
|
|
1057
|
+
console.log(JSON.stringify(result, null, 2));
|
|
993
1058
|
} catch (error) {
|
|
994
|
-
handleCommandError(error, "tabs forward");
|
|
1059
|
+
handleCommandError(error, "tabs forward", args);
|
|
995
1060
|
}
|
|
996
1061
|
});
|
|
997
1062
|
program.parse(process.argv);
|