@opentiny/webmcp-cli 0.0.2 → 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 +67 -63
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +67 -63
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +66 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +66 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -264,6 +264,31 @@ async function checkCdpReady(url, retries = 3) {
|
|
|
264
264
|
}
|
|
265
265
|
return false;
|
|
266
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
|
+
}
|
|
267
292
|
function getDefaultBrowserPath() {
|
|
268
293
|
const platform = import_os.default.platform();
|
|
269
294
|
if (platform === "darwin") {
|
|
@@ -310,72 +335,56 @@ async function startBrowserInBackground() {
|
|
|
310
335
|
}
|
|
311
336
|
console.log(import_picocolors.default.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
|
|
312
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
|
+
}
|
|
313
347
|
const child = (0, import_child_process.spawn)(
|
|
314
348
|
browserInfo.path,
|
|
315
|
-
|
|
316
|
-
`--remote-debugging-port=${CDP_PORT}`,
|
|
317
|
-
`--user-data-dir=${userDataDir}`,
|
|
318
|
-
"--no-first-run",
|
|
319
|
-
"--no-default-browser-check"
|
|
320
|
-
],
|
|
349
|
+
launchArgs,
|
|
321
350
|
{
|
|
322
351
|
detached: true,
|
|
323
352
|
stdio: "ignore"
|
|
324
353
|
}
|
|
325
354
|
);
|
|
326
355
|
child.unref();
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
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 {
|
|
337
365
|
}
|
|
338
|
-
} catch (e) {
|
|
339
366
|
}
|
|
340
367
|
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
341
368
|
}
|
|
342
369
|
throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
|
|
343
370
|
}
|
|
344
371
|
async function connectBrowser() {
|
|
345
|
-
const
|
|
346
|
-
try {
|
|
347
|
-
const info = typeof target._getTargetInfo === "function" ? target._getTargetInfo() : target;
|
|
348
|
-
const type = info.type || "";
|
|
349
|
-
const url = info.url || "";
|
|
350
|
-
if (type === "service_worker" || type === "shared_worker" || type === "iframe" || type === "other" || type === "webview" || type === "background_page") {
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
if (url.startsWith("devtools://") || url.startsWith("chrome-extension://")) {
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
return true;
|
|
357
|
-
} catch (e) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
|
-
const portAddresses = [
|
|
372
|
+
const versionAddresses = [
|
|
362
373
|
`http://127.0.0.1:${CDP_PORT}/json/version`,
|
|
363
374
|
`http://localhost:${CDP_PORT}/json/version`
|
|
364
375
|
];
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
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}...`));
|
|
369
379
|
try {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
import_puppeteer_core.default.connect({ browserURL, defaultViewport: null, targetFilter }),
|
|
373
|
-
1e4,
|
|
374
|
-
`puppeteer.connect to ${browserURL} timed out`
|
|
375
|
-
);
|
|
376
|
-
console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
380
|
+
const browser = await connectPuppeteer(existingUrl);
|
|
381
|
+
console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${existingUrl}`));
|
|
377
382
|
return browser;
|
|
378
|
-
} catch {
|
|
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`
|
|
387
|
+
);
|
|
379
388
|
}
|
|
380
389
|
}
|
|
381
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...`));
|
|
@@ -386,23 +395,18 @@ async function connectBrowser() {
|
|
|
386
395
|
console.error(import_picocolors.default.red(`\u65E0\u6CD5\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
|
|
387
396
|
throw new Error("Browser launch failed.");
|
|
388
397
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
console.log(import_picocolors.default.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
|
|
401
|
-
return browser;
|
|
402
|
-
} catch {
|
|
403
|
-
}
|
|
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`);
|
|
404
409
|
}
|
|
405
|
-
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`);
|
|
406
410
|
}
|
|
407
411
|
async function getPageTargetId(page) {
|
|
408
412
|
const session = await page.target().createCDPSession();
|
|
@@ -795,7 +799,7 @@ async function tabsForwardCommand(tabid) {
|
|
|
795
799
|
// package.json
|
|
796
800
|
var package_default = {
|
|
797
801
|
name: "@opentiny/webmcp-cli",
|
|
798
|
-
version: "0.0.
|
|
802
|
+
version: "0.0.3",
|
|
799
803
|
type: "module",
|
|
800
804
|
description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
|
|
801
805
|
main: "dist/index.js",
|
package/dist/bin.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bin.ts","../src/expand-file-refs.ts","../src/parse-run-args.ts","../src/browser.ts","../src/commands/state.ts","../src/commands/run.ts","../src/commands/clipboard.ts","../src/commands/tabs.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { Command } from 'commander'\nimport pc from 'picocolors'\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport { getFileBaseDir } from './expand-file-refs'\nimport { prepareRunArgsJson } from './parse-run-args'\nimport { stateCommand } from './commands/state'\nimport { runCommand } from './commands/run'\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 cleanOldLogs(baseDir: string, logDir: string) {\n try {\n // 1. 清理原本根目录下的旧版单文件和旧版日期文件\n const oldLogFile = path.join(baseDir, 'webmcp-cli.log')\n if (fs.existsSync(oldLogFile)) {\n try { fs.unlinkSync(oldLogFile) } catch {}\n }\n if (fs.existsSync(baseDir)) {\n const baseFiles = fs.readdirSync(baseDir)\n for (const file of baseFiles) {\n if (/^webmcp-cli-\\d{4}-\\d{2}-\\d{2}\\.log$/.test(file)) {\n try { fs.unlinkSync(path.join(baseDir, file)) } catch {}\n }\n }\n }\n\n // 2. 清理 logs 目录下超过 7 天的日志\n if (!fs.existsSync(logDir)) return\n const files = fs.readdirSync(logDir)\n const logFilePattern = /^webmcp-cli-(\\d{4}-\\d{2}-\\d{2})\\.log$/\n const now = Date.now()\n const maxAgeMs = 7 * 24 * 60 * 60 * 1000 // 7天\n\n for (const file of files) {\n const match = file.match(logFilePattern)\n if (match) {\n const fileDateStr = match[1]\n const fileDate = new Date(fileDateStr).getTime()\n if (isNaN(fileDate)) continue\n if (now - fileDate > maxAgeMs) {\n try {\n fs.unlinkSync(path.join(logDir, file))\n } catch {\n // 忽略单文件删除失败\n }\n }\n }\n }\n } catch (e) {\n // 忽略清理日志本身的错误\n }\n}\n\nfunction writeLog(commandName: string, args: any, result: any, error?: any) {\n try {\n const baseDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), '.webmcp_chrome_profile')\n const logDir = path.join(baseDir, 'logs')\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true })\n }\n const todayStr = new Date().toISOString().split('T')[0]\n const logFile = path.join(logDir, `webmcp-cli-${todayStr}.log`)\n\n const timestamp = new Date().toISOString()\n let logText = `========================================\\n`\n logText += `[${timestamp}] COMMAND: ${commandName}\\n`\n logText += `ARGS:\\n${JSON.stringify(args, null, 2)}\\n\\n`\n\n if (error) {\n logText += `ERROR:\\n${error instanceof Error ? error.stack : String(error)}\\n`\n } else if (result) {\n logText += `RESULT:\\n`\n try {\n if (result && typeof result === 'object') {\n logText += JSON.stringify(result, null, 2).split('\\n').map(line => ` ${line}`).join('\\n') + '\\n'\n } else {\n logText += String(result).split('\\n').map(line => ` ${line}`).join('\\n') + '\\n'\n }\n } catch {\n logText += JSON.stringify(result, null, 2).split('\\n').map(line => ` ${line}`).join('\\n') + '\\n'\n }\n } else {\n logText += `RESULT: null\\n`\n }\n logText += `========================================\\n\\n`\n\n fs.appendFileSync(logFile, logText, 'utf-8')\n cleanOldLogs(baseDir, logDir)\n } catch (e) {\n // 忽略日志写入本身的错误,防止阻塞主流程\n }\n}\n\nfunction handleCommandError(error: unknown, commandName: string, args?: any): never {\n const msg = error instanceof Error ? error.message : String(error)\n writeLog(commandName, args, null, error)\n console.error(pc.red(`Error executing ${commandName} command: ${msg}`))\n process.exit(1)\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('获取浏览器当前页签或指定页签的导航元数据(url、title、webmcpTools、所有页签列表)')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .action(async (options) => {\n const args = { tabid: parseTabId(options.tabid) }\n try {\n const result = await stateCommand(args)\n writeLog('state', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'state', args)\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 let finalArgsJson = ''\n const rawArgs = { toolName, args, file: options.file, tabid: options.tabid }\n try {\n let fileContent: string | undefined\n let fileBaseDir = process.cwd()\n\n if (options.file) {\n const filePath = resolve(process.cwd(), options.file)\n fileBaseDir = getFileBaseDir(options.file)\n try {\n fileContent = readFileSync(filePath, 'utf-8')\n } catch (e: any) {\n throw new Error(`无法读取文件 \"${filePath}\": ${e.message}`)\n }\n }\n\n finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir)\n\n const result = await runCommand({\n toolName,\n argsJson: finalArgsJson,\n tabid: parseTabId(options.tabid)\n })\n writeLog('run', { ...rawArgs, finalArgsJson }, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'run', { ...rawArgs, finalArgsJson })\n }\n })\n\n\nprogram\n .command('clipboard <content>')\n .description('将内容设置到系统剪贴板')\n .action(async (content) => {\n const args = { content }\n try {\n await setClipboard(content)\n const result = { success: true, message: '内容已设置到系统剪贴板' }\n writeLog('clipboard', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'clipboard', args)\n }\n })\n\nconst tabs = program.command('tabs').description('管理浏览器标签页')\n\ntabs\n .command('open <url>')\n .description('打开新网页')\n .action(async (url) => {\n const args = { url }\n try {\n const result = await tabsOpenCommand(url)\n writeLog('tabs open', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs open', args)\n }\n })\n\ntabs\n .command('close <tabid>')\n .description('关闭指定 tabid 的标签页')\n .action(async (tabid) => {\n const args = { tabid }\n try {\n const result = await tabsCloseCommand(tabid)\n writeLog('tabs close', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs close', args)\n }\n })\n\ntabs\n .command('switch <tabid>')\n .description('激活并切换到指定 tabid 的标签页')\n .action(async (tabid) => {\n const args = { tabid }\n try {\n const result = await tabsSwitchCommand(tabid)\n writeLog('tabs switch', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs switch', args)\n }\n })\n\ntabs\n .command('back [tabid]')\n .description('将当前或指定标签页导航后退一步')\n .action(async (tabid) => {\n const args = { tabid: parseTabId(tabid) }\n try {\n const result = await tabsBackCommand(args.tabid)\n writeLog('tabs back', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs back', args)\n }\n })\n\ntabs\n .command('forward [tabid]')\n .description('将当前或指定标签页导航前进一步')\n .action(async (tabid) => {\n const args = { tabid: parseTabId(tabid) }\n try {\n const result = await tabsForwardCommand(args.tabid)\n writeLog('tabs forward', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs forward', args)\n }\n })\n\nprogram.parse(process.argv)\n","import { readFileSync } from 'fs'\nimport { dirname, isAbsolute, normalize, resolve } from 'path'\nimport { fileURLToPath } from 'url'\n\n/** 匹配完整 JSON 字符串字面量 */\nconst JSON_STRING_LITERAL = /\"(?:[^\"\\\\]|\\\\.)*\"/g\n\n/** 匹配未加引号的 @file: / @base64file: 占位符(路径中可含反斜杠) */\nconst UNQUOTED_FILE_REF = /(?<![\\\\\"])@(base64)?file:([^\\s\"',}\\]]+)/g\n\nconst FILE_REF_PREFIX = /^@(base64)?file:(.+)$/s\n\nexport function resolveFilePath(filePath: string, baseDir: string = process.cwd()): string {\n let normalizedPath = filePath.trim()\n\n if (\n (normalizedPath.startsWith('\"') && normalizedPath.endsWith('\"')) ||\n (normalizedPath.startsWith(\"'\") && normalizedPath.endsWith(\"'\"))\n ) {\n normalizedPath = normalizedPath.slice(1, -1).trim()\n }\n\n if (normalizedPath.startsWith('file://')) {\n normalizedPath = fileURLToPath(normalizedPath)\n }\n\n if (isAbsolute(normalizedPath)) {\n return normalize(normalizedPath)\n }\n\n return resolve(baseDir, normalizedPath)\n}\n\n/**\n * 解码文件路径:仅折叠 JSON 中的 `\\\\`,不把 `\\t`、`\\U` 等当作转义。\n */\nexport function decodeFilePath(rawPath: string): string {\n return rawPath.replace(/\\\\\\\\/g, '\\\\')\n}\n\nfunction readFileRefContent(filePath: string, baseDir: string, base64: boolean): string {\n const absPath = resolveFilePath(filePath, baseDir)\n let content: string\n try {\n content = readFileSync(absPath, 'utf-8')\n } catch (e: any) {\n throw new Error(`无法读取文件引用 \"${filePath}\" (${absPath}): ${e.message}`)\n }\n\n return base64 ? Buffer.from(content, 'utf-8').toString('base64') : content\n}\n\nfunction expandFileRefValue(rawInner: string, baseDir: string): string | null {\n const match = rawInner.match(FILE_REF_PREFIX)\n if (!match) {\n return null\n }\n\n const [, base64Flag, rawPath] = match\n const filePath = decodeFilePath(rawPath)\n const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag))\n return JSON.stringify(value)\n}\n\nfunction expandQuotedFileRefs(argsJson: string, baseDir: string): string {\n return argsJson.replace(JSON_STRING_LITERAL, (jsonStringLiteral) => {\n const rawInner = jsonStringLiteral.slice(1, -1)\n const replacement = expandFileRefValue(rawInner, baseDir)\n return replacement ?? jsonStringLiteral\n })\n}\n\nfunction expandUnquotedFileRefs(argsJson: string, baseDir: string): string {\n return argsJson.replace(UNQUOTED_FILE_REF, (match, base64Flag, filePath) => {\n const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag))\n return JSON.stringify(value)\n })\n}\n\n/**\n * 展开 argsJson 中的文件引用占位符:\n * @file:<path> → 读取文件原始文本内容\n * @base64file:<path> → 读取文件内容并 Base64 编码\n */\nexport function expandFileRefs(argsJson: string, baseDir: string = process.cwd()): string {\n const withQuotedExpanded = expandQuotedFileRefs(argsJson, baseDir)\n return expandUnquotedFileRefs(withQuotedExpanded, baseDir)\n}\n\n/**\n * 确定 `@file` / `@base64file` 相对路径的解析基准目录。\n *\n * 未使用 `-f/--file` 时以 `process.cwd()` 为基准;\n * 使用参数文件时以该文件所在目录为基准,使 `args.json` 内 `./article.md` 等路径\n * 相对于参数文件而非 shell 当前工作目录解析。\n *\n * @example\n * // cwd 为 /home/user/project,执行 webmcp run tool -f test/fixtures/nested/args.json\n * getFileBaseDir('test/fixtures/nested/args.json')\n * // => '/home/user/project/test/fixtures/nested'\n */\nexport function getFileBaseDir(fileOption?: string): string {\n if (!fileOption) {\n return process.cwd()\n }\n return dirname(resolve(process.cwd(), fileOption))\n}\n","import JSON5 from 'json5'\nimport { expandFileRefs } from './expand-file-refs'\n\nexport function 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 looksLikeObjectBody(raw: string): boolean {\n return /^(\"?[\\w-]+\"?\\s*:)/.test(raw)\n}\n\n/**\n * 拼接 shell 拆散的参数。\n * 兼容 bash 花括号展开后丢失 `{}` 的情况:title:\"...\", \"content\":\"...\"\n */\nexport function joinShellArgs(args: string[]): string {\n const cleaned = args.map(cleanJsonString)\n const first = cleaned[0] ?? ''\n\n if (first.startsWith('{')) {\n return cleaned.join(' ')\n }\n\n if (looksLikeObjectBody(first)) {\n let joined = cleaned.join(' ')\n if (!joined.startsWith('{')) {\n joined = `{${joined}}`\n }\n if (!joined.endsWith('}')) {\n joined = `${joined}}`\n }\n return joined\n }\n\n return cleaned.join(' ')\n}\n\n/** 在相邻属性之间补逗号:title:\"a\" \"content\":\"b\" → title:\"a\",\"content\":\"b\" */\nexport function insertMissingCommas(raw: string): string {\n return raw.replace(/\"(\\s+)(\"?[\\w-]+\"?\\s*:)/g, '\",$2')\n}\n\n/**\n * 为未加引号的字符串值补引号。\n * 典型 shell 损坏:{title:TinyVue Icon,content:@base64file:./a.md}\n */\nexport function quoteUnquotedStringValues(raw: string): string {\n return raw.replace(\n /([{,]\\s*(?:\"([^\"]+)\"|([\\w-]+))\\s*:\\s*)(?!\"@(?:base64)?file:|\"|true|false|null|-?\\d|\\{|\\[)([^\",}\\]]+?)(?=\\s*[,}])/g,\n (_match, prefix, _quotedKey, _unquotedKey, value: string) => {\n const trimmed = value.trim()\n if (/^-?\\d+(\\.\\d+)?([eE][+-]?\\d+)?$/.test(trimmed)) {\n return prefix + trimmed\n }\n return prefix + JSON.stringify(trimmed)\n }\n )\n}\n\nexport function normalizeShellDamagedJson(raw: string): string {\n let normalized = raw.trim()\n normalized = insertMissingCommas(normalized)\n normalized = quoteUnquotedStringValues(normalized)\n return normalized\n}\n\nexport function buildArgsFromKeyValuePairs(args: string[]): string {\n const obj: Record<string, unknown> = {}\n for (const pair of args) {\n const index = pair.indexOf('=')\n if (index === -1) {\n throw new Error(`无效的参数格式 \"${pair}\"。请使用 key=value,或 JSON / -f 文件。`)\n }\n const key = pair.substring(0, index).trim()\n let val = cleanJsonString(pair.substring(index + 1).trim())\n\n try {\n obj[key] = JSON.parse(val)\n } catch {\n obj[key] = val\n }\n }\n return JSON.stringify(obj)\n}\n\n/**\n * 将用户输入的 JSON / JS 对象字面量解析并规范化为标准 JSON 字符串。\n *\n * 解析顺序:\n * 1. 去除首尾空白及 shell 单引号包裹\n * 2. 使用 JSON5 解析(支持尾随逗号、未加引号的 key 等)\n * 3. 若失败,尝试修复 shell 损坏(缺逗号、未加引号的字符串值)后重试\n *\n * @param raw - 原始参数字符串(可能已被 expandFileRefs 展开 @file 占位符)\n * @returns 规范化后的 JSON 字符串,可直接传给 MCP 工具\n *\n * @example\n * // 标准 JSON\n * parseArgsJson('{\"title\":\"标题\",\"content\":\"正文\"}')\n * // => '{\"title\":\"标题\",\"content\":\"正文\"}'\n *\n * @example\n * // shell 损坏:花括号展开后丢失逗号、值未加引号\n * parseArgsJson('{title:TinyVue Icon,content:\"hello\"}')\n * // => '{\"title\":\"TinyVue Icon\",\"content\":\"hello\"}'\n *\n * @example\n * // bash 单引号包裹(外层引号会被 cleanJsonString 剥除)\n * parseArgsJson('\\'{\"title\":\"标题\"}\\'')\n * // => '{\"title\":\"标题\"}'\n */\nexport function parseArgsJson(raw: string): string {\n const trimmed = cleanJsonString(raw)\n try {\n return JSON.stringify(JSON5.parse(trimmed))\n } catch (firstError) {\n const repaired = normalizeShellDamagedJson(trimmed)\n try {\n return JSON.stringify(JSON5.parse(repaired))\n } catch {\n const hint =\n '提示:bash 中 JSON 必须用单引号包裹,例如 \\'{\"title\":\"标题\",\"content\":\"@base64file:./article.md\"}\\';' +\n '或使用 key=value:title=标题 content=@base64file:./article.md;' +\n '或使用 -f args.json。'\n const message = firstError instanceof Error ? firstError.message : String(firstError)\n throw new Error(`参数不是有效的 JSON 或 JS 对象: ${message}\\n${hint}`)\n }\n }\n}\n\n/**\n * `webmcp run` 命令的入口:从 CLI 参数或文件内容组装最终 JSON 参数字符串,兼容三种情况。\n *\n * 参数来源优先级:`-f/--file` 文件内容 > 命令行 `args`。\n * 命令行支持三种格式(自动识别):\n * - JSON 对象:`{\"title\":\"标题\"}`\n * - shell 拆散的 JSON 片段:`title:\"标题\" \"content\":\"正文\"`(bash 花括号展开后)\n * - key=value:`title=标题 content=@base64file:./article.md`\n *\n * 组装后会调用 expandFileRefs 展开 `@file` / `@base64file` 占位符,再经 parseArgsJson 规范化。\n *\n * @param args - commander 解析后的剩余参数数组\n * @param fileContent - `-f` 指定文件的文本内容;未指定时为 undefined\n * @param baseDir - 解析 `@file` 相对路径的基准目录(`-f` 时为文件所在目录,否则为 cwd)\n * @returns 规范化后的 JSON 字符串\n *\n * @example\n * // CLI: webmcp run publish-article '{\"title\":\"标题\",\"content\":\"@base64file:./article.md\"}'\n * prepareRunArgsJson(['{\"title\":\"标题\",\"content\":\"@base64file:./article.md\"}'], undefined, process.cwd())\n *\n * @example\n * // CLI: webmcp run publish-article title=标题 content=@base64file:./article.md\n * prepareRunArgsJson(['title=标题', 'content=@base64file:./article.md'], undefined, process.cwd())\n *\n * @example\n * // CLI: webmcp run publish-article -f args.json\n * prepareRunArgsJson([], readFileSync('args.json', 'utf-8'), dirname('args.json'))\n */\nexport function prepareRunArgsJson(args: string[], fileContent: string | undefined, baseDir: string): string {\n let raw = ''\n\n if (fileContent !== undefined) {\n raw = fileContent.trim()\n } else if (args.length > 0) {\n const joined = joinShellArgs(args)\n if (joined.startsWith('{') || looksLikeObjectBody(joined)) {\n raw = joined\n } else if (args.every((arg) => arg.includes('='))) {\n raw = buildArgsFromKeyValuePairs(args)\n } else {\n raw = joined\n }\n }\n\n if (!raw) {\n throw new Error('必须提供参数或通过 -f/--file 指定参数文件')\n }\n\n const expanded = expandFileRefs(raw, baseDir)\n return parseArgsJson(expanded)\n}\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 // 显式等待 WebMCP 注册接口完全就绪\n await page.waitForFunction(() => {\n const mcp = (navigator as any).modelContext\n return mcp && typeof mcp.registerTool === 'function'\n }, { timeout: 10000 }).catch(() => {\n console.warn('等待 WebMCP registerTool 接口就绪超时,正在继续注入...')\n })\n }\n\n // 无论 polyfill 是否刚注入,都检查域名工具(工具内部有防重复 flag)\n await injectDomainTools(page)\n}\n\nfunction getToolsBundleName(hostname: string): string | null {\n const normalized = hostname.split(':')[0].toLowerCase()\n const toolsDir = path.resolve(__dirname, 'webmcp-tools')\n if (!fs.existsSync(toolsDir)) {\n return null\n }\n let files: string[] = []\n try {\n files = fs.readdirSync(toolsDir)\n } catch {\n return null\n }\n const supportedDomains = files\n .filter(f => f.endsWith('.js'))\n .map(f => f.slice(0, -3))\n\n if (supportedDomains.includes(normalized)) {\n return `webmcp-tools/${normalized}.js`\n }\n for (const domain of supportedDomains) {\n if (normalized.endsWith('.' + domain)) {\n return `webmcp-tools/${domain}.js`\n }\n }\n return null\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 bundleName = getToolsBundleName(hostname)\n if (!bundleName) {\n return // 没有对应的工具预置,跳过\n }\n\n const toolBundlePath = path.resolve(__dirname, bundleName)\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 // 不获取 DOM content(由调用方按需通过 browserState 拉取)\n const state = await page.evaluate(async () => {\n const url = document.URL\n const title = document.title\n\n const mcp = (navigator as any).modelContextTesting || (navigator as any).modelContext\n let webmcpTools: any[] = []\n\n if (mcp && typeof mcp.listTools === 'function') {\n const toolsResult = await mcp.listTools()\n webmcpTools = toolsResult?.tools || toolsResult || []\n }\n\n return { url, title, webmcpTools }\n })\n\n // 3. 获取所有标签页信息(排除 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 // argsJson 已在 bin.ts 中完成 @base64file 展开与 JSON 校验\n const cleanedArgs = argsJson.trim()\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 {\n // 保留原始字符串\n }\n }\n\n if (res === undefined || res === null) {\n throw new Error('工具 execute 未返回结果,请检查工具实现是否缺少 return')\n }\n\n if (typeof res === 'object' && (res as { success?: boolean }).success === false) {\n const failed = res as { error?: string; message?: string }\n throw new Error(failed.error || failed.message || '工具执行失败')\n }\n\n return res\n }, toolName, cleanedArgs)\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.2\",\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_fs","import_path","import_os","import_picocolors","JSON5","import_fs","import_path","import_url","path","resolve","http","os","fs","pc","puppeteer","tabs","resolve","clipboard","resolve","path","fs","os","pc"]}
|
|
1
|
+
{"version":3,"sources":["../src/bin.ts","../src/expand-file-refs.ts","../src/parse-run-args.ts","../src/browser.ts","../src/commands/state.ts","../src/commands/run.ts","../src/commands/clipboard.ts","../src/commands/tabs.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { Command } from 'commander'\nimport pc from 'picocolors'\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport { getFileBaseDir } from './expand-file-refs'\nimport { prepareRunArgsJson } from './parse-run-args'\nimport { stateCommand } from './commands/state'\nimport { runCommand } from './commands/run'\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 cleanOldLogs(baseDir: string, logDir: string) {\n try {\n // 1. 清理原本根目录下的旧版单文件和旧版日期文件\n const oldLogFile = path.join(baseDir, 'webmcp-cli.log')\n if (fs.existsSync(oldLogFile)) {\n try { fs.unlinkSync(oldLogFile) } catch {}\n }\n if (fs.existsSync(baseDir)) {\n const baseFiles = fs.readdirSync(baseDir)\n for (const file of baseFiles) {\n if (/^webmcp-cli-\\d{4}-\\d{2}-\\d{2}\\.log$/.test(file)) {\n try { fs.unlinkSync(path.join(baseDir, file)) } catch {}\n }\n }\n }\n\n // 2. 清理 logs 目录下超过 7 天的日志\n if (!fs.existsSync(logDir)) return\n const files = fs.readdirSync(logDir)\n const logFilePattern = /^webmcp-cli-(\\d{4}-\\d{2}-\\d{2})\\.log$/\n const now = Date.now()\n const maxAgeMs = 7 * 24 * 60 * 60 * 1000 // 7天\n\n for (const file of files) {\n const match = file.match(logFilePattern)\n if (match) {\n const fileDateStr = match[1]\n const fileDate = new Date(fileDateStr).getTime()\n if (isNaN(fileDate)) continue\n if (now - fileDate > maxAgeMs) {\n try {\n fs.unlinkSync(path.join(logDir, file))\n } catch {\n // 忽略单文件删除失败\n }\n }\n }\n }\n } catch (e) {\n // 忽略清理日志本身的错误\n }\n}\n\nfunction writeLog(commandName: string, args: any, result: any, error?: any) {\n try {\n const baseDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), '.webmcp_chrome_profile')\n const logDir = path.join(baseDir, 'logs')\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true })\n }\n const todayStr = new Date().toISOString().split('T')[0]\n const logFile = path.join(logDir, `webmcp-cli-${todayStr}.log`)\n\n const timestamp = new Date().toISOString()\n let logText = `========================================\\n`\n logText += `[${timestamp}] COMMAND: ${commandName}\\n`\n logText += `ARGS:\\n${JSON.stringify(args, null, 2)}\\n\\n`\n\n if (error) {\n logText += `ERROR:\\n${error instanceof Error ? error.stack : String(error)}\\n`\n } else if (result) {\n logText += `RESULT:\\n`\n try {\n if (result && typeof result === 'object') {\n logText += JSON.stringify(result, null, 2).split('\\n').map(line => ` ${line}`).join('\\n') + '\\n'\n } else {\n logText += String(result).split('\\n').map(line => ` ${line}`).join('\\n') + '\\n'\n }\n } catch {\n logText += JSON.stringify(result, null, 2).split('\\n').map(line => ` ${line}`).join('\\n') + '\\n'\n }\n } else {\n logText += `RESULT: null\\n`\n }\n logText += `========================================\\n\\n`\n\n fs.appendFileSync(logFile, logText, 'utf-8')\n cleanOldLogs(baseDir, logDir)\n } catch (e) {\n // 忽略日志写入本身的错误,防止阻塞主流程\n }\n}\n\nfunction handleCommandError(error: unknown, commandName: string, args?: any): never {\n const msg = error instanceof Error ? error.message : String(error)\n writeLog(commandName, args, null, error)\n console.error(pc.red(`Error executing ${commandName} command: ${msg}`))\n process.exit(1)\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('获取浏览器当前页签或指定页签的导航元数据(url、title、webmcpTools、所有页签列表)')\n .option('-t, --tabid <id>', '指定页签的 ID')\n .action(async (options) => {\n const args = { tabid: parseTabId(options.tabid) }\n try {\n const result = await stateCommand(args)\n writeLog('state', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'state', args)\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 let finalArgsJson = ''\n const rawArgs = { toolName, args, file: options.file, tabid: options.tabid }\n try {\n let fileContent: string | undefined\n let fileBaseDir = process.cwd()\n\n if (options.file) {\n const filePath = resolve(process.cwd(), options.file)\n fileBaseDir = getFileBaseDir(options.file)\n try {\n fileContent = readFileSync(filePath, 'utf-8')\n } catch (e: any) {\n throw new Error(`无法读取文件 \"${filePath}\": ${e.message}`)\n }\n }\n\n finalArgsJson = prepareRunArgsJson(args ?? [], fileContent, fileBaseDir)\n\n const result = await runCommand({\n toolName,\n argsJson: finalArgsJson,\n tabid: parseTabId(options.tabid)\n })\n writeLog('run', { ...rawArgs, finalArgsJson }, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'run', { ...rawArgs, finalArgsJson })\n }\n })\n\n\nprogram\n .command('clipboard <content>')\n .description('将内容设置到系统剪贴板')\n .action(async (content) => {\n const args = { content }\n try {\n await setClipboard(content)\n const result = { success: true, message: '内容已设置到系统剪贴板' }\n writeLog('clipboard', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'clipboard', args)\n }\n })\n\nconst tabs = program.command('tabs').description('管理浏览器标签页')\n\ntabs\n .command('open <url>')\n .description('打开新网页')\n .action(async (url) => {\n const args = { url }\n try {\n const result = await tabsOpenCommand(url)\n writeLog('tabs open', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs open', args)\n }\n })\n\ntabs\n .command('close <tabid>')\n .description('关闭指定 tabid 的标签页')\n .action(async (tabid) => {\n const args = { tabid }\n try {\n const result = await tabsCloseCommand(tabid)\n writeLog('tabs close', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs close', args)\n }\n })\n\ntabs\n .command('switch <tabid>')\n .description('激活并切换到指定 tabid 的标签页')\n .action(async (tabid) => {\n const args = { tabid }\n try {\n const result = await tabsSwitchCommand(tabid)\n writeLog('tabs switch', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs switch', args)\n }\n })\n\ntabs\n .command('back [tabid]')\n .description('将当前或指定标签页导航后退一步')\n .action(async (tabid) => {\n const args = { tabid: parseTabId(tabid) }\n try {\n const result = await tabsBackCommand(args.tabid)\n writeLog('tabs back', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs back', args)\n }\n })\n\ntabs\n .command('forward [tabid]')\n .description('将当前或指定标签页导航前进一步')\n .action(async (tabid) => {\n const args = { tabid: parseTabId(tabid) }\n try {\n const result = await tabsForwardCommand(args.tabid)\n writeLog('tabs forward', args, result)\n console.log(JSON.stringify(result, null, 2))\n } catch (error: unknown) {\n handleCommandError(error, 'tabs forward', args)\n }\n })\n\nprogram.parse(process.argv)\n","import { readFileSync } from 'fs'\nimport { dirname, isAbsolute, normalize, resolve } from 'path'\nimport { fileURLToPath } from 'url'\n\n/** 匹配完整 JSON 字符串字面量 */\nconst JSON_STRING_LITERAL = /\"(?:[^\"\\\\]|\\\\.)*\"/g\n\n/** 匹配未加引号的 @file: / @base64file: 占位符(路径中可含反斜杠) */\nconst UNQUOTED_FILE_REF = /(?<![\\\\\"])@(base64)?file:([^\\s\"',}\\]]+)/g\n\nconst FILE_REF_PREFIX = /^@(base64)?file:(.+)$/s\n\nexport function resolveFilePath(filePath: string, baseDir: string = process.cwd()): string {\n let normalizedPath = filePath.trim()\n\n if (\n (normalizedPath.startsWith('\"') && normalizedPath.endsWith('\"')) ||\n (normalizedPath.startsWith(\"'\") && normalizedPath.endsWith(\"'\"))\n ) {\n normalizedPath = normalizedPath.slice(1, -1).trim()\n }\n\n if (normalizedPath.startsWith('file://')) {\n normalizedPath = fileURLToPath(normalizedPath)\n }\n\n if (isAbsolute(normalizedPath)) {\n return normalize(normalizedPath)\n }\n\n return resolve(baseDir, normalizedPath)\n}\n\n/**\n * 解码文件路径:仅折叠 JSON 中的 `\\\\`,不把 `\\t`、`\\U` 等当作转义。\n */\nexport function decodeFilePath(rawPath: string): string {\n return rawPath.replace(/\\\\\\\\/g, '\\\\')\n}\n\nfunction readFileRefContent(filePath: string, baseDir: string, base64: boolean): string {\n const absPath = resolveFilePath(filePath, baseDir)\n let content: string\n try {\n content = readFileSync(absPath, 'utf-8')\n } catch (e: any) {\n throw new Error(`无法读取文件引用 \"${filePath}\" (${absPath}): ${e.message}`)\n }\n\n return base64 ? Buffer.from(content, 'utf-8').toString('base64') : content\n}\n\nfunction expandFileRefValue(rawInner: string, baseDir: string): string | null {\n const match = rawInner.match(FILE_REF_PREFIX)\n if (!match) {\n return null\n }\n\n const [, base64Flag, rawPath] = match\n const filePath = decodeFilePath(rawPath)\n const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag))\n return JSON.stringify(value)\n}\n\nfunction expandQuotedFileRefs(argsJson: string, baseDir: string): string {\n return argsJson.replace(JSON_STRING_LITERAL, (jsonStringLiteral) => {\n const rawInner = jsonStringLiteral.slice(1, -1)\n const replacement = expandFileRefValue(rawInner, baseDir)\n return replacement ?? jsonStringLiteral\n })\n}\n\nfunction expandUnquotedFileRefs(argsJson: string, baseDir: string): string {\n return argsJson.replace(UNQUOTED_FILE_REF, (match, base64Flag, filePath) => {\n const value = readFileRefContent(filePath, baseDir, Boolean(base64Flag))\n return JSON.stringify(value)\n })\n}\n\n/**\n * 展开 argsJson 中的文件引用占位符:\n * @file:<path> → 读取文件原始文本内容\n * @base64file:<path> → 读取文件内容并 Base64 编码\n */\nexport function expandFileRefs(argsJson: string, baseDir: string = process.cwd()): string {\n const withQuotedExpanded = expandQuotedFileRefs(argsJson, baseDir)\n return expandUnquotedFileRefs(withQuotedExpanded, baseDir)\n}\n\n/**\n * 确定 `@file` / `@base64file` 相对路径的解析基准目录。\n *\n * 未使用 `-f/--file` 时以 `process.cwd()` 为基准;\n * 使用参数文件时以该文件所在目录为基准,使 `args.json` 内 `./article.md` 等路径\n * 相对于参数文件而非 shell 当前工作目录解析。\n *\n * @example\n * // cwd 为 /home/user/project,执行 webmcp run tool -f test/fixtures/nested/args.json\n * getFileBaseDir('test/fixtures/nested/args.json')\n * // => '/home/user/project/test/fixtures/nested'\n */\nexport function getFileBaseDir(fileOption?: string): string {\n if (!fileOption) {\n return process.cwd()\n }\n return dirname(resolve(process.cwd(), fileOption))\n}\n","import JSON5 from 'json5'\nimport { expandFileRefs } from './expand-file-refs'\n\nexport function 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 looksLikeObjectBody(raw: string): boolean {\n return /^(\"?[\\w-]+\"?\\s*:)/.test(raw)\n}\n\n/**\n * 拼接 shell 拆散的参数。\n * 兼容 bash 花括号展开后丢失 `{}` 的情况:title:\"...\", \"content\":\"...\"\n */\nexport function joinShellArgs(args: string[]): string {\n const cleaned = args.map(cleanJsonString)\n const first = cleaned[0] ?? ''\n\n if (first.startsWith('{')) {\n return cleaned.join(' ')\n }\n\n if (looksLikeObjectBody(first)) {\n let joined = cleaned.join(' ')\n if (!joined.startsWith('{')) {\n joined = `{${joined}}`\n }\n if (!joined.endsWith('}')) {\n joined = `${joined}}`\n }\n return joined\n }\n\n return cleaned.join(' ')\n}\n\n/** 在相邻属性之间补逗号:title:\"a\" \"content\":\"b\" → title:\"a\",\"content\":\"b\" */\nexport function insertMissingCommas(raw: string): string {\n return raw.replace(/\"(\\s+)(\"?[\\w-]+\"?\\s*:)/g, '\",$2')\n}\n\n/**\n * 为未加引号的字符串值补引号。\n * 典型 shell 损坏:{title:TinyVue Icon,content:@base64file:./a.md}\n */\nexport function quoteUnquotedStringValues(raw: string): string {\n return raw.replace(\n /([{,]\\s*(?:\"([^\"]+)\"|([\\w-]+))\\s*:\\s*)(?!\"@(?:base64)?file:|\"|true|false|null|-?\\d|\\{|\\[)([^\",}\\]]+?)(?=\\s*[,}])/g,\n (_match, prefix, _quotedKey, _unquotedKey, value: string) => {\n const trimmed = value.trim()\n if (/^-?\\d+(\\.\\d+)?([eE][+-]?\\d+)?$/.test(trimmed)) {\n return prefix + trimmed\n }\n return prefix + JSON.stringify(trimmed)\n }\n )\n}\n\nexport function normalizeShellDamagedJson(raw: string): string {\n let normalized = raw.trim()\n normalized = insertMissingCommas(normalized)\n normalized = quoteUnquotedStringValues(normalized)\n return normalized\n}\n\nexport function buildArgsFromKeyValuePairs(args: string[]): string {\n const obj: Record<string, unknown> = {}\n for (const pair of args) {\n const index = pair.indexOf('=')\n if (index === -1) {\n throw new Error(`无效的参数格式 \"${pair}\"。请使用 key=value,或 JSON / -f 文件。`)\n }\n const key = pair.substring(0, index).trim()\n let val = cleanJsonString(pair.substring(index + 1).trim())\n\n try {\n obj[key] = JSON.parse(val)\n } catch {\n obj[key] = val\n }\n }\n return JSON.stringify(obj)\n}\n\n/**\n * 将用户输入的 JSON / JS 对象字面量解析并规范化为标准 JSON 字符串。\n *\n * 解析顺序:\n * 1. 去除首尾空白及 shell 单引号包裹\n * 2. 使用 JSON5 解析(支持尾随逗号、未加引号的 key 等)\n * 3. 若失败,尝试修复 shell 损坏(缺逗号、未加引号的字符串值)后重试\n *\n * @param raw - 原始参数字符串(可能已被 expandFileRefs 展开 @file 占位符)\n * @returns 规范化后的 JSON 字符串,可直接传给 MCP 工具\n *\n * @example\n * // 标准 JSON\n * parseArgsJson('{\"title\":\"标题\",\"content\":\"正文\"}')\n * // => '{\"title\":\"标题\",\"content\":\"正文\"}'\n *\n * @example\n * // shell 损坏:花括号展开后丢失逗号、值未加引号\n * parseArgsJson('{title:TinyVue Icon,content:\"hello\"}')\n * // => '{\"title\":\"TinyVue Icon\",\"content\":\"hello\"}'\n *\n * @example\n * // bash 单引号包裹(外层引号会被 cleanJsonString 剥除)\n * parseArgsJson('\\'{\"title\":\"标题\"}\\'')\n * // => '{\"title\":\"标题\"}'\n */\nexport function parseArgsJson(raw: string): string {\n const trimmed = cleanJsonString(raw)\n try {\n return JSON.stringify(JSON5.parse(trimmed))\n } catch (firstError) {\n const repaired = normalizeShellDamagedJson(trimmed)\n try {\n return JSON.stringify(JSON5.parse(repaired))\n } catch {\n const hint =\n '提示:bash 中 JSON 必须用单引号包裹,例如 \\'{\"title\":\"标题\",\"content\":\"@base64file:./article.md\"}\\';' +\n '或使用 key=value:title=标题 content=@base64file:./article.md;' +\n '或使用 -f args.json。'\n const message = firstError instanceof Error ? firstError.message : String(firstError)\n throw new Error(`参数不是有效的 JSON 或 JS 对象: ${message}\\n${hint}`)\n }\n }\n}\n\n/**\n * `webmcp run` 命令的入口:从 CLI 参数或文件内容组装最终 JSON 参数字符串,兼容三种情况。\n *\n * 参数来源优先级:`-f/--file` 文件内容 > 命令行 `args`。\n * 命令行支持三种格式(自动识别):\n * - JSON 对象:`{\"title\":\"标题\"}`\n * - shell 拆散的 JSON 片段:`title:\"标题\" \"content\":\"正文\"`(bash 花括号展开后)\n * - key=value:`title=标题 content=@base64file:./article.md`\n *\n * 组装后会调用 expandFileRefs 展开 `@file` / `@base64file` 占位符,再经 parseArgsJson 规范化。\n *\n * @param args - commander 解析后的剩余参数数组\n * @param fileContent - `-f` 指定文件的文本内容;未指定时为 undefined\n * @param baseDir - 解析 `@file` 相对路径的基准目录(`-f` 时为文件所在目录,否则为 cwd)\n * @returns 规范化后的 JSON 字符串\n *\n * @example\n * // CLI: webmcp run publish-article '{\"title\":\"标题\",\"content\":\"@base64file:./article.md\"}'\n * prepareRunArgsJson(['{\"title\":\"标题\",\"content\":\"@base64file:./article.md\"}'], undefined, process.cwd())\n *\n * @example\n * // CLI: webmcp run publish-article title=标题 content=@base64file:./article.md\n * prepareRunArgsJson(['title=标题', 'content=@base64file:./article.md'], undefined, process.cwd())\n *\n * @example\n * // CLI: webmcp run publish-article -f args.json\n * prepareRunArgsJson([], readFileSync('args.json', 'utf-8'), dirname('args.json'))\n */\nexport function prepareRunArgsJson(args: string[], fileContent: string | undefined, baseDir: string): string {\n let raw = ''\n\n if (fileContent !== undefined) {\n raw = fileContent.trim()\n } else if (args.length > 0) {\n const joined = joinShellArgs(args)\n if (joined.startsWith('{') || looksLikeObjectBody(joined)) {\n raw = joined\n } else if (args.every((arg) => arg.includes('='))) {\n raw = buildArgsFromKeyValuePairs(args)\n } else {\n raw = joined\n }\n }\n\n if (!raw) {\n throw new Error('必须提供参数或通过 -f/--file 指定参数文件')\n }\n\n const expanded = expandFileRefs(raw, baseDir)\n return parseArgsJson(expanded)\n}\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 * 找出第一个真正可达的 CDP 地址,避免 Windows 上 localhost 解析为 IPv6 导致 puppeteer 连接失败\n */\nasync function findAvailableCdpUrl(addresses: string[], retries = 2): Promise<string | null> {\n for (const addr of addresses) {\n if (await checkCdpReady(addr, retries)) {\n return addr.replace('/json/version', '')\n }\n }\n return null\n}\n\n/**\n * 通过 Puppeteer 连接已运行的浏览器。\n * 注意:不要在 connect 时使用 targetFilter——在 Windows Edge 上会导致 WebSocket 握手后永久挂起;\n * 页面 target 的筛选在 getPageTargets / getTargetPage 中按需完成。\n */\nasync function connectPuppeteer(browserBaseUrl: string, attempt = 1, maxAttempts = 3): Promise<Browser> {\n try {\n return await promiseWithTimeout(\n puppeteer.connect({ browserURL: browserBaseUrl, defaultViewport: null }),\n 10000,\n `puppeteer.connect to ${browserBaseUrl} timed out`\n )\n } catch (err) {\n if (attempt >= maxAttempts) {\n throw err\n }\n const delayMs = 500 * attempt\n console.log(pc.yellow(`connectBrowser: 第 ${attempt} 次连接失败,${delayMs}ms 后重试...`))\n await new Promise(resolve => setTimeout(resolve, delayMs))\n return connectPuppeteer(browserBaseUrl, attempt + 1, maxAttempts)\n }\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 launchArgs = [\n `--remote-debugging-port=${CDP_PORT}`,\n `--user-data-dir=${userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check'\n ]\n // Windows 上显式绑定 IPv4,避免 localhost 解析到 IPv6 导致 CDP 连接不稳定\n if (os.platform() === 'win32') {\n launchArgs.push('--remote-debugging-address=127.0.0.1')\n }\n\n const child = spawn(\n browserInfo.path,\n launchArgs,\n {\n detached: true,\n stdio: 'ignore'\n }\n )\n\n child.unref() // 让子进程脱离父进程独立运行\n\n // 轮询等待 CDP 端口就绪(优先 127.0.0.1,避免 Windows IPv6 解析问题)\n const pollUrls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`]\n for (let i = 0; i < 30; i++) {\n for (const url of pollUrls) {\n try {\n await httpGet(url, 1000)\n console.log(pc.green(`${browserInfo.name} 启动并就绪。`))\n // 额外等待 500ms,确保 CDP 完全稳定(Mac 首次启动时端口通但连接不稳定)\n await new Promise(resolve => setTimeout(resolve, 500))\n return\n } catch {}\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 // 第一步:找到第一个真正可达的 CDP 地址(优先 127.0.0.1,规避 Windows localhost → IPv6 问题)\n const versionAddresses = [\n `http://127.0.0.1:${CDP_PORT}/json/version`,\n `http://localhost:${CDP_PORT}/json/version`\n ]\n\n const existingUrl = await findAvailableCdpUrl(versionAddresses, 2)\n if (existingUrl) {\n console.log(pc.yellow(`connectBrowser: 检测到端口 ${CDP_PORT} 已就绪,正在连接 ${existingUrl}...`))\n try {\n const browser = await connectPuppeteer(existingUrl)\n console.log(pc.green(`connectBrowser: 成功连接 ${existingUrl}`))\n return browser\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n throw new Error(\n `无法连接到已在运行的浏览器(${existingUrl}):${msg}。` +\n 'CDP 端口可用,请勿重复启动浏览器;可关闭多余窗口后重试。'\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 const launchedUrl = await findAvailableCdpUrl(versionAddresses, 5)\n if (!launchedUrl) {\n throw new Error(`无法连接到浏览器(端口 ${CDP_PORT}),请检查 Chrome/Edge 是否已安装。`)\n }\n\n try {\n console.log(pc.yellow(`connectBrowser: 浏览器已启动,正在连接 ${launchedUrl}...`))\n const browser = await connectPuppeteer(launchedUrl)\n console.log(pc.green(`connectBrowser: 成功连接 ${launchedUrl}`))\n return browser\n } catch {\n throw new Error(`无法连接到浏览器(端口 ${CDP_PORT}),请检查 Chrome/Edge 是否已安装。`)\n }\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 // 显式等待 WebMCP 注册接口完全就绪\n await page.waitForFunction(() => {\n const mcp = (navigator as any).modelContext\n return mcp && typeof mcp.registerTool === 'function'\n }, { timeout: 10000 }).catch(() => {\n console.warn('等待 WebMCP registerTool 接口就绪超时,正在继续注入...')\n })\n }\n\n // 无论 polyfill 是否刚注入,都检查域名工具(工具内部有防重复 flag)\n await injectDomainTools(page)\n}\n\nfunction getToolsBundleName(hostname: string): string | null {\n const normalized = hostname.split(':')[0].toLowerCase()\n const toolsDir = path.resolve(__dirname, 'webmcp-tools')\n if (!fs.existsSync(toolsDir)) {\n return null\n }\n let files: string[] = []\n try {\n files = fs.readdirSync(toolsDir)\n } catch {\n return null\n }\n const supportedDomains = files\n .filter(f => f.endsWith('.js'))\n .map(f => f.slice(0, -3))\n\n if (supportedDomains.includes(normalized)) {\n return `webmcp-tools/${normalized}.js`\n }\n for (const domain of supportedDomains) {\n if (normalized.endsWith('.' + domain)) {\n return `webmcp-tools/${domain}.js`\n }\n }\n return null\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 bundleName = getToolsBundleName(hostname)\n if (!bundleName) {\n return // 没有对应的工具预置,跳过\n }\n\n const toolBundlePath = path.resolve(__dirname, bundleName)\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 // 不获取 DOM content(由调用方按需通过 browserState 拉取)\n const state = await page.evaluate(async () => {\n const url = document.URL\n const title = document.title\n\n const mcp = (navigator as any).modelContextTesting || (navigator as any).modelContext\n let webmcpTools: any[] = []\n\n if (mcp && typeof mcp.listTools === 'function') {\n const toolsResult = await mcp.listTools()\n webmcpTools = toolsResult?.tools || toolsResult || []\n }\n\n return { url, title, webmcpTools }\n })\n\n // 3. 获取所有标签页信息(排除 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 // argsJson 已在 bin.ts 中完成 @base64file 展开与 JSON 校验\n const cleanedArgs = argsJson.trim()\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 {\n // 保留原始字符串\n }\n }\n\n if (res === undefined || res === null) {\n throw new Error('工具 execute 未返回结果,请检查工具实现是否缺少 return')\n }\n\n if (typeof res === 'object' && (res as { success?: boolean }).success === false) {\n const failed = res as { error?: string; message?: string }\n throw new Error(failed.error || failed.message || '工具执行失败')\n }\n\n return res\n }, toolName, cleanedArgs)\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.3\",\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":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,aAAe;AACf,IAAAC,eAAiB;AACjB,IAAAC,aAAe;AACf,uBAAwB;AACxB,IAAAC,qBAAe;AACf,IAAAH,aAA6B;AAC7B,IAAAC,eAAwB;;;ACPxB,gBAA6B;AAC7B,kBAAwD;AACxD,iBAA8B;AAG9B,IAAM,sBAAsB;AAG5B,IAAM,oBAAoB;AAE1B,IAAM,kBAAkB;AAEjB,SAAS,gBAAgB,UAAkB,UAAkB,QAAQ,IAAI,GAAW;AACzF,MAAI,iBAAiB,SAAS,KAAK;AAEnC,MACG,eAAe,WAAW,GAAG,KAAK,eAAe,SAAS,GAAG,KAC7D,eAAe,WAAW,GAAG,KAAK,eAAe,SAAS,GAAG,GAC9D;AACA,qBAAiB,eAAe,MAAM,GAAG,EAAE,EAAE,KAAK;AAAA,EACpD;AAEA,MAAI,eAAe,WAAW,SAAS,GAAG;AACxC,yBAAiB,0BAAc,cAAc;AAAA,EAC/C;AAEA,UAAI,wBAAW,cAAc,GAAG;AAC9B,eAAO,uBAAU,cAAc;AAAA,EACjC;AAEA,aAAO,qBAAQ,SAAS,cAAc;AACxC;AAKO,SAAS,eAAe,SAAyB;AACtD,SAAO,QAAQ,QAAQ,SAAS,IAAI;AACtC;AAEA,SAAS,mBAAmB,UAAkB,SAAiB,QAAyB;AACtF,QAAM,UAAU,gBAAgB,UAAU,OAAO;AACjD,MAAI;AACJ,MAAI;AACF,kBAAU,wBAAa,SAAS,OAAO;AAAA,EACzC,SAAS,GAAQ;AACf,UAAM,IAAI,MAAM,qDAAa,QAAQ,MAAM,OAAO,MAAM,EAAE,OAAO,EAAE;AAAA,EACrE;AAEA,SAAO,SAAS,OAAO,KAAK,SAAS,OAAO,EAAE,SAAS,QAAQ,IAAI;AACrE;AAEA,SAAS,mBAAmB,UAAkB,SAAgC;AAC5E,QAAM,QAAQ,SAAS,MAAM,eAAe;AAC5C,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,QAAM,CAAC,EAAE,YAAY,OAAO,IAAI;AAChC,QAAM,WAAW,eAAe,OAAO;AACvC,QAAM,QAAQ,mBAAmB,UAAU,SAAS,QAAQ,UAAU,CAAC;AACvE,SAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,qBAAqB,UAAkB,SAAyB;AACvE,SAAO,SAAS,QAAQ,qBAAqB,CAAC,sBAAsB;AAClE,UAAM,WAAW,kBAAkB,MAAM,GAAG,EAAE;AAC9C,UAAM,cAAc,mBAAmB,UAAU,OAAO;AACxD,WAAO,eAAe;AAAA,EACxB,CAAC;AACH;AAEA,SAAS,uBAAuB,UAAkB,SAAyB;AACzE,SAAO,SAAS,QAAQ,mBAAmB,CAAC,OAAO,YAAY,aAAa;AAC1E,UAAM,QAAQ,mBAAmB,UAAU,SAAS,QAAQ,UAAU,CAAC;AACvE,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,CAAC;AACH;AAOO,SAAS,eAAe,UAAkB,UAAkB,QAAQ,IAAI,GAAW;AACxF,QAAM,qBAAqB,qBAAqB,UAAU,OAAO;AACjE,SAAO,uBAAuB,oBAAoB,OAAO;AAC3D;AAcO,SAAS,eAAe,YAA6B;AAC1D,MAAI,CAAC,YAAY;AACf,WAAO,QAAQ,IAAI;AAAA,EACrB;AACA,aAAO,yBAAQ,qBAAQ,QAAQ,IAAI,GAAG,UAAU,CAAC;AACnD;;;AC1GA,mBAAkB;AAGX,SAAS,gBAAgB,KAAqB;AACnD,MAAI,UAAU,IAAI,KAAK;AACvB,MAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,GAAG;AACpD,cAAU,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAK;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,KAAsB;AACjD,SAAO,oBAAoB,KAAK,GAAG;AACrC;AAMO,SAAS,cAAc,MAAwB;AACpD,QAAM,UAAU,KAAK,IAAI,eAAe;AACxC,QAAM,QAAQ,QAAQ,CAAC,KAAK;AAE5B,MAAI,MAAM,WAAW,GAAG,GAAG;AACzB,WAAO,QAAQ,KAAK,GAAG;AAAA,EACzB;AAEA,MAAI,oBAAoB,KAAK,GAAG;AAC9B,QAAI,SAAS,QAAQ,KAAK,GAAG;AAC7B,QAAI,CAAC,OAAO,WAAW,GAAG,GAAG;AAC3B,eAAS,IAAI,MAAM;AAAA,IACrB;AACA,QAAI,CAAC,OAAO,SAAS,GAAG,GAAG;AACzB,eAAS,GAAG,MAAM;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;AAGO,SAAS,oBAAoB,KAAqB;AACvD,SAAO,IAAI,QAAQ,2BAA2B,MAAM;AACtD;AAMO,SAAS,0BAA0B,KAAqB;AAC7D,SAAO,IAAI;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,QAAQ,YAAY,cAAc,UAAkB;AAC3D,YAAM,UAAU,MAAM,KAAK;AAC3B,UAAI,iCAAiC,KAAK,OAAO,GAAG;AAClD,eAAO,SAAS;AAAA,MAClB;AACA,aAAO,SAAS,KAAK,UAAU,OAAO;AAAA,IACxC;AAAA,EACF;AACF;AAEO,SAAS,0BAA0B,KAAqB;AAC7D,MAAI,aAAa,IAAI,KAAK;AAC1B,eAAa,oBAAoB,UAAU;AAC3C,eAAa,0BAA0B,UAAU;AACjD,SAAO;AACT;AAEO,SAAS,2BAA2B,MAAwB;AACjE,QAAM,MAA+B,CAAC;AACtC,aAAW,QAAQ,MAAM;AACvB,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,UAAU,IAAI;AAChB,YAAM,IAAI,MAAM,+CAAY,IAAI,8EAAiC;AAAA,IACnE;AACA,UAAM,MAAM,KAAK,UAAU,GAAG,KAAK,EAAE,KAAK;AAC1C,QAAI,MAAM,gBAAgB,KAAK,UAAU,QAAQ,CAAC,EAAE,KAAK,CAAC;AAE1D,QAAI;AACF,UAAI,GAAG,IAAI,KAAK,MAAM,GAAG;AAAA,IAC3B,QAAQ;AACN,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO,KAAK,UAAU,GAAG;AAC3B;AA4BO,SAAS,cAAc,KAAqB;AACjD,QAAM,UAAU,gBAAgB,GAAG;AACnC,MAAI;AACF,WAAO,KAAK,UAAU,aAAAG,QAAM,MAAM,OAAO,CAAC;AAAA,EAC5C,SAAS,YAAY;AACnB,UAAM,WAAW,0BAA0B,OAAO;AAClD,QAAI;AACF,aAAO,KAAK,UAAU,aAAAA,QAAM,MAAM,QAAQ,CAAC;AAAA,IAC7C,QAAQ;AACN,YAAM,OACJ;AAGF,YAAM,UAAU,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU;AACpF,YAAM,IAAI,MAAM,2EAAyB,OAAO;AAAA,EAAK,IAAI,EAAE;AAAA,IAC7D;AAAA,EACF;AACF;AA8BO,SAAS,mBAAmB,MAAgB,aAAiC,SAAyB;AAC3G,MAAI,MAAM;AAEV,MAAI,gBAAgB,QAAW;AAC7B,UAAM,YAAY,KAAK;AAAA,EACzB,WAAW,KAAK,SAAS,GAAG;AAC1B,UAAM,SAAS,cAAc,IAAI;AACjC,QAAI,OAAO,WAAW,GAAG,KAAK,oBAAoB,MAAM,GAAG;AACzD,YAAM;AAAA,IACR,WAAW,KAAK,MAAM,CAAC,QAAQ,IAAI,SAAS,GAAG,CAAC,GAAG;AACjD,YAAM,2BAA2B,IAAI;AAAA,IACvC,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AAEA,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,uGAA4B;AAAA,EAC9C;AAEA,QAAM,WAAW,eAAe,KAAK,OAAO;AAC5C,SAAO,cAAc,QAAQ;AAC/B;;;ACxLA,4BAAyC;AACzC,wBAAe;AACf,IAAAC,aAAe;AACf,gBAAe;AACf,IAAAC,eAAiB;AACjB,kBAAiB;AACjB,2BAAsB;AACtB,IAAAC,cAA8B;AAP9B;AASA,IAAM,iBAAa,2BAAc,YAAY,GAAG;AAChD,IAAM,YAAY,aAAAC,QAAK,QAAQ,UAAU;AAEzC,IAAM,WAAW;AAEjB,IAAM,UAAU,oBAAoB,QAAQ;AAM5C,SAAS,QAAQ,KAAa,YAAY,MAAuB;AAC/D,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAM,MAAM,YAAAC,QAAK,IAAI,KAAK,EAAE,SAAS,UAAU,GAAG,CAAC,QAAQ;AACzD,UAAI,OAAO;AACX,UAAI,GAAG,QAAQ,CAAC,UAAkB;AAAE,gBAAQ,MAAM,SAAS;AAAA,MAAE,CAAC;AAC9D,UAAI,GAAG,OAAO,MAAM;AAClB,YAAI,IAAI,cAAc,IAAI,cAAc,OAAO,IAAI,aAAa,KAAK;AACnE,UAAAD,SAAQ,IAAI;AAAA,QACd,OAAO;AACL,iBAAO,IAAI,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;AAAA,QAC5C;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,QAAI,GAAG,SAAS,MAAM;AACtB,QAAI,GAAG,WAAW,MAAM;AAAE,UAAI,QAAQ;AAAG,aAAO,IAAI,MAAM,iBAAiB,CAAC;AAAA,IAAE,CAAC;AAAA,EACjF,CAAC;AACH;AAEA,SAAS,mBAAsB,SAAqB,YAAY,KAAM,WAAW,uBAAmC;AAClH,MAAI;AACJ,QAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD,gBAAY,WAAW,MAAM;AAC3B,aAAO,IAAI,MAAM,QAAQ,CAAC;AAAA,IAC5B,GAAG,SAAS;AAAA,EACd,CAAC;AACD,SAAO,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC,EAAE,QAAQ,MAAM;AAC3D,iBAAa,SAAS;AAAA,EACxB,CAAC;AACH;AAEA,eAAe,cAAc,KAAa,UAAU,GAAqB;AACvE,WAAS,IAAI,GAAG,IAAI,SAAS,KAAK;AAChC,QAAI;AACF,YAAM,QAAQ,KAAK,IAAI;AACvB,aAAO;AAAA,IACT,QAAQ;AAAA,IAAC;AACT,QAAI,IAAI,UAAU,GAAG;AACnB,YAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,GAAG,CAAC;AAAA,IAC3C;AAAA,EACF;AACA,SAAO;AACT;AAKA,eAAe,oBAAoB,WAAqB,UAAU,GAA2B;AAC3F,aAAW,QAAQ,WAAW;AAC5B,QAAI,MAAM,cAAc,MAAM,OAAO,GAAG;AACtC,aAAO,KAAK,QAAQ,iBAAiB,EAAE;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AAOA,eAAe,iBAAiB,gBAAwB,UAAU,GAAG,cAAc,GAAqB;AACtG,MAAI;AACF,WAAO,MAAM;AAAA,MACX,sBAAAE,QAAU,QAAQ,EAAE,YAAY,gBAAgB,iBAAiB,KAAK,CAAC;AAAA,MACvE;AAAA,MACA,wBAAwB,cAAc;AAAA,IACxC;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,WAAW,aAAa;AAC1B,YAAM;AAAA,IACR;AACA,UAAM,UAAU,MAAM;AACtB,YAAQ,IAAI,kBAAAC,QAAG,OAAO,0BAAqB,OAAO,wCAAU,OAAO,0BAAW,CAAC;AAC/E,UAAM,IAAI,QAAQ,CAAAH,aAAW,WAAWA,UAAS,OAAO,CAAC;AACzD,WAAO,iBAAiB,gBAAgB,UAAU,GAAG,WAAW;AAAA,EAClE;AACF;AASA,SAAS,wBAA4C;AACnD,QAAM,WAAW,UAAAI,QAAG,SAAS;AAC7B,MAAI,aAAa,UAAU;AAEzB,UAAM,aAAa;AACnB,QAAI,WAAAC,QAAG,WAAW,UAAU,EAAG,QAAO,EAAE,MAAM,YAAY,MAAM,SAAS;AACzE,UAAM,WAAW;AACjB,QAAI,WAAAA,QAAG,WAAW,QAAQ,EAAG,QAAO,EAAE,MAAM,UAAU,MAAM,OAAO;AACnE,WAAO;AAAA,EACT,WAAW,aAAa,SAAS;AAE/B,UAAM,cAAc;AAAA,MAClB,QAAQ,IAAI,eAAe;AAAA,MAC3B,QAAQ,IAAI,eAAe;AAAA,MAC3B,QAAQ,IAAI,mBAAmB,IAAI;AAAA,IACrC;AACA,UAAM,cAAc,YAAY,KAAK,OAAK,WAAAA,QAAG,WAAW,CAAC,CAAC;AAC1D,QAAI,YAAa,QAAO,EAAE,MAAM,aAAa,MAAM,SAAS;AAG5D,UAAM,YAAY;AAAA,MAChB,QAAQ,IAAI,eAAe;AAAA,MAC3B,QAAQ,IAAI,eAAe;AAAA,MAC3B,QAAQ,IAAI,mBAAmB,IAAI;AAAA,MACnC;AAAA,MACA;AAAA,IACF;AACA,UAAM,YAAY,UAAU,KAAK,OAAK,WAAAA,QAAG,WAAW,CAAC,CAAC;AACtD,QAAI,UAAW,QAAO,EAAE,MAAM,WAAW,MAAM,OAAO;AACtD,WAAO;AAAA,EACT,OAAO;AAEL,UAAM,QAA4C;AAAA,MAChD,EAAE,GAAG,0BAA0B,MAAM,SAAS;AAAA,MAC9C,EAAE,GAAG,iCAAiC,MAAM,SAAS;AAAA,MACrD,EAAE,GAAG,qBAAqB,MAAM,WAAW;AAAA,MAC3C,EAAE,GAAG,6BAA6B,MAAM,WAAW;AAAA,MACnD,EAAE,GAAG,2BAA2B,MAAM,OAAO;AAAA,MAC7C,EAAE,GAAG,kCAAkC,MAAM,OAAO;AAAA,IACtD;AACA,UAAM,QAAQ,MAAM,KAAK,CAAC,EAAE,EAAE,MAAM,WAAAA,QAAG,WAAW,CAAC,CAAC;AACpD,WAAO,QAAQ,EAAE,MAAM,MAAM,GAAG,MAAM,MAAM,KAAK,IAAI;AAAA,EACvD;AACF;AAEA,eAAe,2BAA0C;AACvD,QAAM,cAAc,sBAAsB;AAC1C,MAAI,CAAC,eAAe,CAAC,WAAAA,QAAG,WAAW,YAAY,IAAI,GAAG;AACpD,UAAM,IAAI,MAAM,wIAAoC;AAAA,EACtD;AAEA,UAAQ,IAAI,kBAAAF,QAAG,OAAO,wCAAU,YAAY,IAAI,gCAAY,QAAQ,MAAM,CAAC;AAG3E,QAAM,cAAc,QAAQ,IAAI,oBAAoB,aAAAJ,QAAK,KAAK,UAAAK,QAAG,QAAQ,GAAG,wBAAwB;AAEpG,QAAM,aAAa;AAAA,IACjB,2BAA2B,QAAQ;AAAA,IACnC,mBAAmB,WAAW;AAAA,IAC9B;AAAA,IACA;AAAA,EACF;AAEA,MAAI,UAAAA,QAAG,SAAS,MAAM,SAAS;AAC7B,eAAW,KAAK,sCAAsC;AAAA,EACxD;AAEA,QAAM,YAAQ;AAAA,IACZ,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,MACE,UAAU;AAAA,MACV,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,MAAM;AAGZ,QAAM,WAAW,CAAC,oBAAoB,QAAQ,iBAAiB,oBAAoB,QAAQ,eAAe;AAC1G,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,eAAW,OAAO,UAAU;AAC1B,UAAI;AACF,cAAM,QAAQ,KAAK,GAAI;AACvB,gBAAQ,IAAI,kBAAAD,QAAG,MAAM,GAAG,YAAY,IAAI,uCAAS,CAAC;AAElD,cAAM,IAAI,QAAQ,CAAAH,aAAW,WAAWA,UAAS,GAAG,CAAC;AACrD;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,UAAM,IAAI,QAAQ,CAAAA,aAAW,WAAWA,UAAS,GAAG,CAAC;AAAA,EACvD;AAEA,QAAM,IAAI,MAAM,GAAG,YAAY,IAAI,sFAAqB;AAC1D;AAEA,eAAsB,iBAAmC;AAEvD,QAAM,mBAAmB;AAAA,IACvB,oBAAoB,QAAQ;AAAA,IAC5B,oBAAoB,QAAQ;AAAA,EAC9B;AAEA,QAAM,cAAc,MAAM,oBAAoB,kBAAkB,CAAC;AACjE,MAAI,aAAa;AACf,YAAQ,IAAI,kBAAAG,QAAG,OAAO,kDAAyB,QAAQ,qDAAa,WAAW,KAAK,CAAC;AACrF,QAAI;AACF,YAAM,UAAU,MAAM,iBAAiB,WAAW;AAClD,cAAQ,IAAI,kBAAAA,QAAG,MAAM,4CAAwB,WAAW,EAAE,CAAC;AAC3D,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAM,IAAI;AAAA,QACR,uFAAiB,WAAW,eAAK,GAAG;AAAA,MAEtC;AAAA,IACF;AAAA,EACF;AAGA,UAAQ,IAAI,kBAAAA,QAAG,OAAO,gCAAsB,QAAQ,gGAAqB,CAAC;AAC1E,MAAI;AACF,UAAM,yBAAyB;AAAA,EACjC,SAAS,aAAsB;AAC7B,UAAM,MAAM,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW;AACnF,YAAQ,MAAM,kBAAAA,QAAG,IAAI,+CAAY,GAAG,EAAE,CAAC;AACvC,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAGA,QAAM,cAAc,MAAM,oBAAoB,kBAAkB,CAAC;AACjE,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,sEAAe,QAAQ,iFAA0B;AAAA,EACnE;AAEA,MAAI;AACF,YAAQ,IAAI,kBAAAA,QAAG,OAAO,sFAA+B,WAAW,KAAK,CAAC;AACtE,UAAM,UAAU,MAAM,iBAAiB,WAAW;AAClD,YAAQ,IAAI,kBAAAA,QAAG,MAAM,4CAAwB,WAAW,EAAE,CAAC;AAC3D,WAAO;AAAA,EACT,QAAQ;AACN,UAAM,IAAI,MAAM,sEAAe,QAAQ,iFAA0B;AAAA,EACnE;AACF;AAOA,eAAsB,gBAAgB,MAA6B;AACjE,QAAM,UAAU,MAAM,KAAK,OAAO,EAAE,iBAAiB;AACrD,MAAI;AACF,UAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,KAAK,sBAAsB;AAChE,WAAO,WAAW;AAAA,EACpB,UAAE;AACA,UAAM,QAAQ,OAAO,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACvC;AACF;AAEO,SAAS,eAAe,SAAkB;AAC/C,SAAO,QAAQ,QAAQ,EAAE,OAAO,OAAK;AACnC,QAAI;AACF,YAAM,QAAQ,OAAO,EAAE,SAAS,aAAa,EAAE,KAAK,IAAK,EAAU,SAAS;AAC5E,YAAM,OAAO,OAAO,EAAE,QAAQ,aAAa,EAAE,IAAI,IAAK,EAAU,QAAQ;AACxE,aAAO,SAAS,UAAU,CAAC,IAAI,WAAW,aAAa;AAAA,IACzD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEO,SAAS,sBAAsB,QAAqB;AACzD,SAAO,OAAO,OAAO,mBAAmB,aACpC,OAAO,eAAe,EAAE,WACvB,OAAO,aAAa,OAAO,YAAY;AAC9C;AAEO,SAAS,sBAAsB,SAAkB,OAAe;AACrE,QAAM,cAAc,eAAe,OAAO;AAC1C,SAAO,YAAY,KAAK,OAAK;AAC3B,UAAM,MAAM,sBAAsB,CAAC;AACnC,WAAO,QAAQ,SAAS,IAAI,SAAS,KAAK;AAAA,EAC5C,CAAC,KAAK;AACR;AAEA,eAAsB,gBAAgB,SAAkB,OAA8B;AACpF,QAAM,SAAS,sBAAsB,SAAS,KAAK;AACnD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,sBAAsB,KAAK,cAAc;AAAA,EAC3D;AAEA,QAAM,YAAY,sBAAsB,MAAM;AAC9C,QAAM,QAAQ,MAAM,QAAQ,MAAM;AAClC,MAAI,cAAc,MAAM,KAAK,OAAK,CAAC,EAAE,IAAI,EAAE,WAAW,aAAa,CAAC;AACpE,MAAI,CAAC,eAAe,MAAM,SAAS,GAAG;AACpC,kBAAc,MAAM,CAAC;AAAA,EACvB;AACA,MAAI,CAAC,aAAa;AAChB,kBAAc,MAAM,QAAQ,QAAQ;AAAA,EACtC;AAEA,QAAM,UAAU,MAAM,YAAY,iBAAiB;AACnD,MAAI;AACF,UAAM,QAAQ,KAAK,yBAAyB,EAAE,UAAU,UAAU,CAAC;AAAA,EACrE,UAAE;AACA,UAAM,QAAQ,OAAO,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACvC;AACF;AAEA,eAAsB,cAAc,SAAkB,OAA+B;AACnF,QAAM,cAAc,eAAe,OAAO;AAE1C,MAAI,YAAY,WAAW,GAAG;AAC5B,UAAM,UAAU,MAAM,QAAQ,QAAQ;AACtC,UAAM,6BAA6B,OAAO;AAC1C,WAAO;AAAA,EACT;AAEA,MAAI,aAA0B;AAE9B,MAAI,UAAU,QAAW;AACvB,UAAM,SAAS,sBAAsB,SAAS,KAAK;AACnD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,sBAAsB,KAAK,cAAc;AAAA,IAC3D;AACA,iBAAa,MAAM,OAAO,KAAK;AAAA,EACjC,OAAO;AAEL,QAAI;AACF,YAAM,OAAO;AAAA,QACX,oBAAoB,QAAQ;AAAA,QAC5B,oBAAoB,QAAQ;AAAA,MAC9B;AACA,UAAI,iBAAgC;AACpC,iBAAW,OAAO,MAAM;AACtB,YAAI;AACF,gBAAM,OAAO,MAAM,QAAQ,KAAK,GAAI;AACpC,gBAAM,cAAgE,KAAK,MAAM,IAAI;AAErF,gBAAM,SAAS,YAAY,KAAK,OAAK,EAAE,SAAS,UAAU,CAAC,EAAE,IAAI,WAAW,aAAa,CAAC;AAC1F,cAAI,QAAQ;AAAE,6BAAiB,OAAO;AAAI;AAAA,UAAM;AAAA,QAClD,QAAQ;AAAA,QAAoB;AAAA,MAC9B;AAEA,UAAI,gBAAgB;AAClB,mBAAW,UAAU,aAAa;AAChC,cAAI,sBAAsB,MAAM,MAAM,gBAAgB;AACpD,yBAAa,MAAM,OAAO,KAAK;AAC/B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAAuB;AAG/B,QAAI,CAAC,YAAY;AACf,YAAM,aAAa,YAAY,YAAY,SAAS,CAAC;AACrD,mBAAa,MAAM,WAAW,KAAK;AAAA,IACrC;AAAA,EACF;AAEA,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,kDAAU;AAAA,EAC5B;AAGA,QAAM,6BAA6B,UAAU;AAC7C,SAAO;AACT;AAKA,eAAsB,eAAe,MAA2B;AAC9D,QAAM,6BAA6B,MAAM,IAAI;AAC/C;AAEA,eAAe,6BAA6B,MAAY,QAAQ,OAAO;AAErE,QAAM,gBAAgB,CAAC,SAAS,MAAM,KAAK,SAAS,MAAM;AACxD,WAAO,CAAC,CAAE,OAAe;AAAA,EAC3B,CAAC,EAAE,MAAM,MAAM,KAAK;AAEpB,MAAI,CAAC,eAAe;AAClB,YAAQ,IAAI,kBAAAA,QAAG,KAAK,+HAAgC,CAAC;AAErD,UAAM,mBAAmB,aAAAJ,QAAK,QAAQ,WAAW,kBAAkB;AACnE,QAAI,CAAC,WAAAM,QAAG,WAAW,gBAAgB,GAAG;AACpC,YAAM,IAAI,MAAM,mCAAmC,gBAAgB,oDAAoD;AAAA,IACzH;AAEA,UAAM,gBAAgB,WAAAA,QAAG,aAAa,kBAAkB,OAAO;AAG/D,QAAI;AACF,YAAM,KAAK,SAAS,aAAa;AAAA,IACnC,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAM,IAAI,MAAM,mEAAiB,GAAG;AAAA,IACtC;AAGA,UAAM,KAAK,gBAAgB,MAAM;AAC/B,YAAM,MAAO,UAAkB;AAC/B,aAAO,OAAO,OAAO,IAAI,iBAAiB;AAAA,IAC5C,GAAG,EAAE,SAAS,IAAM,CAAC,EAAE,MAAM,MAAM;AACjC,cAAQ,KAAK,oHAAyC;AAAA,IACxD,CAAC;AAAA,EACH;AAGA,QAAM,kBAAkB,IAAI;AAC9B;AAEA,SAAS,mBAAmB,UAAiC;AAC3D,QAAM,aAAa,SAAS,MAAM,GAAG,EAAE,CAAC,EAAE,YAAY;AACtD,QAAM,WAAW,aAAAN,QAAK,QAAQ,WAAW,cAAc;AACvD,MAAI,CAAC,WAAAM,QAAG,WAAW,QAAQ,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,MAAI,QAAkB,CAAC;AACvB,MAAI;AACF,YAAQ,WAAAA,QAAG,YAAY,QAAQ;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,mBAAmB,MACtB,OAAO,OAAK,EAAE,SAAS,KAAK,CAAC,EAC7B,IAAI,OAAK,EAAE,MAAM,GAAG,EAAE,CAAC;AAE1B,MAAI,iBAAiB,SAAS,UAAU,GAAG;AACzC,WAAO,gBAAgB,UAAU;AAAA,EACnC;AACA,aAAW,UAAU,kBAAkB;AACrC,QAAI,WAAW,SAAS,MAAM,MAAM,GAAG;AACrC,aAAO,gBAAgB,MAAM;AAAA,IAC/B;AAAA,EACF;AACA,SAAO;AACT;AAMA,eAAe,kBAAkB,MAA2B;AAC1D,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,KAAK,IAAI,CAAC;AAC9B,eAAW,IAAI;AAAA,EACjB,QAAQ;AACN;AAAA,EACF;AAEA,QAAM,aAAa,mBAAmB,QAAQ;AAC9C,MAAI,CAAC,YAAY;AACf;AAAA,EACF;AAEA,QAAM,iBAAiB,aAAAN,QAAK,QAAQ,WAAW,UAAU;AACzD,MAAI,CAAC,WAAAM,QAAG,WAAW,cAAc,GAAG;AAClC;AAAA,EACF;AAEA,UAAQ,IAAI,kBAAAF,QAAG,KAAK,kCAAS,QAAQ,kEAAgB,CAAC;AAEtD,QAAM,aAAa,WAAAE,QAAG,aAAa,gBAAgB,OAAO;AAC1D,MAAI;AACF,UAAM,KAAK,SAAS,UAAU;AAC9B,YAAQ,IAAI,kBAAAF,QAAG,MAAM,gBAAM,QAAQ,uCAAS,CAAC;AAAA,EAC/C,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAE3D,YAAQ,KAAK,kBAAAA,QAAG,OAAO,qDAAa,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,EAC1D;AACF;;;AC/dA,eAAsB,aAAa,EAAE,MAAM,GAAuB;AAChE,QAAM,UAAU,MAAM,eAAe;AACrC,MAAI;AACF,UAAM,OAAO,MAAM,cAAc,SAAS,KAAK;AAG/C,UAAM,eAAe,IAAI;AAIzB,UAAM,QAAQ,MAAM,KAAK,SAAS,YAAY;AAC5C,YAAM,MAAM,SAAS;AACrB,YAAM,QAAQ,SAAS;AAEvB,YAAM,MAAO,UAAkB,uBAAwB,UAAkB;AACzE,UAAI,cAAqB,CAAC;AAE1B,UAAI,OAAO,OAAO,IAAI,cAAc,YAAY;AAC9C,cAAM,cAAc,MAAM,IAAI,UAAU;AACxC,sBAAc,aAAa,SAAS,eAAe,CAAC;AAAA,MACtD;AAEA,aAAO,EAAE,KAAK,OAAO,YAAY;AAAA,IACnC,CAAC;AAGD,UAAM,QAAQ,MAAM,QAAQ,MAAM;AAClC,UAAMG,QAAO,MAAM,QAAQ;AAAA,MACzB,MAAM,IAAI,OAAO,MAAM;AACrB,cAAM,OAAO,EAAE,IAAI;AACnB,YAAI,KAAK,WAAW,aAAa,EAAG,QAAO;AAE3C,cAAM,SAAS,MAAM,QAAQ,KAAK;AAAA,UAChC,EAAE,MAAM,EAAE,MAAM,MAAM,SAAS;AAAA,UAC/B,IAAI,QAAgB,CAACC,aAAY,WAAW,MAAMA,SAAQ,SAAS,GAAG,GAAG,CAAC;AAAA,QAC5E,CAAC;AAED,eAAO;AAAA;AAAA,UAEL,OAAO,MAAM,gBAAgB,CAAC,EAAE,MAAM,MAAM,IAAI;AAAA,UAChD,OAAO;AAAA,UACP,KAAK;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,MAAMD,MAAK,OAAO,OAAO;AAAA,IAC3B;AAAA,EACF,UAAE;AACA,UAAM,QAAQ,WAAW;AAAA,EAC3B;AACF;;;ACrDA,eAAsB,WAAW;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,UAAU,MAAM,eAAe;AACrC,MAAI;AACF,UAAM,OAAO,MAAM,cAAc,SAAS,KAAK;AAG/C,UAAM,cAAc,SAAS,KAAK;AAElC,UAAM,SAAS,MAAM,KAAK,SAAS,OAAO,MAAM,gBAAgB;AAC9D,YAAM,MAAO,UAAkB,uBAAwB,UAAkB;AAEzE,UAAI,CAAC,OAAO,OAAO,IAAI,gBAAgB,YAAY;AACjD,cAAM,IAAI,MAAM,qIAAoE;AAAA,MACtF;AAGA,UAAI,MAAM,MAAM,IAAI,YAAY,MAAM,WAAW;AAGjD,UAAI,OAAO,QAAQ,UAAU;AAC3B,YAAI;AACF,gBAAM,KAAK,MAAM,GAAG;AAAA,QACtB,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,UAAI,QAAQ,UAAa,QAAQ,MAAM;AACrC,cAAM,IAAI,MAAM,oIAAqC;AAAA,MACvD;AAEA,UAAI,OAAO,QAAQ,YAAa,IAA8B,YAAY,OAAO;AAC/E,cAAM,SAAS;AACf,cAAM,IAAI,MAAM,OAAO,SAAS,OAAO,WAAW,sCAAQ;AAAA,MAC5D;AAEA,aAAO;AAAA,IACT,GAAG,UAAU,WAAW;AAExB,WAAO;AAAA,EACT,UAAE;AACA,UAAM,QAAQ,WAAW;AAAA,EAC3B;AACF;;;ACrDA,wBAAsB;AAMtB,eAAsB,aAAa,SAAgC;AACjE,MAAI;AACF,UAAM,kBAAAE,QAAU,MAAM,OAAO;AAAA,EAC/B,SAAS,OAAY;AACnB,UAAM,IAAI,MAAM,+CAAY,MAAM,OAAO,EAAE;AAAA,EAC7C;AACF;;;ACHA,SAAS,aAAa,KAAqB;AACzC,MAAI,CAAC,IAAI,WAAW,SAAS,KAAK,CAAC,IAAI,WAAW,UAAU,GAAG;AAC7D,WAAO,aAAa;AAAA,EACtB;AACA,SAAO;AACT;AAEA,eAAsB,gBAAgB,KAAa;AACjD,QAAM,UAAU,MAAM,eAAe;AACrC,MAAI;AACF,UAAM,OAAO,MAAM,QAAQ,QAAQ;AACnC,UAAM,aAAa,GAAG;AAEtB,UAAM,KAAK,KAAK,KAAK,EAAE,WAAW,mBAAmB,CAAC;AACtD,UAAM,eAAe,IAAI;AACzB,UAAM,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,GAAG,CAAC;AACvD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,MAAM,gBAAgB,IAAI;AAAA,MACjC,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,MAAM,KAAK,MAAM;AAAA,IAC1B;AAAA,EACF,UAAE;AACA,UAAM,QAAQ,WAAW;AAAA,EAC3B;AACF;AAEA,eAAsB,iBAAiB,OAAe;AACpD,QAAM,UAAU,MAAM,eAAe;AACrC,MAAI;AACF,UAAM,SAAS,sBAAsB,SAAS,KAAK;AACnD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,sBAAsB,KAAK,cAAc;AAAA,IAC3D;AAEA,UAAM,OAAO,MAAM,OAAO,KAAK;AAC/B,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,sBAAsB,KAAK,cAAc;AAAA,IAC3D;AAEA,UAAM,cAAc,MAAM,gBAAgB,IAAI,EAAE,MAAM,MAAM,KAAK;AACjE,UAAM,KAAK,MAAM;AAEjB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF,UAAE;AACA,UAAM,QAAQ,WAAW;AAAA,EAC3B;AACF;AAEA,eAAsB,kBAAkB,OAAe;AACrD,QAAM,UAAU,MAAM,eAAe;AACrC,MAAI;AACF,UAAM,gBAAgB,SAAS,KAAK;AACpC,UAAM,OAAO,MAAM,cAAc,SAAS,KAAK;AAE/C,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,MAAM,gBAAgB,IAAI;AAAA,MACjC,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,MAAM,KAAK,MAAM;AAAA,IAC1B;AAAA,EACF,UAAE;AACA,UAAM,QAAQ,WAAW;AAAA,EAC3B;AACF;AAEA,eAAsB,gBAAgB,OAAgB;AACpD,QAAM,UAAU,MAAM,eAAe;AACrC,MAAI;AACF,UAAM,OAAO,MAAM,cAAc,SAAS,KAAK;AAC/C,UAAM,WAAW,MAAM,KAAK,OAAO;AAEnC,QAAI,CAAC,UAAU;AACb,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,QACP,OAAO,MAAM,gBAAgB,IAAI,EAAE,MAAM,MAAM,KAAK;AAAA,QACpD,KAAK,KAAK,IAAI;AAAA,QACd,OAAO,MAAM,KAAK,MAAM;AAAA,MAC1B;AAAA,IACF;AAEA,UAAM,eAAe,IAAI;AAEzB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,MAAM,gBAAgB,IAAI;AAAA,MACjC,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,MAAM,KAAK,MAAM;AAAA,IAC1B;AAAA,EACF,UAAE;AACA,UAAM,QAAQ,WAAW;AAAA,EAC3B;AACF;AAEA,eAAsB,mBAAmB,OAAgB;AACvD,QAAM,UAAU,MAAM,eAAe;AACrC,MAAI;AACF,UAAM,OAAO,MAAM,cAAc,SAAS,KAAK;AAC/C,UAAM,WAAW,MAAM,KAAK,UAAU;AAEtC,QAAI,CAAC,UAAU;AACb,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,QACP,OAAO,MAAM,gBAAgB,IAAI,EAAE,MAAM,MAAM,KAAK;AAAA,QACpD,KAAK,KAAK,IAAI;AAAA,QACd,OAAO,MAAM,KAAK,MAAM;AAAA,MAC1B;AAAA,IACF;AAEA,UAAM,eAAe,IAAI;AAEzB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,MAAM,gBAAgB,IAAI;AAAA,MACjC,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,MAAM,KAAK,MAAM;AAAA,IAC1B;AAAA,EACF,UAAE;AACA,UAAM,QAAQ,WAAW;AAAA,EAC3B;AACF;;;ACtIA;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,aAAe;AAAA,EACf,MAAQ;AAAA,EACR,OAAS;AAAA,EACT,KAAO;AAAA,IACL,cAAc;AAAA,EAChB;AAAA,EACA,OAAS;AAAA,IACP;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,KAAO;AAAA,IACP,gBAAgB;AAAA,IAChB,OAAS;AAAA,IACT,gBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,OAAS;AAAA,EACX;AAAA,EACA,UAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,EACX,QAAU;AAAA,EACV,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AAAA,EACA,eAAiB;AAAA,IACf,QAAU;AAAA,EACZ;AAAA,EACA,cAAgB;AAAA,IACd,0BAA0B;AAAA,IAC1B,sBAAsB;AAAA,IACtB,+BAA+B;AAAA,IAC/B,YAAc;AAAA,IACd,WAAa;AAAA,IACb,OAAS;AAAA,IACT,YAAc;AAAA,IACd,kBAAkB;AAAA,EACpB;AAAA,EACA,iBAAmB;AAAA,IACjB,eAAe;AAAA,IACf,SAAW;AAAA,IACX,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,YAAc;AAAA,EAChB;AACF;;;AR9BA,IAAM,UAAU,IAAI,yBAAQ;AAE5B,SAAS,WAAW,IAAiC;AACnD,MAAI,CAAC,GAAI,QAAO;AAChB,SAAO;AACT;AAEA,SAAS,aAAa,SAAiB,QAAgB;AACrD,MAAI;AAEF,UAAM,aAAa,aAAAC,QAAK,KAAK,SAAS,gBAAgB;AACtD,QAAI,WAAAC,QAAG,WAAW,UAAU,GAAG;AAC7B,UAAI;AAAE,mBAAAA,QAAG,WAAW,UAAU;AAAA,MAAE,QAAQ;AAAA,MAAC;AAAA,IAC3C;AACA,QAAI,WAAAA,QAAG,WAAW,OAAO,GAAG;AAC1B,YAAM,YAAY,WAAAA,QAAG,YAAY,OAAO;AACxC,iBAAW,QAAQ,WAAW;AAC5B,YAAI,sCAAsC,KAAK,IAAI,GAAG;AACpD,cAAI;AAAE,uBAAAA,QAAG,WAAW,aAAAD,QAAK,KAAK,SAAS,IAAI,CAAC;AAAA,UAAE,QAAQ;AAAA,UAAC;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAGA,QAAI,CAAC,WAAAC,QAAG,WAAW,MAAM,EAAG;AAC5B,UAAM,QAAQ,WAAAA,QAAG,YAAY,MAAM;AACnC,UAAM,iBAAiB;AACvB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WAAW,IAAI,KAAK,KAAK,KAAK;AAEpC,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,KAAK,MAAM,cAAc;AACvC,UAAI,OAAO;AACT,cAAM,cAAc,MAAM,CAAC;AAC3B,cAAM,WAAW,IAAI,KAAK,WAAW,EAAE,QAAQ;AAC/C,YAAI,MAAM,QAAQ,EAAG;AACrB,YAAI,MAAM,WAAW,UAAU;AAC7B,cAAI;AACF,uBAAAA,QAAG,WAAW,aAAAD,QAAK,KAAK,QAAQ,IAAI,CAAC;AAAA,UACvC,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,GAAG;AAAA,EAEZ;AACF;AAEA,SAAS,SAAS,aAAqB,MAAW,QAAa,OAAa;AAC1E,MAAI;AACF,UAAM,UAAU,QAAQ,IAAI,oBAAoB,aAAAA,QAAK,KAAK,WAAAE,QAAG,QAAQ,GAAG,wBAAwB;AAChG,UAAM,SAAS,aAAAF,QAAK,KAAK,SAAS,MAAM;AACxC,QAAI,CAAC,WAAAC,QAAG,WAAW,MAAM,GAAG;AAC1B,iBAAAA,QAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,IAC1C;AACA,UAAM,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AACtD,UAAM,UAAU,aAAAD,QAAK,KAAK,QAAQ,cAAc,QAAQ,MAAM;AAE9D,UAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAI,UAAU;AAAA;AACd,eAAW,IAAI,SAAS,cAAc,WAAW;AAAA;AACjD,eAAW;AAAA,EAAU,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA;AAAA;AAElD,QAAI,OAAO;AACT,iBAAW;AAAA,EAAW,iBAAiB,QAAQ,MAAM,QAAQ,OAAO,KAAK,CAAC;AAAA;AAAA,IAC5E,WAAW,QAAQ;AACjB,iBAAW;AAAA;AACX,UAAI;AACF,YAAI,UAAU,OAAO,WAAW,UAAU;AACxC,qBAAW,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,IAAI,UAAQ,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,IAAI;AAAA,QAC/F,OAAO;AACL,qBAAW,OAAO,MAAM,EAAE,MAAM,IAAI,EAAE,IAAI,UAAQ,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,IAAI;AAAA,QAC9E;AAAA,MACF,QAAQ;AACN,mBAAW,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,IAAI,UAAQ,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,IAAI;AAAA,MAC/F;AAAA,IACF,OAAO;AACL,iBAAW;AAAA;AAAA,IACb;AACA,eAAW;AAAA;AAAA;AAEX,eAAAC,QAAG,eAAe,SAAS,SAAS,OAAO;AAC3C,iBAAa,SAAS,MAAM;AAAA,EAC9B,SAAS,GAAG;AAAA,EAEZ;AACF;AAEA,SAAS,mBAAmB,OAAgB,aAAqB,MAAmB;AAClF,QAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACjE,WAAS,aAAa,MAAM,MAAM,KAAK;AACvC,UAAQ,MAAM,mBAAAE,QAAG,IAAI,mBAAmB,WAAW,aAAa,GAAG,EAAE,CAAC;AACtE,UAAQ,KAAK,CAAC;AAChB;AAEA,QACG,KAAK,YAAY,EACjB,YAAY,iDAAiD,EAC7D,QAAQ,gBAAY,OAAO,EAC3B,OAAO,0BAA0B,4IAAyB,EAC1D,KAAK,aAAa,CAAC,gBAAgB;AAClC,QAAM,OAAO,YAAY,KAAK;AAC9B,MAAI,KAAK,WAAW;AAClB,YAAQ,IAAI,mBAAmB,KAAK;AAAA,EACtC;AACF,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,+MAAoD,EAChE,OAAO,oBAAoB,mCAAU,EACrC,OAAO,OAAO,YAAY;AACzB,QAAM,OAAO,EAAE,OAAO,WAAW,QAAQ,KAAK,EAAE;AAChD,MAAI;AACF,UAAM,SAAS,MAAM,aAAa,IAAI;AACtC,aAAS,SAAS,MAAM,MAAM;AAC9B,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,SAAS,IAAI;AAAA,EACzC;AACF,CAAC;AAEH,QACG,QAAQ,0BAA0B,EAClC,YAAY,0GAA0B,EACtC,OAAO,oBAAoB,mCAAU,EACrC;AAAA,EACC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAKF,EACC,OAAO,OAAO,UAAU,MAAM,YAAY;AACzC,MAAI,gBAAgB;AACpB,QAAM,UAAU,EAAE,UAAU,MAAM,MAAM,QAAQ,MAAM,OAAO,QAAQ,MAAM;AAC3E,MAAI;AACF,QAAI;AACJ,QAAI,cAAc,QAAQ,IAAI;AAE9B,QAAI,QAAQ,MAAM;AAChB,YAAM,eAAW,sBAAQ,QAAQ,IAAI,GAAG,QAAQ,IAAI;AACpD,oBAAc,eAAe,QAAQ,IAAI;AACzC,UAAI;AACF,0BAAc,yBAAa,UAAU,OAAO;AAAA,MAC9C,SAAS,GAAQ;AACf,cAAM,IAAI,MAAM,yCAAW,QAAQ,MAAM,EAAE,OAAO,EAAE;AAAA,MACtD;AAAA,IACF;AAEA,oBAAgB,mBAAmB,QAAQ,CAAC,GAAG,aAAa,WAAW;AAEvE,UAAM,SAAS,MAAM,WAAW;AAAA,MAC9B;AAAA,MACA,UAAU;AAAA,MACV,OAAO,WAAW,QAAQ,KAAK;AAAA,IACjC,CAAC;AACD,aAAS,OAAO,EAAE,GAAG,SAAS,cAAc,GAAG,MAAM;AACrD,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,OAAO,EAAE,GAAG,SAAS,cAAc,CAAC;AAAA,EAChE;AACF,CAAC;AAGH,QACG,QAAQ,qBAAqB,EAC7B,YAAY,oEAAa,EACzB,OAAO,OAAO,YAAY;AACzB,QAAM,OAAO,EAAE,QAAQ;AACvB,MAAI;AACF,UAAM,aAAa,OAAO;AAC1B,UAAM,SAAS,EAAE,SAAS,MAAM,SAAS,qEAAc;AACvD,aAAS,aAAa,MAAM,MAAM;AAClC,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,aAAa,IAAI;AAAA,EAC7C;AACF,CAAC;AAEH,IAAM,OAAO,QAAQ,QAAQ,MAAM,EAAE,YAAY,kDAAU;AAE3D,KACG,QAAQ,YAAY,EACpB,YAAY,gCAAO,EACnB,OAAO,OAAO,QAAQ;AACrB,QAAM,OAAO,EAAE,IAAI;AACnB,MAAI;AACF,UAAM,SAAS,MAAM,gBAAgB,GAAG;AACxC,aAAS,aAAa,MAAM,MAAM;AAClC,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,aAAa,IAAI;AAAA,EAC7C;AACF,CAAC;AAEH,KACG,QAAQ,eAAe,EACvB,YAAY,yDAAiB,EAC7B,OAAO,OAAO,UAAU;AACvB,QAAM,OAAO,EAAE,MAAM;AACrB,MAAI;AACF,UAAM,SAAS,MAAM,iBAAiB,KAAK;AAC3C,aAAS,cAAc,MAAM,MAAM;AACnC,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,cAAc,IAAI;AAAA,EAC9C;AACF,CAAC;AAEH,KACG,QAAQ,gBAAgB,EACxB,YAAY,iFAAqB,EACjC,OAAO,OAAO,UAAU;AACvB,QAAM,OAAO,EAAE,MAAM;AACrB,MAAI;AACF,UAAM,SAAS,MAAM,kBAAkB,KAAK;AAC5C,aAAS,eAAe,MAAM,MAAM;AACpC,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,eAAe,IAAI;AAAA,EAC/C;AACF,CAAC;AAEH,KACG,QAAQ,cAAc,EACtB,YAAY,4FAAiB,EAC7B,OAAO,OAAO,UAAU;AACvB,QAAM,OAAO,EAAE,OAAO,WAAW,KAAK,EAAE;AACxC,MAAI;AACF,UAAM,SAAS,MAAM,gBAAgB,KAAK,KAAK;AAC/C,aAAS,aAAa,MAAM,MAAM;AAClC,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,aAAa,IAAI;AAAA,EAC7C;AACF,CAAC;AAEH,KACG,QAAQ,iBAAiB,EACzB,YAAY,4FAAiB,EAC7B,OAAO,OAAO,UAAU;AACvB,QAAM,OAAO,EAAE,OAAO,WAAW,KAAK,EAAE;AACxC,MAAI;AACF,UAAM,SAAS,MAAM,mBAAmB,KAAK,KAAK;AAClD,aAAS,gBAAgB,MAAM,MAAM;AACrC,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,SAAS,OAAgB;AACvB,uBAAmB,OAAO,gBAAgB,IAAI;AAAA,EAChD;AACF,CAAC;AAEH,QAAQ,MAAM,QAAQ,IAAI;","names":["import_fs","import_path","import_os","import_picocolors","JSON5","import_fs","import_path","import_url","path","resolve","http","puppeteer","pc","os","fs","tabs","resolve","clipboard","resolve","path","fs","os","pc"]}
|