@opentiny/webmcp-cli 0.0.2 → 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 +202 -117
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +202 -117
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +159 -101
- 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 +159 -101
- 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);
|
|
171
|
+
}
|
|
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;
|
|
176
183
|
}
|
|
177
|
-
if (!raw) {
|
|
178
|
-
throw new Error("\
|
|
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) => {
|
|
@@ -240,6 +271,31 @@ async function checkCdpReady(url, retries = 3) {
|
|
|
240
271
|
}
|
|
241
272
|
return false;
|
|
242
273
|
}
|
|
274
|
+
async function findAvailableCdpUrl(addresses, retries = 2) {
|
|
275
|
+
for (const addr of addresses) {
|
|
276
|
+
if (await checkCdpReady(addr, retries)) {
|
|
277
|
+
return addr.replace("/json/version", "");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
async function connectPuppeteer(browserBaseUrl, attempt = 1, maxAttempts = 3) {
|
|
283
|
+
try {
|
|
284
|
+
return await promiseWithTimeout(
|
|
285
|
+
puppeteer.connect({ browserURL: browserBaseUrl, defaultViewport: null }),
|
|
286
|
+
1e4,
|
|
287
|
+
`puppeteer.connect to ${browserBaseUrl} timed out`
|
|
288
|
+
);
|
|
289
|
+
} catch (err) {
|
|
290
|
+
if (attempt >= maxAttempts) {
|
|
291
|
+
throw err;
|
|
292
|
+
}
|
|
293
|
+
const delayMs = 500 * attempt;
|
|
294
|
+
console.log(pc.yellow(`connectBrowser: \u7B2C ${attempt} \u6B21\u8FDE\u63A5\u5931\u8D25\uFF0C${delayMs}ms \u540E\u91CD\u8BD5...`));
|
|
295
|
+
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
296
|
+
return connectPuppeteer(browserBaseUrl, attempt + 1, maxAttempts);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
243
299
|
function getDefaultBrowserPath() {
|
|
244
300
|
const platform = os.platform();
|
|
245
301
|
if (platform === "darwin") {
|
|
@@ -286,72 +342,56 @@ async function startBrowserInBackground() {
|
|
|
286
342
|
}
|
|
287
343
|
console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
|
|
288
344
|
const userDataDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), ".webmcp_chrome_profile");
|
|
345
|
+
const launchArgs = [
|
|
346
|
+
`--remote-debugging-port=${CDP_PORT}`,
|
|
347
|
+
`--user-data-dir=${userDataDir}`,
|
|
348
|
+
"--no-first-run",
|
|
349
|
+
"--no-default-browser-check"
|
|
350
|
+
];
|
|
351
|
+
if (os.platform() === "win32") {
|
|
352
|
+
launchArgs.push("--remote-debugging-address=127.0.0.1");
|
|
353
|
+
}
|
|
289
354
|
const child = spawn(
|
|
290
355
|
browserInfo.path,
|
|
291
|
-
|
|
292
|
-
`--remote-debugging-port=${CDP_PORT}`,
|
|
293
|
-
`--user-data-dir=${userDataDir}`,
|
|
294
|
-
"--no-first-run",
|
|
295
|
-
"--no-default-browser-check"
|
|
296
|
-
],
|
|
356
|
+
launchArgs,
|
|
297
357
|
{
|
|
298
358
|
detached: true,
|
|
299
359
|
stdio: "ignore"
|
|
300
360
|
}
|
|
301
361
|
);
|
|
302
362
|
child.unref();
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
363
|
+
const pollUrls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`];
|
|
364
|
+
for (let i = 0; i < 30; i++) {
|
|
365
|
+
for (const url of pollUrls) {
|
|
366
|
+
try {
|
|
367
|
+
await httpGet(url, 1e3);
|
|
368
|
+
console.log(pc.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
|
|
369
|
+
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
370
|
+
return;
|
|
371
|
+
} catch {
|
|
313
372
|
}
|
|
314
|
-
} catch (e) {
|
|
315
373
|
}
|
|
316
374
|
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
317
375
|
}
|
|
318
376
|
throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
|
|
319
377
|
}
|
|
320
378
|
async function connectBrowser() {
|
|
321
|
-
const
|
|
322
|
-
try {
|
|
323
|
-
const info = typeof target._getTargetInfo === "function" ? target._getTargetInfo() : target;
|
|
324
|
-
const type = info.type || "";
|
|
325
|
-
const url = info.url || "";
|
|
326
|
-
if (type === "service_worker" || type === "shared_worker" || type === "iframe" || type === "other" || type === "webview" || type === "background_page") {
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
if (url.startsWith("devtools://") || url.startsWith("chrome-extension://")) {
|
|
330
|
-
return false;
|
|
331
|
-
}
|
|
332
|
-
return true;
|
|
333
|
-
} catch (e) {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
const portAddresses = [
|
|
379
|
+
const versionAddresses = [
|
|
338
380
|
`http://127.0.0.1:${CDP_PORT}/json/version`,
|
|
339
381
|
`http://localhost:${CDP_PORT}/json/version`
|
|
340
382
|
];
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const browserURL = addr.replace("/json/version", "");
|
|
383
|
+
const existingUrl = await findAvailableCdpUrl(versionAddresses, 2);
|
|
384
|
+
if (existingUrl) {
|
|
385
|
+
console.log(pc.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${existingUrl}...`));
|
|
345
386
|
try {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
349
|
-
1e4,
|
|
350
|
-
`puppeteer.connect to ${browserURL} timed out`
|
|
351
|
-
);
|
|
352
|
-
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
387
|
+
const browser = await connectPuppeteer(existingUrl);
|
|
388
|
+
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${existingUrl}`));
|
|
353
389
|
return browser;
|
|
354
|
-
} catch {
|
|
390
|
+
} catch (err) {
|
|
391
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
392
|
+
throw new Error(
|
|
393
|
+
`\u65E0\u6CD5\u8FDE\u63A5\u5230\u5DF2\u5728\u8FD0\u884C\u7684\u6D4F\u89C8\u5668\uFF08${existingUrl}\uFF09\uFF1A${msg}\u3002CDP \u7AEF\u53E3\u53EF\u7528\uFF0C\u8BF7\u52FF\u91CD\u590D\u542F\u52A8\u6D4F\u89C8\u5668\uFF1B\u53EF\u5173\u95ED\u591A\u4F59\u7A97\u53E3\u540E\u91CD\u8BD5\u3002`
|
|
394
|
+
);
|
|
355
395
|
}
|
|
356
396
|
}
|
|
357
397
|
console.log(pc.yellow(`connectBrowser: \u7AEF\u53E3 ${CDP_PORT} \u65E0\u54CD\u5E94\uFF0C\u6B63\u5728\u542F\u52A8\u65B0\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B...`));
|
|
@@ -362,23 +402,18 @@ async function connectBrowser() {
|
|
|
362
402
|
console.error(pc.red(`\u65E0\u6CD5\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
363
403
|
throw new Error("Browser launch failed.");
|
|
364
404
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
377
|
-
return browser;
|
|
378
|
-
} catch {
|
|
379
|
-
}
|
|
405
|
+
const launchedUrl = await findAvailableCdpUrl(versionAddresses, 5);
|
|
406
|
+
if (!launchedUrl) {
|
|
407
|
+
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`);
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
console.log(pc.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${launchedUrl}...`));
|
|
411
|
+
const browser = await connectPuppeteer(launchedUrl);
|
|
412
|
+
console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${launchedUrl}`));
|
|
413
|
+
return browser;
|
|
414
|
+
} catch {
|
|
415
|
+
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`);
|
|
380
416
|
}
|
|
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`);
|
|
382
417
|
}
|
|
383
418
|
async function getPageTargetId(page) {
|
|
384
419
|
const session = await page.target().createCDPSession();
|
|
@@ -448,33 +483,42 @@ async function getTargetPage(browser, tabid) {
|
|
|
448
483
|
}
|
|
449
484
|
targetPage = await target.page();
|
|
450
485
|
} else {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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 {
|
|
465
510
|
}
|
|
466
|
-
} catch {
|
|
467
511
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
512
|
+
if (activeTargetId) {
|
|
513
|
+
for (const target of pageTargets) {
|
|
514
|
+
if (getTargetIdFromTarget(target) === activeTargetId) {
|
|
515
|
+
targetPage = await target.page();
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
474
518
|
}
|
|
475
519
|
}
|
|
520
|
+
} catch {
|
|
476
521
|
}
|
|
477
|
-
} catch {
|
|
478
522
|
}
|
|
479
523
|
if (!targetPage) {
|
|
480
524
|
const lastTarget = pageTargets[pageTargets.length - 1];
|
|
@@ -555,6 +599,12 @@ async function injectDomainTools(page) {
|
|
|
555
599
|
if (!fs.existsSync(toolBundlePath)) {
|
|
556
600
|
return;
|
|
557
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
|
+
}
|
|
558
608
|
console.log(pc.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
|
|
559
609
|
const toolScript = fs.readFileSync(toolBundlePath, "utf-8");
|
|
560
610
|
try {
|
|
@@ -583,6 +633,7 @@ async function stateCommand({ tabid }) {
|
|
|
583
633
|
}
|
|
584
634
|
return { url, title, webmcpTools };
|
|
585
635
|
});
|
|
636
|
+
const activeTabid = await getPageTargetId(page).catch(() => null);
|
|
586
637
|
const pages = await browser.pages();
|
|
587
638
|
const tabs2 = await Promise.all(
|
|
588
639
|
pages.map(async (p) => {
|
|
@@ -602,6 +653,7 @@ async function stateCommand({ tabid }) {
|
|
|
602
653
|
);
|
|
603
654
|
return {
|
|
604
655
|
...state,
|
|
656
|
+
activeTabid,
|
|
605
657
|
tabs: tabs2.filter(Boolean)
|
|
606
658
|
};
|
|
607
659
|
} finally {
|
|
@@ -618,28 +670,51 @@ async function runCommand({
|
|
|
618
670
|
const browser = await connectBrowser();
|
|
619
671
|
try {
|
|
620
672
|
const page = await getTargetPage(browser, tabid);
|
|
673
|
+
const urlBefore = page.url();
|
|
621
674
|
const cleanedArgs = argsJson.trim();
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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 = "";
|
|
629
704
|
try {
|
|
630
|
-
|
|
705
|
+
newUrl = page.url();
|
|
631
706
|
} catch {
|
|
632
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
|
+
}
|
|
633
715
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
if (typeof res === "object" && res.success === false) {
|
|
638
|
-
const failed = res;
|
|
639
|
-
throw new Error(failed.error || failed.message || "\u5DE5\u5177\u6267\u884C\u5931\u8D25");
|
|
640
|
-
}
|
|
641
|
-
return res;
|
|
642
|
-
}, toolName, cleanedArgs);
|
|
716
|
+
throw evalError;
|
|
717
|
+
}
|
|
643
718
|
return result;
|
|
644
719
|
} finally {
|
|
645
720
|
await browser.disconnect();
|
|
@@ -671,11 +746,19 @@ async function tabsOpenCommand(url) {
|
|
|
671
746
|
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
672
747
|
await injectIntoPage(page);
|
|
673
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
|
+
}
|
|
674
757
|
return {
|
|
675
758
|
success: true,
|
|
676
|
-
tabid
|
|
759
|
+
tabid,
|
|
677
760
|
url: page.url(),
|
|
678
|
-
title: await page.title()
|
|
761
|
+
title: await page.title().catch(() => "")
|
|
679
762
|
};
|
|
680
763
|
} finally {
|
|
681
764
|
await browser.disconnect();
|
|
@@ -707,9 +790,11 @@ async function tabsSwitchCommand(tabid) {
|
|
|
707
790
|
try {
|
|
708
791
|
await activateTabById(browser, tabid);
|
|
709
792
|
const page = await getTargetPage(browser, tabid);
|
|
793
|
+
const realTabid = await getPageTargetId(page);
|
|
794
|
+
setLastActiveTabId(realTabid);
|
|
710
795
|
return {
|
|
711
796
|
success: true,
|
|
712
|
-
tabid:
|
|
797
|
+
tabid: realTabid,
|
|
713
798
|
url: page.url(),
|
|
714
799
|
title: await page.title()
|
|
715
800
|
};
|
|
@@ -771,7 +856,7 @@ async function tabsForwardCommand(tabid) {
|
|
|
771
856
|
// package.json
|
|
772
857
|
var package_default = {
|
|
773
858
|
name: "@opentiny/webmcp-cli",
|
|
774
|
-
version: "0.0.
|
|
859
|
+
version: "0.0.4",
|
|
775
860
|
type: "module",
|
|
776
861
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
777
862
|
main: "dist/index.js",
|