@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.cjs
CHANGED
|
@@ -184,21 +184,28 @@ ${hint}`);
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
function prepareRunArgsJson(args, fileContent, baseDir) {
|
|
187
|
-
let raw = "";
|
|
188
187
|
if (fileContent !== void 0) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (joined.startsWith("{") || looksLikeObjectBody(joined)) {
|
|
193
|
-
raw = joined;
|
|
194
|
-
} else if (args.every((arg) => arg.includes("="))) {
|
|
195
|
-
raw = buildArgsFromKeyValuePairs(args);
|
|
196
|
-
} else {
|
|
197
|
-
raw = joined;
|
|
188
|
+
const trimmed = fileContent.trim();
|
|
189
|
+
if (!trimmed) {
|
|
190
|
+
throw new Error("\u901A\u8FC7 -f/--file \u6307\u5B9A\u7684\u53C2\u6570\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
|
|
198
191
|
}
|
|
192
|
+
const expanded2 = expandFileRefs(trimmed, baseDir);
|
|
193
|
+
return parseArgsJson(expanded2);
|
|
199
194
|
}
|
|
200
|
-
if (
|
|
201
|
-
|
|
195
|
+
if (args.length === 0) {
|
|
196
|
+
return "{}";
|
|
197
|
+
}
|
|
198
|
+
const joined = joinShellArgs(args);
|
|
199
|
+
let raw = "";
|
|
200
|
+
if (joined.startsWith("{") || looksLikeObjectBody(joined)) {
|
|
201
|
+
raw = joined;
|
|
202
|
+
} else if (args.every((arg) => arg.includes("="))) {
|
|
203
|
+
raw = buildArgsFromKeyValuePairs(args);
|
|
204
|
+
} else {
|
|
205
|
+
raw = joined;
|
|
206
|
+
}
|
|
207
|
+
if (!raw.trim()) {
|
|
208
|
+
throw new Error("\u672A\u63D0\u4F9B\u6709\u6548\u53C2\u6570");
|
|
202
209
|
}
|
|
203
210
|
const expanded = expandFileRefs(raw, baseDir);
|
|
204
211
|
return parseArgsJson(expanded);
|
|
@@ -218,6 +225,30 @@ var __filename = (0, import_url2.fileURLToPath)(import_meta.url);
|
|
|
218
225
|
var __dirname = import_path2.default.dirname(__filename);
|
|
219
226
|
var CDP_PORT = 9222;
|
|
220
227
|
var CDP_URL = `http://localhost:${CDP_PORT}`;
|
|
228
|
+
function getWorkspaceDir() {
|
|
229
|
+
return process.env.WEBMCP_WORKSPACE || import_path2.default.join(import_os.default.homedir(), ".webmcp_chrome_profile");
|
|
230
|
+
}
|
|
231
|
+
function getLastTabIdFilePath() {
|
|
232
|
+
return import_path2.default.join(getWorkspaceDir(), ".last-tab-id");
|
|
233
|
+
}
|
|
234
|
+
function setLastActiveTabId(tabid) {
|
|
235
|
+
try {
|
|
236
|
+
const dir = getWorkspaceDir();
|
|
237
|
+
if (!import_fs2.default.existsSync(dir)) {
|
|
238
|
+
import_fs2.default.mkdirSync(dir, { recursive: true });
|
|
239
|
+
}
|
|
240
|
+
import_fs2.default.writeFileSync(getLastTabIdFilePath(), tabid, "utf-8");
|
|
241
|
+
} catch {
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function getLastActiveTabId() {
|
|
245
|
+
try {
|
|
246
|
+
const tabid = import_fs2.default.readFileSync(getLastTabIdFilePath(), "utf-8").trim();
|
|
247
|
+
return tabid || null;
|
|
248
|
+
} catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
221
252
|
function httpGet(url, timeoutMs = 1500) {
|
|
222
253
|
return new Promise((resolve3, reject) => {
|
|
223
254
|
const req = import_http.default.get(url, { timeout: timeoutMs }, (res) => {
|
|
@@ -476,33 +507,42 @@ async function getTargetPage(browser, tabid) {
|
|
|
476
507
|
}
|
|
477
508
|
targetPage = await target.page();
|
|
478
509
|
} else {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
510
|
+
const lastTabId = getLastActiveTabId();
|
|
511
|
+
if (lastTabId) {
|
|
512
|
+
const lastTarget = findPageTargetByTabId(browser, lastTabId);
|
|
513
|
+
if (lastTarget) {
|
|
514
|
+
targetPage = await lastTarget.page();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (!targetPage) {
|
|
518
|
+
try {
|
|
519
|
+
const urls = [
|
|
520
|
+
`http://localhost:${CDP_PORT}/json/list`,
|
|
521
|
+
`http://127.0.0.1:${CDP_PORT}/json/list`
|
|
522
|
+
];
|
|
523
|
+
let activeTargetId = null;
|
|
524
|
+
for (const url of urls) {
|
|
525
|
+
try {
|
|
526
|
+
const body = await httpGet(url, 1e3);
|
|
527
|
+
const targetsData = JSON.parse(body);
|
|
528
|
+
const active = targetsData.find((t) => t.type === "page" && !t.url.startsWith("devtools://"));
|
|
529
|
+
if (active) {
|
|
530
|
+
activeTargetId = active.id;
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
} catch {
|
|
493
534
|
}
|
|
494
|
-
} catch {
|
|
495
535
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
536
|
+
if (activeTargetId) {
|
|
537
|
+
for (const target of pageTargets) {
|
|
538
|
+
if (getTargetIdFromTarget(target) === activeTargetId) {
|
|
539
|
+
targetPage = await target.page();
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
502
542
|
}
|
|
503
543
|
}
|
|
544
|
+
} catch {
|
|
504
545
|
}
|
|
505
|
-
} catch {
|
|
506
546
|
}
|
|
507
547
|
if (!targetPage) {
|
|
508
548
|
const lastTarget = pageTargets[pageTargets.length - 1];
|
|
@@ -583,6 +623,12 @@ async function injectDomainTools(page) {
|
|
|
583
623
|
if (!import_fs2.default.existsSync(toolBundlePath)) {
|
|
584
624
|
return;
|
|
585
625
|
}
|
|
626
|
+
const bundleKey = bundleName.replace(/^webmcp-tools\//, "").replace(/\.js$/, "");
|
|
627
|
+
const flagKey = `__webmcptools_${bundleKey.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
628
|
+
const alreadyInjected = await page.evaluate((key) => !!window[key], flagKey).catch(() => false);
|
|
629
|
+
if (alreadyInjected) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
586
632
|
console.log(import_picocolors.default.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
|
|
587
633
|
const toolScript = import_fs2.default.readFileSync(toolBundlePath, "utf-8");
|
|
588
634
|
try {
|
|
@@ -611,6 +657,7 @@ async function stateCommand({ tabid }) {
|
|
|
611
657
|
}
|
|
612
658
|
return { url, title, webmcpTools };
|
|
613
659
|
});
|
|
660
|
+
const activeTabid = await getPageTargetId(page).catch(() => null);
|
|
614
661
|
const pages = await browser.pages();
|
|
615
662
|
const tabs2 = await Promise.all(
|
|
616
663
|
pages.map(async (p) => {
|
|
@@ -630,6 +677,7 @@ async function stateCommand({ tabid }) {
|
|
|
630
677
|
);
|
|
631
678
|
return {
|
|
632
679
|
...state,
|
|
680
|
+
activeTabid,
|
|
633
681
|
tabs: tabs2.filter(Boolean)
|
|
634
682
|
};
|
|
635
683
|
} finally {
|
|
@@ -646,28 +694,51 @@ async function runCommand({
|
|
|
646
694
|
const browser = await connectBrowser();
|
|
647
695
|
try {
|
|
648
696
|
const page = await getTargetPage(browser, tabid);
|
|
697
|
+
const urlBefore = page.url();
|
|
649
698
|
const cleanedArgs = argsJson.trim();
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
699
|
+
let result;
|
|
700
|
+
try {
|
|
701
|
+
result = await page.evaluate(async (name, inputString) => {
|
|
702
|
+
const mcp = navigator.modelContextTesting || navigator.modelContext;
|
|
703
|
+
if (!mcp || typeof mcp.executeTool !== "function") {
|
|
704
|
+
throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
|
|
705
|
+
}
|
|
706
|
+
let res = await mcp.executeTool(name, inputString);
|
|
707
|
+
if (typeof res === "string") {
|
|
708
|
+
try {
|
|
709
|
+
res = JSON.parse(res);
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (res === void 0 || res === null) {
|
|
714
|
+
throw new Error("\u5DE5\u5177 execute \u672A\u8FD4\u56DE\u7ED3\u679C\uFF0C\u8BF7\u68C0\u67E5\u5DE5\u5177\u5B9E\u73B0\u662F\u5426\u7F3A\u5C11 return");
|
|
715
|
+
}
|
|
716
|
+
if (typeof res === "object" && res.success === false) {
|
|
717
|
+
const failed = res;
|
|
718
|
+
throw new Error(failed.error || failed.message || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
|
|
719
|
+
}
|
|
720
|
+
return res;
|
|
721
|
+
}, toolName, cleanedArgs);
|
|
722
|
+
} catch (evalError) {
|
|
723
|
+
const errMsg = evalError instanceof Error ? evalError.message : String(evalError);
|
|
724
|
+
const isContextDestroyed = errMsg.includes("context was destroyed") || errMsg.includes("Execution context was destroyed") || errMsg.includes("Cannot read properties of null") && errMsg.includes("context");
|
|
725
|
+
if (isContextDestroyed) {
|
|
726
|
+
await new Promise((resolve3) => setTimeout(resolve3, 800));
|
|
727
|
+
let newUrl = "";
|
|
657
728
|
try {
|
|
658
|
-
|
|
729
|
+
newUrl = page.url();
|
|
659
730
|
} catch {
|
|
660
731
|
}
|
|
732
|
+
if (newUrl && newUrl !== urlBefore) {
|
|
733
|
+
return {
|
|
734
|
+
success: true,
|
|
735
|
+
message: `\u5DE5\u5177 ${toolName} \u6267\u884C\u5B8C\u6210\uFF0C\u9875\u9762\u5DF2\u5BFC\u822A\u81F3 ${newUrl}`,
|
|
736
|
+
navigatedTo: newUrl
|
|
737
|
+
};
|
|
738
|
+
}
|
|
661
739
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
if (typeof res === "object" && res.success === false) {
|
|
666
|
-
const failed = res;
|
|
667
|
-
throw new Error(failed.error || failed.message || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
|
|
668
|
-
}
|
|
669
|
-
return res;
|
|
670
|
-
}, toolName, cleanedArgs);
|
|
740
|
+
throw evalError;
|
|
741
|
+
}
|
|
671
742
|
return result;
|
|
672
743
|
} finally {
|
|
673
744
|
await browser.disconnect();
|
|
@@ -699,11 +770,19 @@ async function tabsOpenCommand(url) {
|
|
|
699
770
|
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
700
771
|
await injectIntoPage(page);
|
|
701
772
|
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
773
|
+
let tabid;
|
|
774
|
+
try {
|
|
775
|
+
tabid = await getPageTargetId(page);
|
|
776
|
+
await activateTabById(browser, tabid);
|
|
777
|
+
setLastActiveTabId(tabid);
|
|
778
|
+
} catch (e) {
|
|
779
|
+
console.warn("Best-effort tab activation failed:", e);
|
|
780
|
+
}
|
|
702
781
|
return {
|
|
703
782
|
success: true,
|
|
704
|
-
tabid
|
|
783
|
+
tabid,
|
|
705
784
|
url: page.url(),
|
|
706
|
-
title: await page.title()
|
|
785
|
+
title: await page.title().catch(() => "")
|
|
707
786
|
};
|
|
708
787
|
} finally {
|
|
709
788
|
await browser.disconnect();
|
|
@@ -735,9 +814,11 @@ async function tabsSwitchCommand(tabid) {
|
|
|
735
814
|
try {
|
|
736
815
|
await activateTabById(browser, tabid);
|
|
737
816
|
const page = await getTargetPage(browser, tabid);
|
|
817
|
+
const realTabid = await getPageTargetId(page);
|
|
818
|
+
setLastActiveTabId(realTabid);
|
|
738
819
|
return {
|
|
739
820
|
success: true,
|
|
740
|
-
tabid:
|
|
821
|
+
tabid: realTabid,
|
|
741
822
|
url: page.url(),
|
|
742
823
|
title: await page.title()
|
|
743
824
|
};
|
|
@@ -799,7 +880,7 @@ async function tabsForwardCommand(tabid) {
|
|
|
799
880
|
// package.json
|
|
800
881
|
var package_default = {
|
|
801
882
|
name: "@opentiny/webmcp-cli",
|
|
802
|
-
version: "0.0.
|
|
883
|
+
version: "0.0.4",
|
|
803
884
|
type: "module",
|
|
804
885
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
805
886
|
main: "dist/index.js",
|