@opentiny/webmcp-cli 0.0.1-alpha.6 → 0.0.3

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 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 import_fs3 = require("fs");
30
- var import_path3 = require("path");
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");
@@ -261,6 +264,31 @@ async function checkCdpReady(url, retries = 3) {
261
264
  }
262
265
  return false;
263
266
  }
267
+ async function findAvailableCdpUrl(addresses, retries = 2) {
268
+ for (const addr of addresses) {
269
+ if (await checkCdpReady(addr, retries)) {
270
+ return addr.replace("/json/version", "");
271
+ }
272
+ }
273
+ return null;
274
+ }
275
+ async function connectPuppeteer(browserBaseUrl, attempt = 1, maxAttempts = 3) {
276
+ try {
277
+ return await promiseWithTimeout(
278
+ import_puppeteer_core.default.connect({ browserURL: browserBaseUrl, defaultViewport: null }),
279
+ 1e4,
280
+ `puppeteer.connect to ${browserBaseUrl} timed out`
281
+ );
282
+ } catch (err) {
283
+ if (attempt >= maxAttempts) {
284
+ throw err;
285
+ }
286
+ const delayMs = 500 * attempt;
287
+ console.log(import_picocolors.default.yellow(`connectBrowser: \u7B2C ${attempt} \u6B21\u8FDE\u63A5\u5931\u8D25\uFF0C${delayMs}ms \u540E\u91CD\u8BD5...`));
288
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
289
+ return connectPuppeteer(browserBaseUrl, attempt + 1, maxAttempts);
290
+ }
291
+ }
264
292
  function getDefaultBrowserPath() {
265
293
  const platform = import_os.default.platform();
266
294
  if (platform === "darwin") {
@@ -307,72 +335,56 @@ async function startBrowserInBackground() {
307
335
  }
308
336
  console.log(import_picocolors.default.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
309
337
  const userDataDir = process.env.WEBMCP_WORKSPACE || import_path2.default.join(import_os.default.homedir(), ".webmcp_chrome_profile");
338
+ const launchArgs = [
339
+ `--remote-debugging-port=${CDP_PORT}`,
340
+ `--user-data-dir=${userDataDir}`,
341
+ "--no-first-run",
342
+ "--no-default-browser-check"
343
+ ];
344
+ if (import_os.default.platform() === "win32") {
345
+ launchArgs.push("--remote-debugging-address=127.0.0.1");
346
+ }
310
347
  const child = (0, import_child_process.spawn)(
311
348
  browserInfo.path,
312
- [
313
- `--remote-debugging-port=${CDP_PORT}`,
314
- `--user-data-dir=${userDataDir}`,
315
- "--no-first-run",
316
- "--no-default-browser-check"
317
- ],
349
+ launchArgs,
318
350
  {
319
351
  detached: true,
320
352
  stdio: "ignore"
321
353
  }
322
354
  );
323
355
  child.unref();
324
- for (let i = 0; i < 20; i++) {
325
- try {
326
- const urls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`];
327
- for (const url of urls) {
328
- try {
329
- await httpGet(url, 1e3);
330
- console.log(import_picocolors.default.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
331
- return;
332
- } catch (err) {
333
- }
356
+ const pollUrls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`];
357
+ for (let i = 0; i < 30; i++) {
358
+ for (const url of pollUrls) {
359
+ try {
360
+ await httpGet(url, 1e3);
361
+ console.log(import_picocolors.default.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
362
+ await new Promise((resolve3) => setTimeout(resolve3, 500));
363
+ return;
364
+ } catch {
334
365
  }
335
- } catch (e) {
336
366
  }
337
367
  await new Promise((resolve3) => setTimeout(resolve3, 500));
338
368
  }
339
369
  throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
340
370
  }
341
371
  async function connectBrowser() {
342
- const targetFilter = (target) => {
343
- try {
344
- const info = typeof target._getTargetInfo === "function" ? target._getTargetInfo() : target;
345
- const type = info.type || "";
346
- const url = info.url || "";
347
- if (type === "service_worker" || type === "shared_worker" || type === "iframe" || type === "other" || type === "webview" || type === "background_page") {
348
- return false;
349
- }
350
- if (url.startsWith("devtools://") || url.startsWith("chrome-extension://")) {
351
- return false;
352
- }
353
- return true;
354
- } catch (e) {
355
- return false;
356
- }
357
- };
358
- const portAddresses = [
372
+ const versionAddresses = [
359
373
  `http://127.0.0.1:${CDP_PORT}/json/version`,
360
374
  `http://localhost:${CDP_PORT}/json/version`
361
375
  ];
362
- for (const addr of portAddresses) {
363
- const isReady = await checkCdpReady(addr, 2);
364
- if (!isReady) continue;
365
- const browserURL = addr.replace("/json/version", "");
376
+ const existingUrl = await findAvailableCdpUrl(versionAddresses, 2);
377
+ if (existingUrl) {
378
+ console.log(import_picocolors.default.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${existingUrl}...`));
366
379
  try {
367
- 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 browser2 = await promiseWithTimeout(
369
- import_puppeteer_core.default.connect({ browserURL, defaultViewport: null, targetFilter }),
370
- 1e4,
371
- `puppeteer.connect to ${browserURL} timed out`
380
+ const browser = await connectPuppeteer(existingUrl);
381
+ console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${existingUrl}`));
382
+ return browser;
383
+ } catch (err) {
384
+ const msg = err instanceof Error ? err.message : String(err);
385
+ throw new Error(
386
+ `\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`
372
387
  );
373
- console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
374
- return browser2;
375
- } catch {
376
388
  }
377
389
  }
378
390
  console.log(import_picocolors.default.yellow(`connectBrowser: \u7AEF\u53E3 ${CDP_PORT} \u65E0\u54CD\u5E94\uFF0C\u6B63\u5728\u542F\u52A8\u65B0\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B...`));
@@ -383,26 +395,21 @@ async function connectBrowser() {
383
395
  console.error(import_picocolors.default.red(`\u65E0\u6CD5\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
384
396
  throw new Error("Browser launch failed.");
385
397
  }
386
- for (const addr of portAddresses) {
387
- const isReady = await checkCdpReady(addr, 3);
388
- if (!isReady) continue;
389
- const browserURL = addr.replace("/json/version", "");
390
- try {
391
- console.log(import_picocolors.default.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
392
- const browser2 = await promiseWithTimeout(
393
- import_puppeteer_core.default.connect({ browserURL, defaultViewport: null, targetFilter }),
394
- 1e4,
395
- `puppeteer.connect to ${browserURL} after launch timed out`
396
- );
397
- console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
398
- return browser2;
399
- } catch {
400
- }
398
+ const launchedUrl = await findAvailableCdpUrl(versionAddresses, 5);
399
+ if (!launchedUrl) {
400
+ 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`);
401
+ }
402
+ try {
403
+ console.log(import_picocolors.default.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${launchedUrl}...`));
404
+ const browser = await connectPuppeteer(launchedUrl);
405
+ console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${launchedUrl}`));
406
+ return browser;
407
+ } catch {
408
+ 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`);
401
409
  }
402
- 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
410
  }
404
- async function getPageTargetId(page2) {
405
- const session = await page2.target().createCDPSession();
411
+ async function getPageTargetId(page) {
412
+ const session = await page.target().createCDPSession();
406
413
  try {
407
414
  const { targetInfo } = await session.send("Target.getTargetInfo");
408
415
  return targetInfo.targetId;
@@ -411,8 +418,8 @@ async function getPageTargetId(page2) {
411
418
  });
412
419
  }
413
420
  }
414
- function getPageTargets(browser2) {
415
- return browser2.targets().filter((t) => {
421
+ function getPageTargets(browser) {
422
+ return browser.targets().filter((t) => {
416
423
  try {
417
424
  const type = (typeof t.type === "function" ? t.type() : t.type) || "";
418
425
  const url = (typeof t.url === "function" ? t.url() : t.url) || "";
@@ -425,26 +432,26 @@ function getPageTargets(browser2) {
425
432
  function getTargetIdFromTarget(target) {
426
433
  return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
427
434
  }
428
- function findPageTargetByTabId(browser2, tabid2) {
429
- const pageTargets = getPageTargets(browser2);
435
+ function findPageTargetByTabId(browser, tabid) {
436
+ const pageTargets = getPageTargets(browser);
430
437
  return pageTargets.find((t) => {
431
438
  const tid = getTargetIdFromTarget(t);
432
- return tid === tabid2 || tid.includes(tabid2);
439
+ return tid === tabid || tid.includes(tabid);
433
440
  }) ?? null;
434
441
  }
435
- async function activateTabById(browser2, tabid2) {
436
- const target = findPageTargetByTabId(browser2, tabid2);
442
+ async function activateTabById(browser, tabid) {
443
+ const target = findPageTargetByTabId(browser, tabid);
437
444
  if (!target) {
438
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
445
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
439
446
  }
440
447
  const realTabId = getTargetIdFromTarget(target);
441
- const pages = await browser2.pages();
448
+ const pages = await browser.pages();
442
449
  let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
443
450
  if (!sessionPage && pages.length > 0) {
444
451
  sessionPage = pages[0];
445
452
  }
446
453
  if (!sessionPage) {
447
- sessionPage = await browser2.newPage();
454
+ sessionPage = await browser.newPage();
448
455
  }
449
456
  const session = await sessionPage.createCDPSession();
450
457
  try {
@@ -454,18 +461,18 @@ async function activateTabById(browser2, tabid2) {
454
461
  });
455
462
  }
456
463
  }
457
- async function getTargetPage(browser2, tabid2) {
458
- const pageTargets = getPageTargets(browser2);
464
+ async function getTargetPage(browser, tabid) {
465
+ const pageTargets = getPageTargets(browser);
459
466
  if (pageTargets.length === 0) {
460
- const newPage = await browser2.newPage();
467
+ const newPage = await browser.newPage();
461
468
  await injectWebMCPPolyfillAndTools(newPage);
462
469
  return newPage;
463
470
  }
464
471
  let targetPage = null;
465
- if (tabid2 !== void 0) {
466
- const target = findPageTargetByTabId(browser2, tabid2);
472
+ if (tabid !== void 0) {
473
+ const target = findPageTargetByTabId(browser, tabid);
467
474
  if (!target) {
468
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
475
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
469
476
  }
470
477
  targetPage = await target.page();
471
478
  } else {
@@ -508,11 +515,11 @@ async function getTargetPage(browser2, tabid2) {
508
515
  await injectWebMCPPolyfillAndTools(targetPage);
509
516
  return targetPage;
510
517
  }
511
- async function injectIntoPage(page2) {
512
- await injectWebMCPPolyfillAndTools(page2, true);
518
+ async function injectIntoPage(page) {
519
+ await injectWebMCPPolyfillAndTools(page, true);
513
520
  }
514
- async function injectWebMCPPolyfillAndTools(page2, force = false) {
515
- const polyfillReady = !force && await page2.evaluate(() => {
521
+ async function injectWebMCPPolyfillAndTools(page, force = false) {
522
+ const polyfillReady = !force && await page.evaluate(() => {
516
523
  return !!window.__webmcpcli_init;
517
524
  }).catch(() => false);
518
525
  if (!polyfillReady) {
@@ -523,31 +530,63 @@ async function injectWebMCPPolyfillAndTools(page2, force = false) {
523
530
  }
524
531
  const scriptContent = import_fs2.default.readFileSync(injectScriptPath, "utf-8");
525
532
  try {
526
- await page2.evaluate(scriptContent);
533
+ await page.evaluate(scriptContent);
527
534
  } catch (err) {
528
535
  const msg = err instanceof Error ? err.message : String(err);
529
536
  throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
530
537
  }
531
- await new Promise((resolve3) => setTimeout(resolve3, 300));
538
+ await page.waitForFunction(() => {
539
+ const mcp = navigator.modelContext;
540
+ return mcp && typeof mcp.registerTool === "function";
541
+ }, { timeout: 1e4 }).catch(() => {
542
+ console.warn("\u7B49\u5F85 WebMCP registerTool \u63A5\u53E3\u5C31\u7EEA\u8D85\u65F6\uFF0C\u6B63\u5728\u7EE7\u7EED\u6CE8\u5165...");
543
+ });
532
544
  }
533
- await injectDomainTools(page2);
545
+ await injectDomainTools(page);
534
546
  }
535
- async function injectDomainTools(page2) {
547
+ function getToolsBundleName(hostname) {
548
+ const normalized = hostname.split(":")[0].toLowerCase();
549
+ const toolsDir = import_path2.default.resolve(__dirname, "webmcp-tools");
550
+ if (!import_fs2.default.existsSync(toolsDir)) {
551
+ return null;
552
+ }
553
+ let files = [];
554
+ try {
555
+ files = import_fs2.default.readdirSync(toolsDir);
556
+ } catch {
557
+ return null;
558
+ }
559
+ const supportedDomains = files.filter((f) => f.endsWith(".js")).map((f) => f.slice(0, -3));
560
+ if (supportedDomains.includes(normalized)) {
561
+ return `webmcp-tools/${normalized}.js`;
562
+ }
563
+ for (const domain of supportedDomains) {
564
+ if (normalized.endsWith("." + domain)) {
565
+ return `webmcp-tools/${domain}.js`;
566
+ }
567
+ }
568
+ return null;
569
+ }
570
+ async function injectDomainTools(page) {
536
571
  let hostname;
537
572
  try {
538
- const url = new URL(page2.url());
573
+ const url = new URL(page.url());
539
574
  hostname = url.hostname;
540
575
  } catch {
541
576
  return;
542
577
  }
543
- const toolBundlePath = import_path2.default.resolve(__dirname, "webmcp-tools", `${hostname}.js`);
578
+ const bundleName = getToolsBundleName(hostname);
579
+ if (!bundleName) {
580
+ return;
581
+ }
582
+ const toolBundlePath = import_path2.default.resolve(__dirname, bundleName);
544
583
  if (!import_fs2.default.existsSync(toolBundlePath)) {
545
584
  return;
546
585
  }
547
586
  console.log(import_picocolors.default.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
548
587
  const toolScript = import_fs2.default.readFileSync(toolBundlePath, "utf-8");
549
588
  try {
550
- await page2.evaluate(toolScript);
589
+ await page.evaluate(toolScript);
551
590
  console.log(import_picocolors.default.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
552
591
  } catch (err) {
553
592
  const msg = err instanceof Error ? err.message : String(err);
@@ -556,60 +595,23 @@ async function injectDomainTools(page2) {
556
595
  }
557
596
 
558
597
  // src/commands/state.ts
559
- async function stateCommand({ tabid: tabid2 }) {
560
- const browser2 = await connectBrowser();
598
+ async function stateCommand({ tabid }) {
599
+ const browser = await connectBrowser();
561
600
  try {
562
- const page2 = await getTargetPage(browser2, tabid2);
563
- await injectIntoPage(page2);
564
- const state = await page2.evaluate(async () => {
601
+ const page = await getTargetPage(browser, tabid);
602
+ await injectIntoPage(page);
603
+ const state = await page.evaluate(async () => {
565
604
  const url = document.URL;
566
605
  const title = document.title;
567
606
  const mcp = navigator.modelContextTesting || navigator.modelContext;
568
607
  let webmcpTools = [];
569
- let contentData = `\u9875\u9762\u5DF2\u51C6\u5907\u597D: ${title}`;
570
- if (mcp) {
571
- if (typeof mcp.listTools === "function") {
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
- }
608
+ if (mcp && typeof mcp.listTools === "function") {
609
+ const toolsResult = await mcp.listTools();
610
+ webmcpTools = toolsResult?.tools || toolsResult || [];
604
611
  }
605
- return {
606
- content: contentData,
607
- url,
608
- title,
609
- webmcpTools
610
- };
612
+ return { url, title, webmcpTools };
611
613
  });
612
- const pages = await browser2.pages();
614
+ const pages = await browser.pages();
613
615
  const tabs2 = await Promise.all(
614
616
  pages.map(async (p) => {
615
617
  const pUrl = p.url();
@@ -631,7 +633,7 @@ async function stateCommand({ tabid: tabid2 }) {
631
633
  tabs: tabs2.filter(Boolean)
632
634
  };
633
635
  } finally {
634
- await browser2.disconnect();
636
+ await browser.disconnect();
635
637
  }
636
638
  }
637
639
 
@@ -639,13 +641,13 @@ async function stateCommand({ tabid: tabid2 }) {
639
641
  async function runCommand({
640
642
  toolName,
641
643
  argsJson,
642
- tabid: tabid2
644
+ tabid
643
645
  }) {
644
- const browser2 = await connectBrowser();
646
+ const browser = await connectBrowser();
645
647
  try {
646
- const page2 = await getTargetPage(browser2, tabid2);
648
+ const page = await getTargetPage(browser, tabid);
647
649
  const cleanedArgs = argsJson.trim();
648
- const result2 = await page2.evaluate(async (name, inputString) => {
650
+ const result = await page.evaluate(async (name, inputString) => {
649
651
  const mcp = navigator.modelContextTesting || navigator.modelContext;
650
652
  if (!mcp || typeof mcp.executeTool !== "function") {
651
653
  throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
@@ -666,35 +668,6 @@ async function runCommand({
666
668
  }
667
669
  return res;
668
670
  }, 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
671
  return result;
699
672
  } finally {
700
673
  await browser.disconnect();
@@ -719,114 +692,114 @@ function normalizeUrl(url) {
719
692
  return url;
720
693
  }
721
694
  async function tabsOpenCommand(url) {
722
- const browser2 = await connectBrowser();
695
+ const browser = await connectBrowser();
723
696
  try {
724
- const page2 = await browser2.newPage();
697
+ const page = await browser.newPage();
725
698
  url = normalizeUrl(url);
726
- await page2.goto(url, { waitUntil: "domcontentloaded" });
727
- await injectIntoPage(page2);
699
+ await page.goto(url, { waitUntil: "domcontentloaded" });
700
+ await injectIntoPage(page);
728
701
  await new Promise((resolve3) => setTimeout(resolve3, 500));
729
702
  return {
730
703
  success: true,
731
- tabid: await getPageTargetId(page2),
732
- url: page2.url(),
733
- title: await page2.title()
704
+ tabid: await getPageTargetId(page),
705
+ url: page.url(),
706
+ title: await page.title()
734
707
  };
735
708
  } finally {
736
- await browser2.disconnect();
709
+ await browser.disconnect();
737
710
  }
738
711
  }
739
- async function tabsCloseCommand(tabid2) {
740
- const browser2 = await connectBrowser();
712
+ async function tabsCloseCommand(tabid) {
713
+ const browser = await connectBrowser();
741
714
  try {
742
- const target = findPageTargetByTabId(browser2, tabid2);
715
+ const target = findPageTargetByTabId(browser, tabid);
743
716
  if (!target) {
744
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
717
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
745
718
  }
746
- const page2 = await target.page();
747
- if (!page2) {
748
- throw new Error(`Tab with targetId "${tabid2}" not found.`);
719
+ const page = await target.page();
720
+ if (!page) {
721
+ throw new Error(`Tab with targetId "${tabid}" not found.`);
749
722
  }
750
- const closedTabid = await getPageTargetId(page2).catch(() => tabid2);
751
- await page2.close();
723
+ const closedTabid = await getPageTargetId(page).catch(() => tabid);
724
+ await page.close();
752
725
  return {
753
726
  success: true,
754
727
  tabid: closedTabid
755
728
  };
756
729
  } finally {
757
- await browser2.disconnect();
730
+ await browser.disconnect();
758
731
  }
759
732
  }
760
- async function tabsSwitchCommand(tabid2) {
761
- const browser2 = await connectBrowser();
733
+ async function tabsSwitchCommand(tabid) {
734
+ const browser = await connectBrowser();
762
735
  try {
763
- await activateTabById(browser2, tabid2);
764
- const page2 = await getTargetPage(browser2, tabid2);
736
+ await activateTabById(browser, tabid);
737
+ const page = await getTargetPage(browser, tabid);
765
738
  return {
766
739
  success: true,
767
- tabid: await getPageTargetId(page2),
768
- url: page2.url(),
769
- title: await page2.title()
740
+ tabid: await getPageTargetId(page),
741
+ url: page.url(),
742
+ title: await page.title()
770
743
  };
771
744
  } finally {
772
- await browser2.disconnect();
745
+ await browser.disconnect();
773
746
  }
774
747
  }
775
- async function tabsBackCommand(tabid2) {
776
- const browser2 = await connectBrowser();
748
+ async function tabsBackCommand(tabid) {
749
+ const browser = await connectBrowser();
777
750
  try {
778
- const page2 = await getTargetPage(browser2, tabid2);
779
- const response = await page2.goBack();
751
+ const page = await getTargetPage(browser, tabid);
752
+ const response = await page.goBack();
780
753
  if (!response) {
781
754
  return {
782
755
  success: false,
783
756
  error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
784
- tabid: await getPageTargetId(page2).catch(() => tabid2),
785
- url: page2.url(),
786
- title: await page2.title()
757
+ tabid: await getPageTargetId(page).catch(() => tabid),
758
+ url: page.url(),
759
+ title: await page.title()
787
760
  };
788
761
  }
789
- await injectIntoPage(page2);
762
+ await injectIntoPage(page);
790
763
  return {
791
764
  success: true,
792
- tabid: await getPageTargetId(page2),
793
- url: page2.url(),
794
- title: await page2.title()
765
+ tabid: await getPageTargetId(page),
766
+ url: page.url(),
767
+ title: await page.title()
795
768
  };
796
769
  } finally {
797
- await browser2.disconnect();
770
+ await browser.disconnect();
798
771
  }
799
772
  }
800
- async function tabsForwardCommand(tabid2) {
801
- const browser2 = await connectBrowser();
773
+ async function tabsForwardCommand(tabid) {
774
+ const browser = await connectBrowser();
802
775
  try {
803
- const page2 = await getTargetPage(browser2, tabid2);
804
- const response = await page2.goForward();
776
+ const page = await getTargetPage(browser, tabid);
777
+ const response = await page.goForward();
805
778
  if (!response) {
806
779
  return {
807
780
  success: false,
808
781
  error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
809
- tabid: await getPageTargetId(page2).catch(() => tabid2),
810
- url: page2.url(),
811
- title: await page2.title()
782
+ tabid: await getPageTargetId(page).catch(() => tabid),
783
+ url: page.url(),
784
+ title: await page.title()
812
785
  };
813
786
  }
814
- await injectIntoPage(page2);
787
+ await injectIntoPage(page);
815
788
  return {
816
789
  success: true,
817
- tabid: await getPageTargetId(page2),
818
- url: page2.url(),
819
- title: await page2.title()
790
+ tabid: await getPageTargetId(page),
791
+ url: page.url(),
792
+ title: await page.title()
820
793
  };
821
794
  } finally {
822
- await browser2.disconnect();
795
+ await browser.disconnect();
823
796
  }
824
797
  }
825
798
 
826
799
  // package.json
827
800
  var package_default = {
828
801
  name: "@opentiny/webmcp-cli",
829
- version: "0.0.1-alpha.6",
802
+ version: "0.0.3",
830
803
  type: "module",
831
804
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
832
805
  main: "dist/index.js",
@@ -884,8 +857,97 @@ function parseTabId(id) {
884
857
  if (!id) return void 0;
885
858
  return id;
886
859
  }
887
- function handleCommandError(error, commandName) {
860
+ function cleanOldLogs(baseDir, logDir) {
861
+ try {
862
+ const oldLogFile = import_path3.default.join(baseDir, "webmcp-cli.log");
863
+ if (import_fs3.default.existsSync(oldLogFile)) {
864
+ try {
865
+ import_fs3.default.unlinkSync(oldLogFile);
866
+ } catch {
867
+ }
868
+ }
869
+ if (import_fs3.default.existsSync(baseDir)) {
870
+ const baseFiles = import_fs3.default.readdirSync(baseDir);
871
+ for (const file of baseFiles) {
872
+ if (/^webmcp-cli-\d{4}-\d{2}-\d{2}\.log$/.test(file)) {
873
+ try {
874
+ import_fs3.default.unlinkSync(import_path3.default.join(baseDir, file));
875
+ } catch {
876
+ }
877
+ }
878
+ }
879
+ }
880
+ if (!import_fs3.default.existsSync(logDir)) return;
881
+ const files = import_fs3.default.readdirSync(logDir);
882
+ const logFilePattern = /^webmcp-cli-(\d{4}-\d{2}-\d{2})\.log$/;
883
+ const now = Date.now();
884
+ const maxAgeMs = 7 * 24 * 60 * 60 * 1e3;
885
+ for (const file of files) {
886
+ const match = file.match(logFilePattern);
887
+ if (match) {
888
+ const fileDateStr = match[1];
889
+ const fileDate = new Date(fileDateStr).getTime();
890
+ if (isNaN(fileDate)) continue;
891
+ if (now - fileDate > maxAgeMs) {
892
+ try {
893
+ import_fs3.default.unlinkSync(import_path3.default.join(logDir, file));
894
+ } catch {
895
+ }
896
+ }
897
+ }
898
+ }
899
+ } catch (e) {
900
+ }
901
+ }
902
+ function writeLog(commandName, args, result, error) {
903
+ try {
904
+ const baseDir = process.env.WEBMCP_WORKSPACE || import_path3.default.join(import_os2.default.homedir(), ".webmcp_chrome_profile");
905
+ const logDir = import_path3.default.join(baseDir, "logs");
906
+ if (!import_fs3.default.existsSync(logDir)) {
907
+ import_fs3.default.mkdirSync(logDir, { recursive: true });
908
+ }
909
+ const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
910
+ const logFile = import_path3.default.join(logDir, `webmcp-cli-${todayStr}.log`);
911
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
912
+ let logText = `========================================
913
+ `;
914
+ logText += `[${timestamp}] COMMAND: ${commandName}
915
+ `;
916
+ logText += `ARGS:
917
+ ${JSON.stringify(args, null, 2)}
918
+
919
+ `;
920
+ if (error) {
921
+ logText += `ERROR:
922
+ ${error instanceof Error ? error.stack : String(error)}
923
+ `;
924
+ } else if (result) {
925
+ logText += `RESULT:
926
+ `;
927
+ try {
928
+ if (result && typeof result === "object") {
929
+ logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
930
+ } else {
931
+ logText += String(result).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
932
+ }
933
+ } catch {
934
+ logText += JSON.stringify(result, null, 2).split("\n").map((line) => ` ${line}`).join("\n") + "\n";
935
+ }
936
+ } else {
937
+ logText += `RESULT: null
938
+ `;
939
+ }
940
+ logText += `========================================
941
+
942
+ `;
943
+ import_fs3.default.appendFileSync(logFile, logText, "utf-8");
944
+ cleanOldLogs(baseDir, logDir);
945
+ } catch (e) {
946
+ }
947
+ }
948
+ function handleCommandError(error, commandName, args) {
888
949
  const msg = error instanceof Error ? error.message : String(error);
950
+ writeLog(commandName, args, null, error);
889
951
  console.error(import_picocolors2.default.red(`Error executing ${commandName} command: ${msg}`));
890
952
  process.exit(1);
891
953
  }
@@ -895,12 +957,14 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
895
957
  process.env.WEBMCP_WORKSPACE = opts.workspace;
896
958
  }
897
959
  });
898
- program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\u72B6\u6001\uFF08\u5185\u5BB9\u3001\u6240\u6709\u9875\u7B7E\u5217\u8868\u3001\u53EF\u7528 WebMCP \u5DE5\u5177\u5217\u8868\uFF09").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (options) => {
960
+ 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) => {
961
+ const args = { tabid: parseTabId(options.tabid) };
899
962
  try {
900
- const result2 = await stateCommand({ tabid: parseTabId(options.tabid) });
901
- console.log(JSON.stringify(result2, null, 2));
963
+ const result = await stateCommand(args);
964
+ writeLog("state", args, result);
965
+ console.log(JSON.stringify(result, null, 2));
902
966
  } catch (error) {
903
- handleCommandError(error, "state");
967
+ handleCommandError(error, "state", args);
904
968
  }
905
969
  });
906
970
  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 +975,92 @@ program.command("run <toolName> [args...]").description("\u5411\u6307\u5B9A\u987
911
975
  @base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
912
976
  \u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
913
977
  ).action(async (toolName, args, options) => {
978
+ let finalArgsJson = "";
979
+ const rawArgs = { toolName, args, file: options.file, tabid: options.tabid };
914
980
  try {
915
981
  let fileContent;
916
982
  let fileBaseDir = process.cwd();
917
983
  if (options.file) {
918
- const filePath = (0, import_path3.resolve)(process.cwd(), options.file);
984
+ const filePath = (0, import_path4.resolve)(process.cwd(), options.file);
919
985
  fileBaseDir = getFileBaseDir(options.file);
920
986
  try {
921
- fileContent = (0, import_fs3.readFileSync)(filePath, "utf-8");
987
+ fileContent = (0, import_fs4.readFileSync)(filePath, "utf-8");
922
988
  } catch (e) {
923
989
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
924
990
  }
925
991
  }
926
- const finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir);
927
- const result2 = await runCommand({
992
+ finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir);
993
+ const result = await runCommand({
928
994
  toolName,
929
995
  argsJson: finalArgsJson,
930
996
  tabid: parseTabId(options.tabid)
931
997
  });
932
- console.log(JSON.stringify(result2, null, 2));
933
- } catch (error) {
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));
998
+ writeLog("run", { ...rawArgs, finalArgsJson }, result);
999
+ console.log(JSON.stringify(result, null, 2));
944
1000
  } catch (error) {
945
- handleCommandError(error, "evaluate");
1001
+ handleCommandError(error, "run", { ...rawArgs, finalArgsJson });
946
1002
  }
947
1003
  });
948
1004
  program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
1005
+ const args = { content };
949
1006
  try {
950
1007
  await setClipboard(content);
951
- console.log(JSON.stringify({ success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" }, null, 2));
1008
+ const result = { success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" };
1009
+ writeLog("clipboard", args, result);
1010
+ console.log(JSON.stringify(result, null, 2));
952
1011
  } catch (error) {
953
- handleCommandError(error, "clipboard");
1012
+ handleCommandError(error, "clipboard", args);
954
1013
  }
955
1014
  });
956
1015
  var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
957
1016
  tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
1017
+ const args = { url };
958
1018
  try {
959
- const result2 = await tabsOpenCommand(url);
960
- console.log(JSON.stringify(result2, null, 2));
1019
+ const result = await tabsOpenCommand(url);
1020
+ writeLog("tabs open", args, result);
1021
+ console.log(JSON.stringify(result, null, 2));
961
1022
  } catch (error) {
962
- handleCommandError(error, "tabs open");
1023
+ handleCommandError(error, "tabs open", args);
963
1024
  }
964
1025
  });
965
- tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
1026
+ tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
1027
+ const args = { tabid };
966
1028
  try {
967
- const result2 = await tabsCloseCommand(tabid2);
968
- console.log(JSON.stringify(result2, null, 2));
1029
+ const result = await tabsCloseCommand(tabid);
1030
+ writeLog("tabs close", args, result);
1031
+ console.log(JSON.stringify(result, null, 2));
969
1032
  } catch (error) {
970
- handleCommandError(error, "tabs close");
1033
+ handleCommandError(error, "tabs close", args);
971
1034
  }
972
1035
  });
973
- tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
1036
+ tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
1037
+ const args = { tabid };
974
1038
  try {
975
- const result2 = await tabsSwitchCommand(tabid2);
976
- console.log(JSON.stringify(result2, null, 2));
1039
+ const result = await tabsSwitchCommand(tabid);
1040
+ writeLog("tabs switch", args, result);
1041
+ console.log(JSON.stringify(result, null, 2));
977
1042
  } catch (error) {
978
- handleCommandError(error, "tabs switch");
1043
+ handleCommandError(error, "tabs switch", args);
979
1044
  }
980
1045
  });
981
- tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid2) => {
1046
+ tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid) => {
1047
+ const args = { tabid: parseTabId(tabid) };
982
1048
  try {
983
- const result2 = await tabsBackCommand(parseTabId(tabid2));
984
- console.log(JSON.stringify(result2, null, 2));
1049
+ const result = await tabsBackCommand(args.tabid);
1050
+ writeLog("tabs back", args, result);
1051
+ console.log(JSON.stringify(result, null, 2));
985
1052
  } catch (error) {
986
- handleCommandError(error, "tabs back");
1053
+ handleCommandError(error, "tabs back", args);
987
1054
  }
988
1055
  });
989
- tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid2) => {
1056
+ tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid) => {
1057
+ const args = { tabid: parseTabId(tabid) };
990
1058
  try {
991
- const result2 = await tabsForwardCommand(parseTabId(tabid2));
992
- console.log(JSON.stringify(result2, null, 2));
1059
+ const result = await tabsForwardCommand(args.tabid);
1060
+ writeLog("tabs forward", args, result);
1061
+ console.log(JSON.stringify(result, null, 2));
993
1062
  } catch (error) {
994
- handleCommandError(error, "tabs forward");
1063
+ handleCommandError(error, "tabs forward", args);
995
1064
  }
996
1065
  });
997
1066
  program.parse(process.argv);