@opentiny/webmcp-cli 0.0.3 → 0.0.4
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 +136 -55
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +136 -55
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +93 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +93 -39
- package/dist/index.js.map +1 -1
- package/dist/webmcp-tools/excalidraw.com.js +3 -1
- package/dist/webmcp-tools/juejin.cn.js +131 -62
- package/dist/webmcp-tools/my.oschina.net.js +4 -2
- package/dist/webmcp-tools/www.baidu.com.js +3 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -161,21 +161,28 @@ ${hint}`);
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
function prepareRunArgsJson(args, fileContent, baseDir) {
|
|
164
|
-
let raw = "";
|
|
165
164
|
if (fileContent !== void 0) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (joined.startsWith("{") || looksLikeObjectBody(joined)) {
|
|
170
|
-
raw = joined;
|
|
171
|
-
} else if (args.every((arg) => arg.includes("="))) {
|
|
172
|
-
raw = buildArgsFromKeyValuePairs(args);
|
|
173
|
-
} else {
|
|
174
|
-
raw = joined;
|
|
165
|
+
const trimmed = fileContent.trim();
|
|
166
|
+
if (!trimmed) {
|
|
167
|
+
throw new Error("\u901A\u8FC7 -f/--file \u6307\u5B9A\u7684\u53C2\u6570\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
|
|
175
168
|
}
|
|
169
|
+
const expanded2 = expandFileRefs(trimmed, baseDir);
|
|
170
|
+
return parseArgsJson(expanded2);
|
|
176
171
|
}
|
|
177
|
-
if (
|
|
178
|
-
|
|
172
|
+
if (args.length === 0) {
|
|
173
|
+
return "{}";
|
|
174
|
+
}
|
|
175
|
+
const joined = joinShellArgs(args);
|
|
176
|
+
let raw = "";
|
|
177
|
+
if (joined.startsWith("{") || looksLikeObjectBody(joined)) {
|
|
178
|
+
raw = joined;
|
|
179
|
+
} else if (args.every((arg) => arg.includes("="))) {
|
|
180
|
+
raw = buildArgsFromKeyValuePairs(args);
|
|
181
|
+
} else {
|
|
182
|
+
raw = joined;
|
|
183
|
+
}
|
|
184
|
+
if (!raw.trim()) {
|
|
185
|
+
throw new Error("\u672A\u63D0\u4F9B\u6709\u6548\u53C2\u6570");
|
|
179
186
|
}
|
|
180
187
|
const expanded = expandFileRefs(raw, baseDir);
|
|
181
188
|
return parseArgsJson(expanded);
|
|
@@ -194,6 +201,30 @@ var __filename = fileURLToPath2(import.meta.url);
|
|
|
194
201
|
var __dirname = path.dirname(__filename);
|
|
195
202
|
var CDP_PORT = 9222;
|
|
196
203
|
var CDP_URL = `http://localhost:${CDP_PORT}`;
|
|
204
|
+
function getWorkspaceDir() {
|
|
205
|
+
return process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), ".webmcp_chrome_profile");
|
|
206
|
+
}
|
|
207
|
+
function getLastTabIdFilePath() {
|
|
208
|
+
return path.join(getWorkspaceDir(), ".last-tab-id");
|
|
209
|
+
}
|
|
210
|
+
function setLastActiveTabId(tabid) {
|
|
211
|
+
try {
|
|
212
|
+
const dir = getWorkspaceDir();
|
|
213
|
+
if (!fs.existsSync(dir)) {
|
|
214
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
215
|
+
}
|
|
216
|
+
fs.writeFileSync(getLastTabIdFilePath(), tabid, "utf-8");
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function getLastActiveTabId() {
|
|
221
|
+
try {
|
|
222
|
+
const tabid = fs.readFileSync(getLastTabIdFilePath(), "utf-8").trim();
|
|
223
|
+
return tabid || null;
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
197
228
|
function httpGet(url, timeoutMs = 1500) {
|
|
198
229
|
return new Promise((resolve3, reject) => {
|
|
199
230
|
const req = http.get(url, { timeout: timeoutMs }, (res) => {
|
|
@@ -452,33 +483,42 @@ async function getTargetPage(browser, tabid) {
|
|
|
452
483
|
}
|
|
453
484
|
targetPage = await target.page();
|
|
454
485
|
} else {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
486
|
+
const lastTabId = getLastActiveTabId();
|
|
487
|
+
if (lastTabId) {
|
|
488
|
+
const lastTarget = findPageTargetByTabId(browser, lastTabId);
|
|
489
|
+
if (lastTarget) {
|
|
490
|
+
targetPage = await lastTarget.page();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (!targetPage) {
|
|
494
|
+
try {
|
|
495
|
+
const urls = [
|
|
496
|
+
`http://localhost:${CDP_PORT}/json/list`,
|
|
497
|
+
`http://127.0.0.1:${CDP_PORT}/json/list`
|
|
498
|
+
];
|
|
499
|
+
let activeTargetId = null;
|
|
500
|
+
for (const url of urls) {
|
|
501
|
+
try {
|
|
502
|
+
const body = await httpGet(url, 1e3);
|
|
503
|
+
const targetsData = JSON.parse(body);
|
|
504
|
+
const active = targetsData.find((t) => t.type === "page" && !t.url.startsWith("devtools://"));
|
|
505
|
+
if (active) {
|
|
506
|
+
activeTargetId = active.id;
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
} catch {
|
|
469
510
|
}
|
|
470
|
-
} catch {
|
|
471
511
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
512
|
+
if (activeTargetId) {
|
|
513
|
+
for (const target of pageTargets) {
|
|
514
|
+
if (getTargetIdFromTarget(target) === activeTargetId) {
|
|
515
|
+
targetPage = await target.page();
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
478
518
|
}
|
|
479
519
|
}
|
|
520
|
+
} catch {
|
|
480
521
|
}
|
|
481
|
-
} catch {
|
|
482
522
|
}
|
|
483
523
|
if (!targetPage) {
|
|
484
524
|
const lastTarget = pageTargets[pageTargets.length - 1];
|
|
@@ -559,6 +599,12 @@ async function injectDomainTools(page) {
|
|
|
559
599
|
if (!fs.existsSync(toolBundlePath)) {
|
|
560
600
|
return;
|
|
561
601
|
}
|
|
602
|
+
const bundleKey = bundleName.replace(/^webmcp-tools\//, "").replace(/\.js$/, "");
|
|
603
|
+
const flagKey = `__webmcptools_${bundleKey.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
604
|
+
const alreadyInjected = await page.evaluate((key) => !!window[key], flagKey).catch(() => false);
|
|
605
|
+
if (alreadyInjected) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
562
608
|
console.log(pc.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
|
|
563
609
|
const toolScript = fs.readFileSync(toolBundlePath, "utf-8");
|
|
564
610
|
try {
|
|
@@ -587,6 +633,7 @@ async function stateCommand({ tabid }) {
|
|
|
587
633
|
}
|
|
588
634
|
return { url, title, webmcpTools };
|
|
589
635
|
});
|
|
636
|
+
const activeTabid = await getPageTargetId(page).catch(() => null);
|
|
590
637
|
const pages = await browser.pages();
|
|
591
638
|
const tabs2 = await Promise.all(
|
|
592
639
|
pages.map(async (p) => {
|
|
@@ -606,6 +653,7 @@ async function stateCommand({ tabid }) {
|
|
|
606
653
|
);
|
|
607
654
|
return {
|
|
608
655
|
...state,
|
|
656
|
+
activeTabid,
|
|
609
657
|
tabs: tabs2.filter(Boolean)
|
|
610
658
|
};
|
|
611
659
|
} finally {
|
|
@@ -622,28 +670,51 @@ async function runCommand({
|
|
|
622
670
|
const browser = await connectBrowser();
|
|
623
671
|
try {
|
|
624
672
|
const page = await getTargetPage(browser, tabid);
|
|
673
|
+
const urlBefore = page.url();
|
|
625
674
|
const cleanedArgs = argsJson.trim();
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
675
|
+
let result;
|
|
676
|
+
try {
|
|
677
|
+
result = await page.evaluate(async (name, inputString) => {
|
|
678
|
+
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
679
|
+
if (!mcp || typeof mcp.executeTool !== "function") {
|
|
680
|
+
throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
|
|
681
|
+
}
|
|
682
|
+
let res = await mcp.executeTool(name, inputString);
|
|
683
|
+
if (typeof res === "string") {
|
|
684
|
+
try {
|
|
685
|
+
res = JSON.parse(res);
|
|
686
|
+
} catch {
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (res === void 0 || res === null) {
|
|
690
|
+
throw new Error("\u5DE5\u5177 execute \u672A\u8FD4\u56DE\u7ED3\u679C\uFF0C\u8BF7\u68C0\u67E5\u5DE5\u5177\u5B9E\u73B0\u662F\u5426\u7F3A\u5C11 return");
|
|
691
|
+
}
|
|
692
|
+
if (typeof res === "object" && res.success === false) {
|
|
693
|
+
const failed = res;
|
|
694
|
+
throw new Error(failed.error || failed.message || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
|
|
695
|
+
}
|
|
696
|
+
return res;
|
|
697
|
+
}, toolName, cleanedArgs);
|
|
698
|
+
} catch (evalError) {
|
|
699
|
+
const errMsg = evalError instanceof Error ? evalError.message : String(evalError);
|
|
700
|
+
const isContextDestroyed = errMsg.includes("context was destroyed") || errMsg.includes("Execution context was destroyed") || errMsg.includes("Cannot read properties of null") && errMsg.includes("context");
|
|
701
|
+
if (isContextDestroyed) {
|
|
702
|
+
await new Promise((resolve3) => setTimeout(resolve3, 800));
|
|
703
|
+
let newUrl = "";
|
|
633
704
|
try {
|
|
634
|
-
|
|
705
|
+
newUrl = page.url();
|
|
635
706
|
} catch {
|
|
636
707
|
}
|
|
708
|
+
if (newUrl && newUrl !== urlBefore) {
|
|
709
|
+
return {
|
|
710
|
+
success: true,
|
|
711
|
+
message: `\u5DE5\u5177 ${toolName} \u6267\u884C\u5B8C\u6210\uFF0C\u9875\u9762\u5DF2\u5BFC\u822A\u81F3 ${newUrl}`,
|
|
712
|
+
navigatedTo: newUrl
|
|
713
|
+
};
|
|
714
|
+
}
|
|
637
715
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
641
|
-
if (typeof res === "object" && res.success === false) {
|
|
642
|
-
const failed = res;
|
|
643
|
-
throw new Error(failed.error || failed.message || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
|
|
644
|
-
}
|
|
645
|
-
return res;
|
|
646
|
-
}, toolName, cleanedArgs);
|
|
716
|
+
throw evalError;
|
|
717
|
+
}
|
|
647
718
|
return result;
|
|
648
719
|
} finally {
|
|
649
720
|
await browser.disconnect();
|
|
@@ -675,11 +746,19 @@ async function tabsOpenCommand(url) {
|
|
|
675
746
|
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
676
747
|
await injectIntoPage(page);
|
|
677
748
|
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
749
|
+
let tabid;
|
|
750
|
+
try {
|
|
751
|
+
tabid = await getPageTargetId(page);
|
|
752
|
+
await activateTabById(browser, tabid);
|
|
753
|
+
setLastActiveTabId(tabid);
|
|
754
|
+
} catch (e) {
|
|
755
|
+
console.warn("Best-effort tab activation failed:", e);
|
|
756
|
+
}
|
|
678
757
|
return {
|
|
679
758
|
success: true,
|
|
680
|
-
tabid
|
|
759
|
+
tabid,
|
|
681
760
|
url: page.url(),
|
|
682
|
-
title: await page.title()
|
|
761
|
+
title: await page.title().catch(() => "")
|
|
683
762
|
};
|
|
684
763
|
} finally {
|
|
685
764
|
await browser.disconnect();
|
|
@@ -711,9 +790,11 @@ async function tabsSwitchCommand(tabid) {
|
|
|
711
790
|
try {
|
|
712
791
|
await activateTabById(browser, tabid);
|
|
713
792
|
const page = await getTargetPage(browser, tabid);
|
|
793
|
+
const realTabid = await getPageTargetId(page);
|
|
794
|
+
setLastActiveTabId(realTabid);
|
|
714
795
|
return {
|
|
715
796
|
success: true,
|
|
716
|
-
tabid:
|
|
797
|
+
tabid: realTabid,
|
|
717
798
|
url: page.url(),
|
|
718
799
|
title: await page.title()
|
|
719
800
|
};
|
|
@@ -775,7 +856,7 @@ async function tabsForwardCommand(tabid) {
|
|
|
775
856
|
// package.json
|
|
776
857
|
var package_default = {
|
|
777
858
|
name: "@opentiny/webmcp-cli",
|
|
778
|
-
version: "0.0.
|
|
859
|
+
version: "0.0.4",
|
|
779
860
|
type: "module",
|
|
780
861
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
781
862
|
main: "dist/index.js",
|