@opentiny/webmcp-cli 0.0.1-alpha.4 → 0.0.1-alpha.5

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
@@ -464,6 +464,7 @@ async function stateCommand({ tabid: tabid2 }) {
464
464
  }
465
465
 
466
466
  // src/commands/run.ts
467
+ var import_json5 = __toESM(require("json5"), 1);
467
468
  async function runCommand({
468
469
  toolName,
469
470
  argsJson,
@@ -472,10 +473,15 @@ async function runCommand({
472
473
  const browser2 = await connectBrowser();
473
474
  try {
474
475
  const page2 = await getTargetPage(browser2, tabid2);
476
+ let cleanedArgs = argsJson.trim();
477
+ if (cleanedArgs.startsWith("'") && cleanedArgs.endsWith("'")) {
478
+ cleanedArgs = cleanedArgs.slice(1, -1).trim();
479
+ }
475
480
  try {
476
- JSON.parse(argsJson);
481
+ const obj = import_json5.default.parse(cleanedArgs);
482
+ cleanedArgs = JSON.stringify(obj);
477
483
  } catch (e) {
478
- throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
484
+ throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${e.message}`);
479
485
  }
480
486
  const result2 = await page2.evaluate(async (name, inputString) => {
481
487
  const mcp = navigator.modelContextTesting || navigator.modelContext;
@@ -489,8 +495,7 @@ async function runCommand({
489
495
  } catch (e) {
490
496
  }
491
497
  }
492
- return res;
493
- }, toolName, argsJson);
498
+ }, toolName, cleanedArgs);
494
499
  return result2;
495
500
  } finally {
496
501
  await browser2.disconnect();
@@ -651,7 +656,7 @@ async function tabsForwardCommand(tabid2) {
651
656
  // package.json
652
657
  var package_default = {
653
658
  name: "@opentiny/webmcp-cli",
654
- version: "0.0.1-alpha.4",
659
+ version: "0.0.1-alpha.5",
655
660
  type: "module",
656
661
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
657
662
  main: "dist/index.js",
@@ -690,6 +695,7 @@ var package_default = {
690
695
  "@page-agent/page-controller": "catalog:",
691
696
  clipboardy: "^5.3.1",
692
697
  commander: "^11.1.0",
698
+ json5: "^2.2.3",
693
699
  picocolors: "^1.0.0",
694
700
  "puppeteer-core": "^22.0.0"
695
701
  },
@@ -704,6 +710,13 @@ var package_default = {
704
710
 
705
711
  // src/bin.ts
706
712
  var program = new import_commander.Command();
713
+ function cleanJsonString(str) {
714
+ let cleaned = str.trim();
715
+ if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
716
+ cleaned = cleaned.slice(1, -1).trim();
717
+ }
718
+ return cleaned;
719
+ }
707
720
  function parseTabId(id) {
708
721
  if (!id) return void 0;
709
722
  return id;
@@ -740,16 +753,16 @@ program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D
740
753
  handleCommandError(error, "state");
741
754
  }
742
755
  });
743
- program.command("run <toolName> [argsJson]").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(
756
+ 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(
744
757
  "-f, --file <path>",
745
- `\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A argsJson\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
746
- \u5982\u9700\u5728 argsJson \u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
758
+ `\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A\u53C2\u6570\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
759
+ \u5982\u9700\u5728\u53C2\u6570\u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
747
760
  @file:<path> \u8BFB\u53D6\u6587\u4EF6\u539F\u59CB\u6587\u672C
748
761
  @base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
749
762
  \u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
750
- ).action(async (toolName, argsJson, options) => {
763
+ ).action(async (toolName, args, options) => {
751
764
  try {
752
- let finalArgsJson = argsJson;
765
+ let finalArgsJson = "";
753
766
  if (options.file) {
754
767
  const filePath = (0, import_path2.resolve)(process.cwd(), options.file);
755
768
  try {
@@ -757,9 +770,32 @@ program.command("run <toolName> [argsJson]").description("\u5411\u6307\u5B9A\u98
757
770
  } catch (e) {
758
771
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
759
772
  }
773
+ } else if (args && args.length > 0) {
774
+ const firstArg = args[0].trim();
775
+ const cleanedFirstArg = cleanJsonString(firstArg);
776
+ if (cleanedFirstArg.startsWith("{") && cleanedFirstArg.endsWith("}")) {
777
+ finalArgsJson = args.map(cleanJsonString).join(" ");
778
+ } else {
779
+ const obj = {};
780
+ for (const pair of args) {
781
+ const index = pair.indexOf("=");
782
+ if (index === -1) {
783
+ throw new Error(`\u65E0\u6548\u7684\u53C2\u6570\u683C\u5F0F "${pair}"\u3002\u5FC5\u987B\u662F JSON \u5B57\u7B26\u4E32\u6216 key=value \u683C\u5F0F\u3002`);
784
+ }
785
+ const key = pair.substring(0, index).trim();
786
+ let val = pair.substring(index + 1).trim();
787
+ val = cleanJsonString(val);
788
+ try {
789
+ obj[key] = JSON.parse(val);
790
+ } catch {
791
+ obj[key] = val;
792
+ }
793
+ }
794
+ finalArgsJson = JSON.stringify(obj);
795
+ }
760
796
  }
761
797
  if (!finalArgsJson) {
762
- throw new Error("\u5FC5\u987B\u63D0\u4F9B argsJson \u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
798
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
763
799
  }
764
800
  finalArgsJson = expandFileRefs(finalArgsJson);
765
801
  const result2 = await runCommand({
package/dist/bin.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts","../src/browser.ts","../src/commands/state.ts","../src/commands/run.ts","../src/commands/evaluate.ts","../src/commands/clipboard.ts","../src/commands/tabs.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander'\nimport pc from 'picocolors'\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport { stateCommand } from './commands/state'\nimport { runCommand } from './commands/run'\nimport { evaluateCommand } from './commands/evaluate'\nimport { setClipboard } from './commands/clipboard'\nimport {\n tabsOpenCommand,\n tabsCloseCommand,\n tabsSwitchCommand,\n tabsBackCommand,\n tabsForwardCommand\n} from './commands/tabs'\nimport packageJson from '../package.json'\n\nconst program = new Command()\n\nfunction parseTabId(id?: string): string | undefined {\n if (!id) return undefined\n return id\n}\n\nfunction handleCommandError(error: unknown, commandName: string): never {\n const msg = error instanceof Error ? error.message : String(error)\n console.error(pc.red(`Error executing ${commandName} command: ${msg}`))\n process.exit(1)\n}\n\n/**\n * 展开 argsJson 字符串中的文件引用占位符:\n * @file:<path> → 读取文件原始文本内容\n * @base64file:<path> → 读取文件内容并 Base64 编码\n * 占位符必须是 JSON 字符串值,例如:\n * '{\"content\":\"@base64file:./article.md\",\"prompt\":\"@file:./prompt.txt\"}'\n */\nfunction expandFileRefs(argsJson: string): string {\n // 匹配 JSON 字符串值内的 @file: / @base64file: 引用\n // 格式:\"@(base64)?file:<path>\",path 不含双引号\n return argsJson.replace(/\"@(base64)?file:([^\"]+)\"/g, (_match, base64Flag, filePath) => {\n const absPath = resolve(process.cwd(), filePath)\n let content: string\n try {\n content = readFileSync(absPath, 'utf-8')\n } catch (e: any) {\n throw new Error(`无法读取文件引用 \"${filePath}\": ${e.message}`)\n }\n const value = base64Flag ? Buffer.from(content, 'utf-8').toString('base64') : content\n // 将值序列化为合法 JSON 字符串(自动处理换行、引号等转义)\n return JSON.stringify(value)\n })\n}\n\nprogram\n .name('webmcp-cli')\n .description('WebMCP CLI for interacting with browser via CDP')\n .version(packageJson.version)\n .option('-w, --workspace <path>', '指定自定义的浏览器工作空间(用户配置目录)路径')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts()\n if (opts.workspace) {\n process.env.WEBMCP_WORKSPACE = opts.workspace\n }\n })\n\nprogram\n .command('state')\n .description('获取浏览器当前页签或指定页签的状态(内容、所有页签列表、可用 WebMCP 工具列表)')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .action(async (options) => {\n try {\n const result = await stateCommand({ tabid: parseTabId(options.tabid) })\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'state')\n }\n })\n\nprogram\n .command('run <toolName> [argsJson]')\n .description('向指定页签调用指定的 WebMCP 工具执行操作')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .option(\n '-f, --file <path>',\n '从指定 .json 文件读取整个 argsJson(文件内容即为参数 JSON)。\\n' +\n '如需在 argsJson 中内联引用文件,使用占位符语法:\\n' +\n ' @file:<path> 读取文件原始文本\\n' +\n ' @base64file:<path> 读取文件并 Base64 编码\\n' +\n '示例:webmcp-cli run mytool \\'{\"content\":\"@base64file:./doc.md\"}\\'' \n )\n .action(async (toolName, argsJson, options) => {\n try {\n let finalArgsJson = argsJson\n\n // -f/--file:把整个文件内容作为 argsJson(适合 .json 参数文件)\n if (options.file) {\n const filePath = resolve(process.cwd(), options.file)\n try {\n finalArgsJson = readFileSync(filePath, 'utf-8').trim()\n } catch (e: any) {\n throw new Error(`无法读取文件 \"${filePath}\": ${e.message}`)\n }\n }\n\n if (!finalArgsJson) {\n throw new Error('必须提供 argsJson 参数或通过 -f/--file 指定参数文件')\n }\n\n // 展开 argsJson 中的 @file: / @base64file: 内联文件引用\n finalArgsJson = expandFileRefs(finalArgsJson)\n\n const result = await runCommand({\n toolName,\n argsJson: finalArgsJson,\n tabid: parseTabId(options.tabid)\n })\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'run')\n }\n })\n\n\nprogram\n .command('evaluate <jsScript>')\n .description('向当前网页或指定页签注入并执行 JavaScript 代码')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .action(async (jsScript, options) => {\n try {\n const result = await evaluateCommand({\n jsScript,\n tabid: parseTabId(options.tabid)\n })\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'evaluate')\n }\n })\n\nprogram\n .command('clipboard <content>')\n .description('将内容设置到系统剪贴板')\n .action(async (content) => {\n try {\n await setClipboard(content)\n console.log(JSON.stringify({ success: true, message: '内容已设置到系统剪贴板' }, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'clipboard')\n }\n })\n\nconst tabs = program.command('tabs').description('管理浏览器标签页')\n\ntabs\n .command('open <url>')\n .description('打开新网页')\n .action(async (url) => {\n try {\n const result = await tabsOpenCommand(url)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs open')\n }\n })\n\ntabs\n .command('close <tabid>')\n .description('关闭指定 tabid 的标签页')\n .action(async (tabid) => {\n try {\n const result = await tabsCloseCommand(tabid)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs close')\n }\n })\n\ntabs\n .command('switch <tabid>')\n .description('激活并切换到指定 tabid 的标签页')\n .action(async (tabid) => {\n try {\n const result = await tabsSwitchCommand(tabid)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs switch')\n }\n })\n\ntabs\n .command('back [tabid]')\n .description('将当前或指定标签页导航后退一步')\n .action(async (tabid) => {\n try {\n const result = await tabsBackCommand(parseTabId(tabid))\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs back')\n }\n })\n\ntabs\n .command('forward [tabid]')\n .description('将当前或指定标签页导航前进一步')\n .action(async (tabid) => {\n try {\n const result = await tabsForwardCommand(parseTabId(tabid))\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs forward')\n }\n })\n\nprogram.parse(process.argv)\n","import puppeteer, { Browser, Page } from 'puppeteer-core'\nimport pc from 'picocolors'\nimport fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport http from 'http'\nimport { spawn } from 'child_process'\nimport { fileURLToPath } from 'url'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\nconst CDP_PORT = 9222\n// 使用 localhost 以兼容 IPv4/IPv6 绑定\nconst CDP_URL = `http://localhost:${CDP_PORT}`\n\n/**\n * 使用 Node.js 原生 http 模块发起 GET 请求并返回响应体文本\n * 避免 Node.js fetch 将 localhost 解析为 IPv6 ::1 导致连接 Chrome CDP 失败的问题\n */\nfunction httpGet(url: string, timeoutMs = 1500): Promise<string> {\n return new Promise((resolve, reject) => {\n const req = http.get(url, { timeout: timeoutMs }, (res) => {\n let data = ''\n res.on('data', (chunk: Buffer) => { data += chunk.toString() })\n res.on('end', () => {\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve(data)\n } else {\n reject(new Error(`HTTP ${res.statusCode}`))\n }\n })\n })\n req.on('error', reject)\n req.on('timeout', () => { req.destroy(); reject(new Error('request timeout')) })\n })\n}\n\nfunction promiseWithTimeout<T>(promise: Promise<T>, timeoutMs = 5000, errorMsg = 'Operation timed out'): Promise<T> {\n let timeoutId: NodeJS.Timeout\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMsg))\n }, timeoutMs)\n })\n return Promise.race([promise, timeoutPromise]).finally(() => {\n clearTimeout(timeoutId)\n })\n}\n\nasync function checkCdpReady(url: string, retries = 3): Promise<boolean> {\n for (let i = 0; i < retries; i++) {\n try {\n await httpGet(url, 1500)\n return true\n } catch {}\n if (i < retries - 1) {\n await new Promise(r => setTimeout(r, 200))\n }\n }\n return false\n}\n\n\n\ninterface BrowserInfo {\n path: string\n name: string\n}\n\nfunction getDefaultBrowserPath(): BrowserInfo | null {\n const platform = os.platform()\n if (platform === 'darwin') {\n // macOS:优先 Chrome,其次 Edge\n const chromePath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'\n if (fs.existsSync(chromePath)) return { path: chromePath, name: 'Chrome' }\n const edgePath = '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'\n if (fs.existsSync(edgePath)) return { path: edgePath, name: 'Edge' }\n return null\n } else if (platform === 'win32') {\n // Windows:优先 Chrome,其次 Edge(系统内置路径)\n const chromePaths = [\n process.env.LOCALAPPDATA + '\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n process.env.PROGRAMFILES + '\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n process.env['PROGRAMFILES(X86)'] + '\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe'\n ]\n const foundChrome = chromePaths.find(p => fs.existsSync(p))\n if (foundChrome) return { path: foundChrome, name: 'Chrome' }\n\n // Edge 在 Windows 上的常见路径(包括系统内置的 Edge)\n const edgePaths = [\n process.env.LOCALAPPDATA + '\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n process.env.PROGRAMFILES + '\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n process.env['PROGRAMFILES(X86)'] + '\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe'\n ]\n const foundEdge = edgePaths.find(p => fs.existsSync(p))\n if (foundEdge) return { path: foundEdge, name: 'Edge' }\n return null\n } else {\n // Linux:优先 Chrome,其次 Chromium,最后 Edge\n const paths: Array<{ p: string; name: string }> = [\n { p: '/usr/bin/google-chrome', name: 'Chrome' },\n { p: '/usr/bin/google-chrome-stable', name: 'Chrome' },\n { p: '/usr/bin/chromium', name: 'Chromium' },\n { p: '/usr/bin/chromium-browser', name: 'Chromium' },\n { p: '/usr/bin/microsoft-edge', name: 'Edge' },\n { p: '/usr/bin/microsoft-edge-stable', name: 'Edge' }\n ]\n const found = paths.find(({ p }) => fs.existsSync(p))\n return found ? { path: found.p, name: found.name } : null\n }\n}\n\nasync function startBrowserInBackground(): Promise<void> {\n const browserInfo = getDefaultBrowserPath()\n if (!browserInfo || !fs.existsSync(browserInfo.path)) {\n throw new Error('无法在系统中找到 Chrome 或 Edge 浏览器的默认安装路径。')\n }\n\n console.log(pc.yellow(`正在启动后台 ${browserInfo.name} 实例 (端口: ${CDP_PORT})...`))\n \n // 用户可以通过 --workspace CLI 选项或 WEBMCP_WORKSPACE 环境变量自定义。\n const userDataDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), '.webmcp_chrome_profile')\n \n const child = spawn(\n browserInfo.path,\n [\n `--remote-debugging-port=${CDP_PORT}`,\n `--user-data-dir=${userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check'\n ],\n {\n detached: true,\n stdio: 'ignore'\n }\n )\n\n child.unref() // 让子进程脱离父进程独立运行\n\n // 轮询等待 CDP 端口就绪\n for (let i = 0; i < 20; i++) {\n try {\n const urls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`]\n for (const url of urls) {\n try {\n await httpGet(url, 1000)\n console.log(pc.green(`${browserInfo.name} 启动并就绪。`))\n return\n } catch (err) {}\n }\n } catch (e) {\n // 忽略连接错误,继续重试\n }\n await new Promise(resolve => setTimeout(resolve, 500))\n }\n\n throw new Error(`${browserInfo.name} 启动超时,无法连接到 CDP 端口。`)\n}\n\nexport async function connectBrowser(): Promise<Browser> {\n const targetFilter = (target: any) => {\n try {\n const info = typeof target._getTargetInfo === 'function' ? target._getTargetInfo() : target\n const type = info.type || ''\n const url = info.url || ''\n \n // 过滤掉绝对不需要 attach 且容易发生死锁的后台/子框架 target\n if (\n type === 'service_worker' || \n type === 'shared_worker' || \n type === 'iframe' || \n type === 'other' || \n type === 'webview' ||\n type === 'background_page'\n ) {\n return false\n }\n \n // 过滤掉 devtools 和插件页面\n if (url.startsWith('devtools://') || url.startsWith('chrome-extension://')) {\n return false\n }\n \n return true\n } catch (e) {\n return false\n }\n }\n\n // 第一步:检查 9222 端口是否已有可用的 CDP 实例\n const portAddresses = [\n `http://127.0.0.1:${CDP_PORT}/json/version`,\n `http://localhost:${CDP_PORT}/json/version`\n ]\n\n for (const addr of portAddresses) {\n const isReady = await checkCdpReady(addr, 2)\n if (!isReady) continue\n\n const browserURL = addr.replace('/json/version', '')\n try {\n console.log(pc.yellow(`connectBrowser: 检测到端口 ${CDP_PORT} 已就绪,正在连接 ${browserURL}...`))\n const browser = await promiseWithTimeout(\n puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),\n 10000,\n `puppeteer.connect to ${browserURL} timed out`\n )\n console.log(pc.green(`connectBrowser: 成功连接 ${browserURL}`))\n return browser\n } catch {\n // 当前地址连不上,继续尝试下一个\n }\n }\n\n // 第二步:端口无响应,直接启动新的浏览器实例(使用独立用户数据目录,不影响用户已有浏览器)\n console.log(pc.yellow(`connectBrowser: 端口 ${CDP_PORT} 无响应,正在启动新的浏览器实例...`))\n try {\n await startBrowserInBackground()\n } catch (launchError: unknown) {\n const msg = launchError instanceof Error ? launchError.message : String(launchError)\n console.error(pc.red(`无法启动浏览器: ${msg}`))\n throw new Error('Browser launch failed.')\n }\n\n // 第三步:浏览器启动后再次尝试连接\n for (const addr of portAddresses) {\n const isReady = await checkCdpReady(addr, 3)\n if (!isReady) continue\n\n const browserURL = addr.replace('/json/version', '')\n try {\n console.log(pc.yellow(`connectBrowser: 浏览器已启动,正在连接 ${browserURL}...`))\n const browser = await promiseWithTimeout(\n puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),\n 10000,\n `puppeteer.connect to ${browserURL} after launch timed out`\n )\n console.log(pc.green(`connectBrowser: 成功连接 ${browserURL}`))\n return browser\n } catch {\n // 继续尝试下一个地址\n }\n }\n\n throw new Error(`无法连接到浏览器(端口 ${CDP_PORT}),请检查 Chrome/Edge 是否已安装。`)\n}\n\n\n/**\n * 通过 CDP 获取页面真实的 Chrome target ID(UUID 格式字符串)\n * 供 state.ts 等命令展示 tabs 列表时使用\n */\nexport async function getPageTargetId(page: Page): Promise<string> {\n const session = await page.target().createCDPSession()\n try {\n const { targetInfo } = await session.send('Target.getTargetInfo')\n return targetInfo.targetId\n } finally {\n await session.detach().catch(() => {})\n }\n}\n\nexport function getPageTargets(browser: Browser) {\n return browser.targets().filter(t => {\n try {\n const type = (typeof t.type === 'function' ? t.type() : (t as any).type) || ''\n const url = (typeof t.url === 'function' ? t.url() : (t as any).url) || ''\n return type === 'page' && !url.startsWith('devtools://')\n } catch {\n return false\n }\n })\n}\n\nexport function getTargetIdFromTarget(target: any): string {\n return typeof target._getTargetInfo === 'function'\n ? target._getTargetInfo().targetId\n : (target._targetId || target.targetId || '')\n}\n\nexport function findPageTargetByTabId(browser: Browser, tabid: string) {\n const pageTargets = getPageTargets(browser)\n return pageTargets.find(t => {\n const tid = getTargetIdFromTarget(t)\n return tid === tabid || tid.includes(tabid)\n }) ?? null\n}\n\nexport async function activateTabById(browser: Browser, tabid: string): Promise<void> {\n const target = findPageTargetByTabId(browser, tabid)\n if (!target) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n\n const realTabId = getTargetIdFromTarget(target)\n const pages = await browser.pages()\n let sessionPage = pages.find(p => !p.url().startsWith('devtools://'))\n if (!sessionPage && pages.length > 0) {\n sessionPage = pages[0]\n }\n if (!sessionPage) {\n sessionPage = await browser.newPage()\n }\n\n const session = await sessionPage.createCDPSession()\n try {\n await session.send('Target.activateTarget', { targetId: realTabId })\n } finally {\n await session.detach().catch(() => {})\n }\n}\n\nexport async function getTargetPage(browser: Browser, tabid?: string): Promise<Page> {\n const pageTargets = getPageTargets(browser)\n\n if (pageTargets.length === 0) {\n const newPage = await browser.newPage()\n await injectWebMCPPolyfillAndTools(newPage)\n return newPage\n }\n\n let targetPage: Page | null = null\n\n if (tabid !== undefined) {\n const target = findPageTargetByTabId(browser, tabid)\n if (!target) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n targetPage = await target.page()\n } else {\n // Chrome 的 /json/list 接口把当前激活 the tab 排在第一位,用它来判断激活 tab\n try {\n const urls = [\n `http://localhost:${CDP_PORT}/json/list`,\n `http://127.0.0.1:${CDP_PORT}/json/list`\n ]\n let activeTargetId: string | null = null\n for (const url of urls) {\n try {\n const body = await httpGet(url, 1000)\n const targetsData: Array<{ id: string; type: string; url: string }> = JSON.parse(body)\n // 找第一个 type=page 且不是 devtools:// 的 target(Chrome 把激活的排第一)\n const active = targetsData.find(t => t.type === 'page' && !t.url.startsWith('devtools://'))\n if (active) { activeTargetId = active.id; break }\n } catch { /* 忽略,继续试下一个地址 */ }\n }\n\n if (activeTargetId) {\n for (const target of pageTargets) {\n if (getTargetIdFromTarget(target) === activeTargetId) {\n targetPage = await target.page()\n break\n }\n }\n }\n } catch { /* 忽略,使用 fallback */ }\n\n // fallback:取最后一个非 devtools 页面\n if (!targetPage) {\n const lastTarget = pageTargets[pageTargets.length - 1]\n targetPage = await lastTarget.page()\n }\n }\n\n if (!targetPage) {\n throw new Error('无法获取目标页面')\n }\n\n // 注入 polyfill 和域名工具(幂等检查)\n await injectWebMCPPolyfillAndTools(targetPage)\n return targetPage\n}\n\n/**\n * 供 tabs open / back / forward 命令在导航完成后调用:强制注入(不做 flag 检查,因为 goto 后页面上下文已清空)\n */\nexport async function injectIntoPage(page: Page): Promise<void> {\n await injectWebMCPPolyfillAndTools(page, true)\n}\n\nasync function injectWebMCPPolyfillAndTools(page: Page, force = false) {\n // 检查 polyfill 是否已注入(force=true 时跳过,用于 goto 之后的强制重注入)\n const polyfillReady = !force && await page.evaluate(() => {\n return !!(window as any).__webmcpcli_init\n }).catch(() => false)\n\n if (!polyfillReady) {\n console.log(pc.cyan('当前页面尚未注入 WebMCP 环境,正在执行自动注入...'))\n\n const injectScriptPath = path.resolve(__dirname, 'inject-bundle.js')\n if (!fs.existsSync(injectScriptPath)) {\n throw new Error(`Cannot find inject-bundle.js at ${injectScriptPath}. Please ensure you run 'pnpm build:inject' first.`)\n }\n\n const scriptContent = fs.readFileSync(injectScriptPath, 'utf-8')\n\n // 注入 WebMCP polyfill\n try {\n await page.evaluate(scriptContent)\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n throw new Error('自动注入脚本执行失败: ' + msg)\n }\n\n // 等待工具异步注册\n await new Promise(resolve => setTimeout(resolve, 300))\n }\n\n // 无论 polyfill 是否刚注入,都检查域名工具(工具内部有防重复 flag)\n await injectDomainTools(page)\n}\n\n/**\n * 根据页面域名查找并注入对应的工具 bundle\n * bundle 文件位于 dist/webmcp-tools/{hostname}.js\n */\nasync function injectDomainTools(page: Page): Promise<void> {\n let hostname: string\n try {\n const url = new URL(page.url())\n hostname = url.hostname\n } catch {\n return // 非 http(s) 页面,跳过\n }\n\n const toolBundlePath = path.resolve(__dirname, 'webmcp-tools', `${hostname}.js`)\n if (!fs.existsSync(toolBundlePath)) {\n return // 没有对应的工具预置,跳过\n }\n\n console.log(pc.cyan(`检测到域名 ${hostname} 有预置工具,正在注入...`))\n\n const toolScript = fs.readFileSync(toolBundlePath, 'utf-8')\n try {\n await page.evaluate(toolScript)\n console.log(pc.green(`已为 ${hostname} 注入预置工具`))\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n // 工具注入失败不阻断主流程,仅打印警告\n console.warn(pc.yellow(`域名工具注入失败 (${hostname}): ${msg}`))\n }\n}\n","import { connectBrowser, getTargetPage, getPageTargetId, injectIntoPage } from '../browser'\n\nexport async function stateCommand({ tabid }: { tabid?: string }) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n\n // 1. 每一次state都重新注入一次page-agent-tool。 这样用户即使手动打开的网页也会有相应的工具。\n await injectIntoPage(page)\n\n // 2. 在页面上下文中执行,获取当前状态和可用工具\n const state = await page.evaluate(async () => {\n const url = document.URL\n const title = document.title\n\n // 尝试获取内置工具\n const mcp = (navigator as any).modelContextTesting || (navigator as any).modelContext\n let webmcpTools: any[] = []\n let contentData: any = `页面已准备好: ${title}`\n\n if (mcp) {\n if (typeof mcp.listTools === 'function') {\n const toolsResult = await mcp.listTools()\n webmcpTools = toolsResult?.tools || toolsResult || []\n }\n\n if (typeof mcp.executeTool === 'function') {\n try {\n const argsString = JSON.stringify({ action: 'browserState' })\n let stateRes = await mcp.executeTool('page-agent-tool', argsString)\n\n if (typeof stateRes === 'string') {\n try {\n stateRes = JSON.parse(stateRes)\n } catch (e) {\n // ignore\n }\n }\n if (stateRes && stateRes.content && stateRes.content.length > 0) {\n const textContent = stateRes.content.map((c: any) => c.text).join('\\\\n')\n\n // page-agent-tool 返回的格式通常是 \"浏览器状态: {\\\"url\\\":..., \\\"content\\\":\\\"[0]...\\\"}\"\n // 我们尝试把这个 JSON 提取出来,让外层更容易解析\n const prefix = '浏览器状态: '\n if (textContent.startsWith(prefix)) {\n try {\n const jsonStr = textContent.substring(prefix.length)\n const parsedState = JSON.parse(jsonStr)\n // 提取出带有索引的 DOM 树数据作为 content\n contentData = parsedState.content\n } catch (e) {\n contentData = textContent\n }\n } else {\n contentData = textContent\n }\n }\n } catch (e: any) {\n console.error('Snapshot error:', e.message)\n }\n }\n }\n\n return {\n content: contentData,\n url,\n title,\n webmcpTools\n }\n })\n\n // 3. 获取所有的 tab 信息(排除 devtools:// 内部页面)\n const pages = await browser.pages()\n const tabs = await Promise.all(\n pages.map(async (p) => {\n const pUrl = p.url()\n if (pUrl.startsWith('devtools://')) return null\n\n const pTitle = await Promise.race([\n p.title().catch(() => 'Unknown'),\n new Promise<string>((resolve) => setTimeout(() => resolve('Unknown'), 500))\n ])\n\n return {\n // 使用真实的 Chrome target ID,而非数组下标\n tabid: await getPageTargetId(p).catch(() => pUrl),\n title: pTitle,\n url: pUrl\n }\n })\n )\n\n return {\n ...state,\n tabs: tabs.filter(Boolean)\n }\n } finally {\n await browser.disconnect()\n }\n}\n","import { connectBrowser, getTargetPage } from '../browser'\n\nexport async function runCommand({\n toolName,\n argsJson,\n tabid\n}: {\n toolName: string\n argsJson: string\n tabid?: string\n}) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n\n // 验证一下是否是合法的 JSON,以防用户传入了非法的字符串\n try {\n JSON.parse(argsJson)\n } catch (e: any) {\n throw new Error(`参数不是有效的 JSON: ${e.message}`)\n }\n\n const result = await page.evaluate(async (name, inputString) => {\n const mcp = (navigator as any).modelContextTesting || (navigator as any).modelContext\n \n if (!mcp || typeof mcp.executeTool !== 'function') {\n throw new Error('当前页面没有注入 WebMCP 环境 (navigator.modelContextTesting.executeTool 未找到)')\n }\n\n // executeTool 的第二个参数必须是 JSON 字符串\n let res = await mcp.executeTool(name, inputString)\n \n // executeTool 的返回值可能是普通对象,也可能是 JSON 字符串\n if (typeof res === 'string') {\n try {\n res = JSON.parse(res)\n } catch (e) {\n // ignore\n }\n }\n return res\n }, toolName, argsJson)\n\n return result\n } finally {\n await browser.disconnect()\n }\n}\n","import { connectBrowser, getTargetPage } from '../browser'\n\nexport async function evaluateCommand({\n jsScript,\n tabid\n}: {\n jsScript: string\n tabid?: string\n}) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n\n const result = await page.evaluate(async (script) => {\n try {\n // 使用 eval 执行传入的 JavaScript 代码\n // eslint-disable-next-line no-eval\n const result = eval(script)\n \n // 如果结果是 Promise,等待其完成\n if (result instanceof Promise) {\n return await result\n }\n \n return result\n } catch (error: any) {\n return {\n error: true,\n message: error.message,\n stack: error.stack\n }\n }\n }, jsScript)\n\n return result\n } finally {\n await browser.disconnect()\n }\n}\n","import clipboard from 'clipboardy'\n\n/**\n * 将文本设置到系统剪贴板\n * 使用 clipboardy 包,跨平台统一接口,无需手动处理各系统命令\n */\nexport async function setClipboard(content: string): Promise<void> {\n try {\n await clipboard.write(content)\n } catch (error: any) {\n throw new Error(`设置剪贴板失败: ${error.message}`)\n }\n}\n","import {\n connectBrowser,\n getTargetPage,\n injectIntoPage,\n getPageTargetId,\n findPageTargetByTabId,\n activateTabById\n} from '../browser'\n\nfunction normalizeUrl(url: string): string {\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n return 'https://' + url\n }\n return url\n}\n\nexport async function tabsOpenCommand(url: string) {\n const browser = await connectBrowser()\n try {\n const page = await browser.newPage()\n url = normalizeUrl(url)\n\n await page.goto(url, { waitUntil: 'domcontentloaded' })\n await injectIntoPage(page)\n await new Promise((resolve) => setTimeout(resolve, 500))\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsCloseCommand(tabid: string) {\n const browser = await connectBrowser()\n try {\n const target = findPageTargetByTabId(browser, tabid)\n if (!target) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n\n const page = await target.page()\n if (!page) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n\n const closedTabid = await getPageTargetId(page).catch(() => tabid)\n await page.close()\n\n return {\n success: true,\n tabid: closedTabid\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsSwitchCommand(tabid: string) {\n const browser = await connectBrowser()\n try {\n await activateTabById(browser, tabid)\n const page = await getTargetPage(browser, tabid)\n\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsBackCommand(tabid?: string) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n const response = await page.goBack()\n\n if (!response) {\n return {\n success: false,\n error: '无法后退:已在历史记录起点',\n tabid: await getPageTargetId(page).catch(() => tabid),\n url: page.url(),\n title: await page.title()\n }\n }\n\n await injectIntoPage(page)\n\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsForwardCommand(tabid?: string) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n const response = await page.goForward()\n\n if (!response) {\n return {\n success: false,\n error: '无法前进:已在历史记录末尾',\n tabid: await getPageTargetId(page).catch(() => tabid),\n url: page.url(),\n title: await page.title()\n }\n }\n\n await injectIntoPage(page)\n\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n","{\n \"name\": \"@opentiny/webmcp-cli\",\n \"version\": \"0.0.1-alpha.4\",\n \"type\": \"module\",\n \"description\": \"WebMCP CLI for AI Agents to interact with browser via Puppeteer\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"bin\": {\n \"webmcp-cli\": \"./dist/bin.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build:inject\": \"node scripts/build-inject.mjs\",\n \"build\": \"tsup && pnpm run build:inject\",\n \"prepublishOnly\": \"pnpm run build\",\n \"link:global\": \"npm install -g .\",\n \"start\": \"node dist/bin.js\"\n },\n \"keywords\": [\n \"chrome\",\n \"puppeteer\",\n \"cli\"\n ],\n \"license\": \"MIT\",\n \"author\": \"OpenTiny Team\",\n \"engines\": {\n \"node\": \">=16\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"dependencies\": {\n \"@mcp-b/webmcp-polyfill\": \"catalog:\",\n \"@opentiny/next-sdk\": \"workspace:^\",\n \"@page-agent/page-controller\": \"catalog:\",\n \"clipboardy\": \"^5.3.1\",\n \"commander\": \"^11.1.0\",\n \"picocolors\": \"^1.0.0\",\n \"puppeteer-core\": \"^22.0.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"esbuild\": \"^0.25.0\",\n \"tsup\": \"^8.0.2\",\n \"tsx\": \"^4.0.0\",\n \"typescript\": \"^5.3.3\"\n }\n}\n"],"mappings":"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","names":["import_picocolors","import_fs","import_path","path","resolve","http","os","fs","pc","browser","puppeteer","page","tabid","tabid","browser","page","tabs","resolve","tabid","browser","page","result","clipboard","browser","page","resolve","tabid","pc","result","jsScript","tabid"]}
1
+ {"version":3,"sources":["../src/bin.ts","../src/browser.ts","../src/commands/state.ts","../src/commands/run.ts","../src/commands/evaluate.ts","../src/commands/clipboard.ts","../src/commands/tabs.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander'\nimport pc from 'picocolors'\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport { stateCommand } from './commands/state'\nimport { runCommand } from './commands/run'\nimport { evaluateCommand } from './commands/evaluate'\nimport { setClipboard } from './commands/clipboard'\nimport {\n tabsOpenCommand,\n tabsCloseCommand,\n tabsSwitchCommand,\n tabsBackCommand,\n tabsForwardCommand\n} from './commands/tabs'\nimport packageJson from '../package.json'\n\nconst program = new Command()\n\nfunction cleanJsonString(str: string): string {\n let cleaned = str.trim()\n if (cleaned.startsWith(\"'\") && cleaned.endsWith(\"'\")) {\n cleaned = cleaned.slice(1, -1).trim()\n }\n return cleaned\n}\n\nfunction parseTabId(id?: string): string | undefined {\n if (!id) return undefined\n return id\n}\n\nfunction handleCommandError(error: unknown, commandName: string): never {\n const msg = error instanceof Error ? error.message : String(error)\n console.error(pc.red(`Error executing ${commandName} command: ${msg}`))\n process.exit(1)\n}\n\n/**\n * 展开 argsJson 字符串中的文件引用占位符:\n * @file:<path> → 读取文件原始文本内容\n * @base64file:<path> → 读取文件内容并 Base64 编码\n * 占位符必须是 JSON 字符串值,例如:\n * '{\"content\":\"@base64file:./article.md\",\"prompt\":\"@file:./prompt.txt\"}'\n */\nfunction expandFileRefs(argsJson: string): string {\n // 匹配 JSON 字符串值内的 @file: / @base64file: 引用\n // 格式:\"@(base64)?file:<path>\",path 不含双引号\n return argsJson.replace(/\"@(base64)?file:([^\"]+)\"/g, (_match, base64Flag, filePath) => {\n const absPath = resolve(process.cwd(), filePath)\n let content: string\n try {\n content = readFileSync(absPath, 'utf-8')\n } catch (e: any) {\n throw new Error(`无法读取文件引用 \"${filePath}\": ${e.message}`)\n }\n const value = base64Flag ? Buffer.from(content, 'utf-8').toString('base64') : content\n // 将值序列化为合法 JSON 字符串(自动处理换行、引号等转义)\n return JSON.stringify(value)\n })\n}\n\nprogram\n .name('webmcp-cli')\n .description('WebMCP CLI for interacting with browser via CDP')\n .version(packageJson.version)\n .option('-w, --workspace <path>', '指定自定义的浏览器工作空间(用户配置目录)路径')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts()\n if (opts.workspace) {\n process.env.WEBMCP_WORKSPACE = opts.workspace\n }\n })\n\nprogram\n .command('state')\n .description('获取浏览器当前页签或指定页签的状态(内容、所有页签列表、可用 WebMCP 工具列表)')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .action(async (options) => {\n try {\n const result = await stateCommand({ tabid: parseTabId(options.tabid) })\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'state')\n }\n })\n\nprogram\n .command('run <toolName> [args...]')\n .description('向指定页签调用指定的 WebMCP 工具执行操作')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .option(\n '-f, --file <path>',\n '从指定 .json 文件读取整个参数(文件内容即为参数 JSON)。\\n' +\n '如需在参数中内联引用文件,使用占位符语法:\\n' +\n ' @file:<path> 读取文件原始文本\\n' +\n ' @base64file:<path> 读取文件并 Base64 编码\\n' +\n '示例:webmcp-cli run mytool \\'{\"content\":\"@base64file:./doc.md\"}\\''\n )\n .action(async (toolName, args, options) => {\n try {\n let finalArgsJson = ''\n\n // -f/--file:把整个文件内容作为 argsJson\n if (options.file) {\n const filePath = resolve(process.cwd(), options.file)\n try {\n finalArgsJson = readFileSync(filePath, 'utf-8').trim()\n } catch (e: any) {\n throw new Error(`无法读取文件 \"${filePath}\": ${e.message}`)\n }\n } else if (args && args.length > 0) {\n const firstArg = args[0].trim()\n const cleanedFirstArg = cleanJsonString(firstArg)\n\n if (cleanedFirstArg.startsWith('{') && cleanedFirstArg.endsWith('}')) {\n // 认为是完整的或被拆分的 JSON 字符串,重新用空格连接起来\n finalArgsJson = args.map(cleanJsonString).join(' ')\n } else {\n // 认为是 key=value 键值对形式\n const obj: Record<string, any> = {}\n for (const pair of args) {\n const index = pair.indexOf('=')\n if (index === -1) {\n throw new Error(`无效的参数格式 \"${pair}\"。必须是 JSON 字符串或 key=value 格式。`)\n }\n const key = pair.substring(0, index).trim()\n let val = pair.substring(index + 1).trim()\n val = cleanJsonString(val)\n\n // 尝试把值当做 JSON 解析(支持 bool, number, null 以及对象)\n try {\n obj[key] = JSON.parse(val)\n } catch {\n obj[key] = val // 解析失败则保留为原样字符串\n }\n }\n finalArgsJson = JSON.stringify(obj)\n }\n }\n\n if (!finalArgsJson) {\n throw new Error('必须提供参数或通过 -f/--file 指定参数文件')\n }\n\n // 展开 argsJson 中的 @file: / @base64file: 内联文件引用\n finalArgsJson = expandFileRefs(finalArgsJson)\n\n const result = await runCommand({\n toolName,\n argsJson: finalArgsJson,\n tabid: parseTabId(options.tabid)\n })\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'run')\n }\n })\n\nprogram\n .command('evaluate <jsScript>')\n .description('向当前网页或指定页签注入并执行 JavaScript 代码')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .action(async (jsScript, options) => {\n try {\n const result = await evaluateCommand({\n jsScript,\n tabid: parseTabId(options.tabid)\n })\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'evaluate')\n }\n })\n\nprogram\n .command('clipboard <content>')\n .description('将内容设置到系统剪贴板')\n .action(async (content) => {\n try {\n await setClipboard(content)\n console.log(JSON.stringify({ success: true, message: '内容已设置到系统剪贴板' }, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'clipboard')\n }\n })\n\nconst tabs = program.command('tabs').description('管理浏览器标签页')\n\ntabs\n .command('open <url>')\n .description('打开新网页')\n .action(async (url) => {\n try {\n const result = await tabsOpenCommand(url)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs open')\n }\n })\n\ntabs\n .command('close <tabid>')\n .description('关闭指定 tabid 的标签页')\n .action(async (tabid) => {\n try {\n const result = await tabsCloseCommand(tabid)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs close')\n }\n })\n\ntabs\n .command('switch <tabid>')\n .description('激活并切换到指定 tabid 的标签页')\n .action(async (tabid) => {\n try {\n const result = await tabsSwitchCommand(tabid)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs switch')\n }\n })\n\ntabs\n .command('back [tabid]')\n .description('将当前或指定标签页导航后退一步')\n .action(async (tabid) => {\n try {\n const result = await tabsBackCommand(parseTabId(tabid))\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs back')\n }\n })\n\ntabs\n .command('forward [tabid]')\n .description('将当前或指定标签页导航前进一步')\n .action(async (tabid) => {\n try {\n const result = await tabsForwardCommand(parseTabId(tabid))\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs forward')\n }\n })\n\nprogram.parse(process.argv)\n","import puppeteer, { Browser, Page } from 'puppeteer-core'\nimport pc from 'picocolors'\nimport fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport http from 'http'\nimport { spawn } from 'child_process'\nimport { fileURLToPath } from 'url'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\nconst CDP_PORT = 9222\n// 使用 localhost 以兼容 IPv4/IPv6 绑定\nconst CDP_URL = `http://localhost:${CDP_PORT}`\n\n/**\n * 使用 Node.js 原生 http 模块发起 GET 请求并返回响应体文本\n * 避免 Node.js fetch 将 localhost 解析为 IPv6 ::1 导致连接 Chrome CDP 失败的问题\n */\nfunction httpGet(url: string, timeoutMs = 1500): Promise<string> {\n return new Promise((resolve, reject) => {\n const req = http.get(url, { timeout: timeoutMs }, (res) => {\n let data = ''\n res.on('data', (chunk: Buffer) => { data += chunk.toString() })\n res.on('end', () => {\n if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {\n resolve(data)\n } else {\n reject(new Error(`HTTP ${res.statusCode}`))\n }\n })\n })\n req.on('error', reject)\n req.on('timeout', () => { req.destroy(); reject(new Error('request timeout')) })\n })\n}\n\nfunction promiseWithTimeout<T>(promise: Promise<T>, timeoutMs = 5000, errorMsg = 'Operation timed out'): Promise<T> {\n let timeoutId: NodeJS.Timeout\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMsg))\n }, timeoutMs)\n })\n return Promise.race([promise, timeoutPromise]).finally(() => {\n clearTimeout(timeoutId)\n })\n}\n\nasync function checkCdpReady(url: string, retries = 3): Promise<boolean> {\n for (let i = 0; i < retries; i++) {\n try {\n await httpGet(url, 1500)\n return true\n } catch {}\n if (i < retries - 1) {\n await new Promise(r => setTimeout(r, 200))\n }\n }\n return false\n}\n\n\n\ninterface BrowserInfo {\n path: string\n name: string\n}\n\nfunction getDefaultBrowserPath(): BrowserInfo | null {\n const platform = os.platform()\n if (platform === 'darwin') {\n // macOS:优先 Chrome,其次 Edge\n const chromePath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'\n if (fs.existsSync(chromePath)) return { path: chromePath, name: 'Chrome' }\n const edgePath = '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'\n if (fs.existsSync(edgePath)) return { path: edgePath, name: 'Edge' }\n return null\n } else if (platform === 'win32') {\n // Windows:优先 Chrome,其次 Edge(系统内置路径)\n const chromePaths = [\n process.env.LOCALAPPDATA + '\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n process.env.PROGRAMFILES + '\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe',\n process.env['PROGRAMFILES(X86)'] + '\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe'\n ]\n const foundChrome = chromePaths.find(p => fs.existsSync(p))\n if (foundChrome) return { path: foundChrome, name: 'Chrome' }\n\n // Edge 在 Windows 上的常见路径(包括系统内置的 Edge)\n const edgePaths = [\n process.env.LOCALAPPDATA + '\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n process.env.PROGRAMFILES + '\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n process.env['PROGRAMFILES(X86)'] + '\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n 'C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe',\n 'C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe'\n ]\n const foundEdge = edgePaths.find(p => fs.existsSync(p))\n if (foundEdge) return { path: foundEdge, name: 'Edge' }\n return null\n } else {\n // Linux:优先 Chrome,其次 Chromium,最后 Edge\n const paths: Array<{ p: string; name: string }> = [\n { p: '/usr/bin/google-chrome', name: 'Chrome' },\n { p: '/usr/bin/google-chrome-stable', name: 'Chrome' },\n { p: '/usr/bin/chromium', name: 'Chromium' },\n { p: '/usr/bin/chromium-browser', name: 'Chromium' },\n { p: '/usr/bin/microsoft-edge', name: 'Edge' },\n { p: '/usr/bin/microsoft-edge-stable', name: 'Edge' }\n ]\n const found = paths.find(({ p }) => fs.existsSync(p))\n return found ? { path: found.p, name: found.name } : null\n }\n}\n\nasync function startBrowserInBackground(): Promise<void> {\n const browserInfo = getDefaultBrowserPath()\n if (!browserInfo || !fs.existsSync(browserInfo.path)) {\n throw new Error('无法在系统中找到 Chrome 或 Edge 浏览器的默认安装路径。')\n }\n\n console.log(pc.yellow(`正在启动后台 ${browserInfo.name} 实例 (端口: ${CDP_PORT})...`))\n \n // 用户可以通过 --workspace CLI 选项或 WEBMCP_WORKSPACE 环境变量自定义。\n const userDataDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), '.webmcp_chrome_profile')\n \n const child = spawn(\n browserInfo.path,\n [\n `--remote-debugging-port=${CDP_PORT}`,\n `--user-data-dir=${userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check'\n ],\n {\n detached: true,\n stdio: 'ignore'\n }\n )\n\n child.unref() // 让子进程脱离父进程独立运行\n\n // 轮询等待 CDP 端口就绪\n for (let i = 0; i < 20; i++) {\n try {\n const urls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`]\n for (const url of urls) {\n try {\n await httpGet(url, 1000)\n console.log(pc.green(`${browserInfo.name} 启动并就绪。`))\n return\n } catch (err) {}\n }\n } catch (e) {\n // 忽略连接错误,继续重试\n }\n await new Promise(resolve => setTimeout(resolve, 500))\n }\n\n throw new Error(`${browserInfo.name} 启动超时,无法连接到 CDP 端口。`)\n}\n\nexport async function connectBrowser(): Promise<Browser> {\n const targetFilter = (target: any) => {\n try {\n const info = typeof target._getTargetInfo === 'function' ? target._getTargetInfo() : target\n const type = info.type || ''\n const url = info.url || ''\n \n // 过滤掉绝对不需要 attach 且容易发生死锁的后台/子框架 target\n if (\n type === 'service_worker' || \n type === 'shared_worker' || \n type === 'iframe' || \n type === 'other' || \n type === 'webview' ||\n type === 'background_page'\n ) {\n return false\n }\n \n // 过滤掉 devtools 和插件页面\n if (url.startsWith('devtools://') || url.startsWith('chrome-extension://')) {\n return false\n }\n \n return true\n } catch (e) {\n return false\n }\n }\n\n // 第一步:检查 9222 端口是否已有可用的 CDP 实例\n const portAddresses = [\n `http://127.0.0.1:${CDP_PORT}/json/version`,\n `http://localhost:${CDP_PORT}/json/version`\n ]\n\n for (const addr of portAddresses) {\n const isReady = await checkCdpReady(addr, 2)\n if (!isReady) continue\n\n const browserURL = addr.replace('/json/version', '')\n try {\n console.log(pc.yellow(`connectBrowser: 检测到端口 ${CDP_PORT} 已就绪,正在连接 ${browserURL}...`))\n const browser = await promiseWithTimeout(\n puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),\n 10000,\n `puppeteer.connect to ${browserURL} timed out`\n )\n console.log(pc.green(`connectBrowser: 成功连接 ${browserURL}`))\n return browser\n } catch {\n // 当前地址连不上,继续尝试下一个\n }\n }\n\n // 第二步:端口无响应,直接启动新的浏览器实例(使用独立用户数据目录,不影响用户已有浏览器)\n console.log(pc.yellow(`connectBrowser: 端口 ${CDP_PORT} 无响应,正在启动新的浏览器实例...`))\n try {\n await startBrowserInBackground()\n } catch (launchError: unknown) {\n const msg = launchError instanceof Error ? launchError.message : String(launchError)\n console.error(pc.red(`无法启动浏览器: ${msg}`))\n throw new Error('Browser launch failed.')\n }\n\n // 第三步:浏览器启动后再次尝试连接\n for (const addr of portAddresses) {\n const isReady = await checkCdpReady(addr, 3)\n if (!isReady) continue\n\n const browserURL = addr.replace('/json/version', '')\n try {\n console.log(pc.yellow(`connectBrowser: 浏览器已启动,正在连接 ${browserURL}...`))\n const browser = await promiseWithTimeout(\n puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),\n 10000,\n `puppeteer.connect to ${browserURL} after launch timed out`\n )\n console.log(pc.green(`connectBrowser: 成功连接 ${browserURL}`))\n return browser\n } catch {\n // 继续尝试下一个地址\n }\n }\n\n throw new Error(`无法连接到浏览器(端口 ${CDP_PORT}),请检查 Chrome/Edge 是否已安装。`)\n}\n\n\n/**\n * 通过 CDP 获取页面真实的 Chrome target ID(UUID 格式字符串)\n * 供 state.ts 等命令展示 tabs 列表时使用\n */\nexport async function getPageTargetId(page: Page): Promise<string> {\n const session = await page.target().createCDPSession()\n try {\n const { targetInfo } = await session.send('Target.getTargetInfo')\n return targetInfo.targetId\n } finally {\n await session.detach().catch(() => {})\n }\n}\n\nexport function getPageTargets(browser: Browser) {\n return browser.targets().filter(t => {\n try {\n const type = (typeof t.type === 'function' ? t.type() : (t as any).type) || ''\n const url = (typeof t.url === 'function' ? t.url() : (t as any).url) || ''\n return type === 'page' && !url.startsWith('devtools://')\n } catch {\n return false\n }\n })\n}\n\nexport function getTargetIdFromTarget(target: any): string {\n return typeof target._getTargetInfo === 'function'\n ? target._getTargetInfo().targetId\n : (target._targetId || target.targetId || '')\n}\n\nexport function findPageTargetByTabId(browser: Browser, tabid: string) {\n const pageTargets = getPageTargets(browser)\n return pageTargets.find(t => {\n const tid = getTargetIdFromTarget(t)\n return tid === tabid || tid.includes(tabid)\n }) ?? null\n}\n\nexport async function activateTabById(browser: Browser, tabid: string): Promise<void> {\n const target = findPageTargetByTabId(browser, tabid)\n if (!target) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n\n const realTabId = getTargetIdFromTarget(target)\n const pages = await browser.pages()\n let sessionPage = pages.find(p => !p.url().startsWith('devtools://'))\n if (!sessionPage && pages.length > 0) {\n sessionPage = pages[0]\n }\n if (!sessionPage) {\n sessionPage = await browser.newPage()\n }\n\n const session = await sessionPage.createCDPSession()\n try {\n await session.send('Target.activateTarget', { targetId: realTabId })\n } finally {\n await session.detach().catch(() => {})\n }\n}\n\nexport async function getTargetPage(browser: Browser, tabid?: string): Promise<Page> {\n const pageTargets = getPageTargets(browser)\n\n if (pageTargets.length === 0) {\n const newPage = await browser.newPage()\n await injectWebMCPPolyfillAndTools(newPage)\n return newPage\n }\n\n let targetPage: Page | null = null\n\n if (tabid !== undefined) {\n const target = findPageTargetByTabId(browser, tabid)\n if (!target) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n targetPage = await target.page()\n } else {\n // Chrome 的 /json/list 接口把当前激活 the tab 排在第一位,用它来判断激活 tab\n try {\n const urls = [\n `http://localhost:${CDP_PORT}/json/list`,\n `http://127.0.0.1:${CDP_PORT}/json/list`\n ]\n let activeTargetId: string | null = null\n for (const url of urls) {\n try {\n const body = await httpGet(url, 1000)\n const targetsData: Array<{ id: string; type: string; url: string }> = JSON.parse(body)\n // 找第一个 type=page 且不是 devtools:// 的 target(Chrome 把激活的排第一)\n const active = targetsData.find(t => t.type === 'page' && !t.url.startsWith('devtools://'))\n if (active) { activeTargetId = active.id; break }\n } catch { /* 忽略,继续试下一个地址 */ }\n }\n\n if (activeTargetId) {\n for (const target of pageTargets) {\n if (getTargetIdFromTarget(target) === activeTargetId) {\n targetPage = await target.page()\n break\n }\n }\n }\n } catch { /* 忽略,使用 fallback */ }\n\n // fallback:取最后一个非 devtools 页面\n if (!targetPage) {\n const lastTarget = pageTargets[pageTargets.length - 1]\n targetPage = await lastTarget.page()\n }\n }\n\n if (!targetPage) {\n throw new Error('无法获取目标页面')\n }\n\n // 注入 polyfill 和域名工具(幂等检查)\n await injectWebMCPPolyfillAndTools(targetPage)\n return targetPage\n}\n\n/**\n * 供 tabs open / back / forward 命令在导航完成后调用:强制注入(不做 flag 检查,因为 goto 后页面上下文已清空)\n */\nexport async function injectIntoPage(page: Page): Promise<void> {\n await injectWebMCPPolyfillAndTools(page, true)\n}\n\nasync function injectWebMCPPolyfillAndTools(page: Page, force = false) {\n // 检查 polyfill 是否已注入(force=true 时跳过,用于 goto 之后的强制重注入)\n const polyfillReady = !force && await page.evaluate(() => {\n return !!(window as any).__webmcpcli_init\n }).catch(() => false)\n\n if (!polyfillReady) {\n console.log(pc.cyan('当前页面尚未注入 WebMCP 环境,正在执行自动注入...'))\n\n const injectScriptPath = path.resolve(__dirname, 'inject-bundle.js')\n if (!fs.existsSync(injectScriptPath)) {\n throw new Error(`Cannot find inject-bundle.js at ${injectScriptPath}. Please ensure you run 'pnpm build:inject' first.`)\n }\n\n const scriptContent = fs.readFileSync(injectScriptPath, 'utf-8')\n\n // 注入 WebMCP polyfill\n try {\n await page.evaluate(scriptContent)\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n throw new Error('自动注入脚本执行失败: ' + msg)\n }\n\n // 等待工具异步注册\n await new Promise(resolve => setTimeout(resolve, 300))\n }\n\n // 无论 polyfill 是否刚注入,都检查域名工具(工具内部有防重复 flag)\n await injectDomainTools(page)\n}\n\n/**\n * 根据页面域名查找并注入对应的工具 bundle\n * bundle 文件位于 dist/webmcp-tools/{hostname}.js\n */\nasync function injectDomainTools(page: Page): Promise<void> {\n let hostname: string\n try {\n const url = new URL(page.url())\n hostname = url.hostname\n } catch {\n return // 非 http(s) 页面,跳过\n }\n\n const toolBundlePath = path.resolve(__dirname, 'webmcp-tools', `${hostname}.js`)\n if (!fs.existsSync(toolBundlePath)) {\n return // 没有对应的工具预置,跳过\n }\n\n console.log(pc.cyan(`检测到域名 ${hostname} 有预置工具,正在注入...`))\n\n const toolScript = fs.readFileSync(toolBundlePath, 'utf-8')\n try {\n await page.evaluate(toolScript)\n console.log(pc.green(`已为 ${hostname} 注入预置工具`))\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n // 工具注入失败不阻断主流程,仅打印警告\n console.warn(pc.yellow(`域名工具注入失败 (${hostname}): ${msg}`))\n }\n}\n","import { connectBrowser, getTargetPage, getPageTargetId, injectIntoPage } from '../browser'\n\nexport async function stateCommand({ tabid }: { tabid?: string }) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n\n // 1. 每一次state都重新注入一次page-agent-tool。 这样用户即使手动打开的网页也会有相应的工具。\n await injectIntoPage(page)\n\n // 2. 在页面上下文中执行,获取当前状态和可用工具\n const state = await page.evaluate(async () => {\n const url = document.URL\n const title = document.title\n\n // 尝试获取内置工具\n const mcp = (navigator as any).modelContextTesting || (navigator as any).modelContext\n let webmcpTools: any[] = []\n let contentData: any = `页面已准备好: ${title}`\n\n if (mcp) {\n if (typeof mcp.listTools === 'function') {\n const toolsResult = await mcp.listTools()\n webmcpTools = toolsResult?.tools || toolsResult || []\n }\n\n if (typeof mcp.executeTool === 'function') {\n try {\n const argsString = JSON.stringify({ action: 'browserState' })\n let stateRes = await mcp.executeTool('page-agent-tool', argsString)\n\n if (typeof stateRes === 'string') {\n try {\n stateRes = JSON.parse(stateRes)\n } catch (e) {\n // ignore\n }\n }\n if (stateRes && stateRes.content && stateRes.content.length > 0) {\n const textContent = stateRes.content.map((c: any) => c.text).join('\\\\n')\n\n // page-agent-tool 返回的格式通常是 \"浏览器状态: {\\\"url\\\":..., \\\"content\\\":\\\"[0]...\\\"}\"\n // 我们尝试把这个 JSON 提取出来,让外层更容易解析\n const prefix = '浏览器状态: '\n if (textContent.startsWith(prefix)) {\n try {\n const jsonStr = textContent.substring(prefix.length)\n const parsedState = JSON.parse(jsonStr)\n // 提取出带有索引的 DOM 树数据作为 content\n contentData = parsedState.content\n } catch (e) {\n contentData = textContent\n }\n } else {\n contentData = textContent\n }\n }\n } catch (e: any) {\n console.error('Snapshot error:', e.message)\n }\n }\n }\n\n return {\n content: contentData,\n url,\n title,\n webmcpTools\n }\n })\n\n // 3. 获取所有的 tab 信息(排除 devtools:// 内部页面)\n const pages = await browser.pages()\n const tabs = await Promise.all(\n pages.map(async (p) => {\n const pUrl = p.url()\n if (pUrl.startsWith('devtools://')) return null\n\n const pTitle = await Promise.race([\n p.title().catch(() => 'Unknown'),\n new Promise<string>((resolve) => setTimeout(() => resolve('Unknown'), 500))\n ])\n\n return {\n // 使用真实的 Chrome target ID,而非数组下标\n tabid: await getPageTargetId(p).catch(() => pUrl),\n title: pTitle,\n url: pUrl\n }\n })\n )\n\n return {\n ...state,\n tabs: tabs.filter(Boolean)\n }\n } finally {\n await browser.disconnect()\n }\n}\n","import JSON5 from 'json5'\nimport { connectBrowser, getTargetPage } from '../browser'\n\nexport async function runCommand({\n toolName,\n argsJson,\n tabid\n}: {\n toolName: string\n argsJson: string\n tabid?: string\n}) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n\n // 验证并清洗、转换参数为合法的 JSON 字符串\n let cleanedArgs = argsJson.trim()\n if (cleanedArgs.startsWith(\"'\") && cleanedArgs.endsWith(\"'\")) {\n cleanedArgs = cleanedArgs.slice(1, -1).trim()\n }\n\n try {\n const obj = JSON5.parse(cleanedArgs)\n cleanedArgs = JSON.stringify(obj)\n } catch (e: any) {\n throw new Error(`参数不是有效的 JSON 或 JS 对象: ${e.message}`)\n }\n\n const result = await page.evaluate(async (name, inputString) => {\n const mcp = (navigator as any).modelContextTesting || (navigator as any).modelContext\n \n if (!mcp || typeof mcp.executeTool !== 'function') {\n throw new Error('当前页面没有注入 WebMCP 环境 (navigator.modelContextTesting.executeTool 未找到)')\n }\n\n // executeTool 的第二个参数必须是 JSON 字符串\n let res = await mcp.executeTool(name, inputString)\n \n // executeTool 的返回值可能是普通对象,也可能是 JSON 字符串\n if (typeof res === 'string') {\n try {\n res = JSON.parse(res)\n } catch (e) {\n // ignore\n }\n }\n }, toolName, cleanedArgs)\n\n return result\n } finally {\n await browser.disconnect()\n }\n}\n","import { connectBrowser, getTargetPage } from '../browser'\n\nexport async function evaluateCommand({\n jsScript,\n tabid\n}: {\n jsScript: string\n tabid?: string\n}) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n\n const result = await page.evaluate(async (script) => {\n try {\n // 使用 eval 执行传入的 JavaScript 代码\n // eslint-disable-next-line no-eval\n const result = eval(script)\n \n // 如果结果是 Promise,等待其完成\n if (result instanceof Promise) {\n return await result\n }\n \n return result\n } catch (error: any) {\n return {\n error: true,\n message: error.message,\n stack: error.stack\n }\n }\n }, jsScript)\n\n return result\n } finally {\n await browser.disconnect()\n }\n}\n","import clipboard from 'clipboardy'\n\n/**\n * 将文本设置到系统剪贴板\n * 使用 clipboardy 包,跨平台统一接口,无需手动处理各系统命令\n */\nexport async function setClipboard(content: string): Promise<void> {\n try {\n await clipboard.write(content)\n } catch (error: any) {\n throw new Error(`设置剪贴板失败: ${error.message}`)\n }\n}\n","import {\n connectBrowser,\n getTargetPage,\n injectIntoPage,\n getPageTargetId,\n findPageTargetByTabId,\n activateTabById\n} from '../browser'\n\nfunction normalizeUrl(url: string): string {\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n return 'https://' + url\n }\n return url\n}\n\nexport async function tabsOpenCommand(url: string) {\n const browser = await connectBrowser()\n try {\n const page = await browser.newPage()\n url = normalizeUrl(url)\n\n await page.goto(url, { waitUntil: 'domcontentloaded' })\n await injectIntoPage(page)\n await new Promise((resolve) => setTimeout(resolve, 500))\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsCloseCommand(tabid: string) {\n const browser = await connectBrowser()\n try {\n const target = findPageTargetByTabId(browser, tabid)\n if (!target) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n\n const page = await target.page()\n if (!page) {\n throw new Error(`Tab with targetId \"${tabid}\" not found.`)\n }\n\n const closedTabid = await getPageTargetId(page).catch(() => tabid)\n await page.close()\n\n return {\n success: true,\n tabid: closedTabid\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsSwitchCommand(tabid: string) {\n const browser = await connectBrowser()\n try {\n await activateTabById(browser, tabid)\n const page = await getTargetPage(browser, tabid)\n\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsBackCommand(tabid?: string) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n const response = await page.goBack()\n\n if (!response) {\n return {\n success: false,\n error: '无法后退:已在历史记录起点',\n tabid: await getPageTargetId(page).catch(() => tabid),\n url: page.url(),\n title: await page.title()\n }\n }\n\n await injectIntoPage(page)\n\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n\nexport async function tabsForwardCommand(tabid?: string) {\n const browser = await connectBrowser()\n try {\n const page = await getTargetPage(browser, tabid)\n const response = await page.goForward()\n\n if (!response) {\n return {\n success: false,\n error: '无法前进:已在历史记录末尾',\n tabid: await getPageTargetId(page).catch(() => tabid),\n url: page.url(),\n title: await page.title()\n }\n }\n\n await injectIntoPage(page)\n\n return {\n success: true,\n tabid: await getPageTargetId(page),\n url: page.url(),\n title: await page.title()\n }\n } finally {\n await browser.disconnect()\n }\n}\n","{\n \"name\": \"@opentiny/webmcp-cli\",\n \"version\": \"0.0.1-alpha.5\",\n \"type\": \"module\",\n \"description\": \"WebMCP CLI for AI Agents to interact with browser via Puppeteer\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"bin\": {\n \"webmcp-cli\": \"./dist/bin.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build:inject\": \"node scripts/build-inject.mjs\",\n \"build\": \"tsup && pnpm run build:inject\",\n \"prepublishOnly\": \"pnpm run build\",\n \"link:global\": \"npm install -g .\",\n \"start\": \"node dist/bin.js\"\n },\n \"keywords\": [\n \"chrome\",\n \"puppeteer\",\n \"cli\"\n ],\n \"license\": \"MIT\",\n \"author\": \"OpenTiny Team\",\n \"engines\": {\n \"node\": \">=16\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"dependencies\": {\n \"@mcp-b/webmcp-polyfill\": \"catalog:\",\n \"@opentiny/next-sdk\": \"workspace:^\",\n \"@page-agent/page-controller\": \"catalog:\",\n \"clipboardy\": \"^5.3.1\",\n \"commander\": \"^11.1.0\",\n \"json5\": \"^2.2.3\",\n \"picocolors\": \"^1.0.0\",\n \"puppeteer-core\": \"^22.0.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"esbuild\": \"^0.25.0\",\n \"tsup\": \"^8.0.2\",\n \"tsx\": \"^4.0.0\",\n \"typescript\": \"^5.3.3\"\n }\n}\n"],"mappings":"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","names":["import_picocolors","import_fs","import_path","path","resolve","http","os","fs","pc","browser","puppeteer","page","tabid","tabid","browser","page","tabs","resolve","tabid","browser","page","JSON5","result","clipboard","browser","page","resolve","tabid","pc","result","jsScript","tabid"]}
package/dist/bin.js CHANGED
@@ -440,6 +440,7 @@ async function stateCommand({ tabid: tabid2 }) {
440
440
  }
441
441
 
442
442
  // src/commands/run.ts
443
+ import JSON5 from "json5";
443
444
  async function runCommand({
444
445
  toolName,
445
446
  argsJson,
@@ -448,10 +449,15 @@ async function runCommand({
448
449
  const browser2 = await connectBrowser();
449
450
  try {
450
451
  const page2 = await getTargetPage(browser2, tabid2);
452
+ let cleanedArgs = argsJson.trim();
453
+ if (cleanedArgs.startsWith("'") && cleanedArgs.endsWith("'")) {
454
+ cleanedArgs = cleanedArgs.slice(1, -1).trim();
455
+ }
451
456
  try {
452
- JSON.parse(argsJson);
457
+ const obj = JSON5.parse(cleanedArgs);
458
+ cleanedArgs = JSON.stringify(obj);
453
459
  } catch (e) {
454
- throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
460
+ throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON \u6216 JS \u5BF9\u8C61: ${e.message}`);
455
461
  }
456
462
  const result2 = await page2.evaluate(async (name, inputString) => {
457
463
  const mcp = navigator.modelContextTesting || navigator.modelContext;
@@ -465,8 +471,7 @@ async function runCommand({
465
471
  } catch (e) {
466
472
  }
467
473
  }
468
- return res;
469
- }, toolName, argsJson);
474
+ }, toolName, cleanedArgs);
470
475
  return result2;
471
476
  } finally {
472
477
  await browser2.disconnect();
@@ -627,7 +632,7 @@ async function tabsForwardCommand(tabid2) {
627
632
  // package.json
628
633
  var package_default = {
629
634
  name: "@opentiny/webmcp-cli",
630
- version: "0.0.1-alpha.4",
635
+ version: "0.0.1-alpha.5",
631
636
  type: "module",
632
637
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
633
638
  main: "dist/index.js",
@@ -666,6 +671,7 @@ var package_default = {
666
671
  "@page-agent/page-controller": "catalog:",
667
672
  clipboardy: "^5.3.1",
668
673
  commander: "^11.1.0",
674
+ json5: "^2.2.3",
669
675
  picocolors: "^1.0.0",
670
676
  "puppeteer-core": "^22.0.0"
671
677
  },
@@ -680,6 +686,13 @@ var package_default = {
680
686
 
681
687
  // src/bin.ts
682
688
  var program = new Command();
689
+ function cleanJsonString(str) {
690
+ let cleaned = str.trim();
691
+ if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
692
+ cleaned = cleaned.slice(1, -1).trim();
693
+ }
694
+ return cleaned;
695
+ }
683
696
  function parseTabId(id) {
684
697
  if (!id) return void 0;
685
698
  return id;
@@ -716,16 +729,16 @@ program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D
716
729
  handleCommandError(error, "state");
717
730
  }
718
731
  });
719
- program.command("run <toolName> [argsJson]").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(
732
+ 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(
720
733
  "-f, --file <path>",
721
- `\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A argsJson\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
722
- \u5982\u9700\u5728 argsJson \u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
734
+ `\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A\u53C2\u6570\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
735
+ \u5982\u9700\u5728\u53C2\u6570\u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
723
736
  @file:<path> \u8BFB\u53D6\u6587\u4EF6\u539F\u59CB\u6587\u672C
724
737
  @base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
725
738
  \u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
726
- ).action(async (toolName, argsJson, options) => {
739
+ ).action(async (toolName, args, options) => {
727
740
  try {
728
- let finalArgsJson = argsJson;
741
+ let finalArgsJson = "";
729
742
  if (options.file) {
730
743
  const filePath = resolve(process.cwd(), options.file);
731
744
  try {
@@ -733,9 +746,32 @@ program.command("run <toolName> [argsJson]").description("\u5411\u6307\u5B9A\u98
733
746
  } catch (e) {
734
747
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
735
748
  }
749
+ } else if (args && args.length > 0) {
750
+ const firstArg = args[0].trim();
751
+ const cleanedFirstArg = cleanJsonString(firstArg);
752
+ if (cleanedFirstArg.startsWith("{") && cleanedFirstArg.endsWith("}")) {
753
+ finalArgsJson = args.map(cleanJsonString).join(" ");
754
+ } else {
755
+ const obj = {};
756
+ for (const pair of args) {
757
+ const index = pair.indexOf("=");
758
+ if (index === -1) {
759
+ throw new Error(`\u65E0\u6548\u7684\u53C2\u6570\u683C\u5F0F "${pair}"\u3002\u5FC5\u987B\u662F JSON \u5B57\u7B26\u4E32\u6216 key=value \u683C\u5F0F\u3002`);
760
+ }
761
+ const key = pair.substring(0, index).trim();
762
+ let val = pair.substring(index + 1).trim();
763
+ val = cleanJsonString(val);
764
+ try {
765
+ obj[key] = JSON.parse(val);
766
+ } catch {
767
+ obj[key] = val;
768
+ }
769
+ }
770
+ finalArgsJson = JSON.stringify(obj);
771
+ }
736
772
  }
737
773
  if (!finalArgsJson) {
738
- throw new Error("\u5FC5\u987B\u63D0\u4F9B argsJson \u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
774
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B\u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
739
775
  }
740
776
  finalArgsJson = expandFileRefs(finalArgsJson);
741
777
  const result2 = await runCommand({