@opentiny/webmcp-cli 0.0.1-alpha.2 → 0.0.1-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -112,31 +112,54 @@ async function killProcessOnPortIfZombie(port) {
112
112
  } catch (e) {
113
113
  }
114
114
  }
115
- function getDefaultChromePath() {
115
+ function getDefaultBrowserPath() {
116
116
  const platform = import_os.default.platform();
117
117
  if (platform === "darwin") {
118
- return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
118
+ const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
119
+ if (import_fs.default.existsSync(chromePath)) return { path: chromePath, name: "Chrome" };
120
+ const edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
121
+ if (import_fs.default.existsSync(edgePath)) return { path: edgePath, name: "Edge" };
122
+ return null;
119
123
  } else if (platform === "win32") {
120
- const paths = [
124
+ const chromePaths = [
121
125
  process.env.LOCALAPPDATA + "\\Google\\Chrome\\Application\\chrome.exe",
122
126
  process.env.PROGRAMFILES + "\\Google\\Chrome\\Application\\chrome.exe",
123
127
  process.env["PROGRAMFILES(X86)"] + "\\Google\\Chrome\\Application\\chrome.exe"
124
128
  ];
125
- return paths.find((p) => import_fs.default.existsSync(p)) || null;
129
+ const foundChrome = chromePaths.find((p) => import_fs.default.existsSync(p));
130
+ if (foundChrome) return { path: foundChrome, name: "Chrome" };
131
+ const edgePaths = [
132
+ process.env.LOCALAPPDATA + "\\Microsoft\\Edge\\Application\\msedge.exe",
133
+ process.env.PROGRAMFILES + "\\Microsoft\\Edge\\Application\\msedge.exe",
134
+ process.env["PROGRAMFILES(X86)"] + "\\Microsoft\\Edge\\Application\\msedge.exe",
135
+ "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
136
+ "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe"
137
+ ];
138
+ const foundEdge = edgePaths.find((p) => import_fs.default.existsSync(p));
139
+ if (foundEdge) return { path: foundEdge, name: "Edge" };
140
+ return null;
126
141
  } else {
127
- const paths = ["/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium", "/usr/bin/chromium-browser"];
128
- return paths.find((p) => import_fs.default.existsSync(p)) || null;
142
+ const paths = [
143
+ { p: "/usr/bin/google-chrome", name: "Chrome" },
144
+ { p: "/usr/bin/google-chrome-stable", name: "Chrome" },
145
+ { p: "/usr/bin/chromium", name: "Chromium" },
146
+ { p: "/usr/bin/chromium-browser", name: "Chromium" },
147
+ { p: "/usr/bin/microsoft-edge", name: "Edge" },
148
+ { p: "/usr/bin/microsoft-edge-stable", name: "Edge" }
149
+ ];
150
+ const found = paths.find(({ p }) => import_fs.default.existsSync(p));
151
+ return found ? { path: found.p, name: found.name } : null;
129
152
  }
130
153
  }
131
- async function startChromeInBackground() {
132
- const chromePath = getDefaultChromePath();
133
- if (!chromePath || !import_fs.default.existsSync(chromePath)) {
134
- throw new Error("\u65E0\u6CD5\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 Chrome \u6D4F\u89C8\u5668\u7684\u9ED8\u8BA4\u5B89\u88C5\u8DEF\u5F84\u3002");
154
+ async function startBrowserInBackground() {
155
+ const browserInfo = getDefaultBrowserPath();
156
+ if (!browserInfo || !import_fs.default.existsSync(browserInfo.path)) {
157
+ throw new Error("\u65E0\u6CD5\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 Chrome \u6216 Edge \u6D4F\u89C8\u5668\u7684\u9ED8\u8BA4\u5B89\u88C5\u8DEF\u5F84\u3002");
135
158
  }
136
- console.log(import_picocolors.default.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 Chrome \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
159
+ console.log(import_picocolors.default.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
137
160
  const userDataDir = process.env.WEBMCP_WORKSPACE || import_path.default.join(import_os.default.homedir(), ".webmcp_chrome_profile");
138
161
  const child = (0, import_child_process.spawn)(
139
- chromePath,
162
+ browserInfo.path,
140
163
  [
141
164
  `--remote-debugging-port=${CDP_PORT}`,
142
165
  `--user-data-dir=${userDataDir}`,
@@ -156,7 +179,7 @@ async function startChromeInBackground() {
156
179
  try {
157
180
  const response = await fetchWithTimeout(url, 1e3);
158
181
  if (response.ok) {
159
- console.log(import_picocolors.default.green("Chrome \u542F\u52A8\u5E76\u5C31\u7EEA\u3002"));
182
+ console.log(import_picocolors.default.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
160
183
  return;
161
184
  }
162
185
  } catch (err) {
@@ -166,7 +189,7 @@ async function startChromeInBackground() {
166
189
  }
167
190
  await new Promise((resolve) => setTimeout(resolve, 500));
168
191
  }
169
- throw new Error("Chrome \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002");
192
+ throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
170
193
  }
171
194
  async function connectBrowser() {
172
195
  const targetFilter = (target) => {
@@ -191,7 +214,7 @@ async function connectBrowser() {
191
214
  throw new Error("127.0.0.1 CDP port not responding");
192
215
  }
193
216
  console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
194
- const browser = await promiseWithTimeout(
217
+ const browser2 = await promiseWithTimeout(
195
218
  import_puppeteer_core.default.connect({
196
219
  browserURL: `http://127.0.0.1:${CDP_PORT}`,
197
220
  defaultViewport: null,
@@ -201,7 +224,7 @@ async function connectBrowser() {
201
224
  "puppeteer.connect to 127.0.0.1 timed out"
202
225
  );
203
226
  console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
204
- return browser;
227
+ return browser2;
205
228
  } catch (error) {
206
229
  try {
207
230
  const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
@@ -209,7 +232,7 @@ async function connectBrowser() {
209
232
  throw new Error("localhost CDP port not responding");
210
233
  }
211
234
  console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
212
- const browser = await promiseWithTimeout(
235
+ const browser2 = await promiseWithTimeout(
213
236
  import_puppeteer_core.default.connect({
214
237
  browserURL: `http://localhost:${CDP_PORT}`,
215
238
  defaultViewport: null,
@@ -219,19 +242,19 @@ async function connectBrowser() {
219
242
  "puppeteer.connect to localhost timed out"
220
243
  );
221
244
  console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
222
- return browser;
245
+ return browser2;
223
246
  } catch (error2) {
224
247
  console.log(import_picocolors.default.yellow(`connectBrowser: \u8FDE\u63A5\u5931\u8D25\uFF0C\u5C06\u5C1D\u8BD5\u5524\u8D77\u6D4F\u89C8\u5668\u3002\u9519\u8BEF\u539F\u56E0: ${error2 instanceof Error ? error2.message : String(error2)}`));
225
248
  try {
226
249
  await killProcessOnPortIfZombie(CDP_PORT);
227
- await startChromeInBackground();
250
+ await startBrowserInBackground();
228
251
  try {
229
252
  const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
230
253
  if (!is127Ready) {
231
254
  throw new Error("127.0.0.1 CDP port not responding after launch");
232
255
  }
233
256
  console.log(import_picocolors.default.yellow("connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
234
- const browser = await promiseWithTimeout(
257
+ const browser2 = await promiseWithTimeout(
235
258
  import_puppeteer_core.default.connect({
236
259
  browserURL: `http://127.0.0.1:${CDP_PORT}`,
237
260
  defaultViewport: null,
@@ -241,14 +264,14 @@ async function connectBrowser() {
241
264
  "puppeteer.connect to 127.0.0.1 after launch timed out"
242
265
  );
243
266
  console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
244
- return browser;
267
+ return browser2;
245
268
  } catch (e) {
246
269
  const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
247
270
  if (!isLocalhostReady) {
248
271
  throw new Error("localhost CDP port not responding after launch");
249
272
  }
250
273
  console.log(import_picocolors.default.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
251
- const browser = await promiseWithTimeout(
274
+ const browser2 = await promiseWithTimeout(
252
275
  import_puppeteer_core.default.connect({
253
276
  browserURL: `http://localhost:${CDP_PORT}`,
254
277
  defaultViewport: null,
@@ -258,20 +281,20 @@ async function connectBrowser() {
258
281
  "puppeteer.connect to localhost after launch timed out"
259
282
  );
260
283
  console.log(import_picocolors.default.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
261
- return browser;
284
+ return browser2;
262
285
  }
263
286
  } catch (launchError) {
264
287
  const msg = launchError instanceof Error ? launchError.message : String(launchError);
265
288
  console.error(import_picocolors.default.red(`\u65E0\u6CD5\u8FDE\u63A5\u6216\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
266
- console.error(import_picocolors.default.yellow(`\u{1F4A1} \u63D0\u793A\uFF1A\u7531\u4E8E\u6211\u4EEC\u8981\u4F7F\u7528\u4F60\u65E5\u5E38\u7684\u9ED8\u8BA4\u6D4F\u89C8\u5668\uFF08\u5305\u542B\u4F60\u7684\u4E66\u7B7E and \u767B\u5F55\u6001\uFF09\uFF0C\u5982\u679C\u4F60\u7684 Chrome \u76EE\u524D\u6B63\u5904\u4E8E\u6253\u5F00\u72B6\u6001\uFF0C\u5B83\u4F1A\u62D2\u7EDD\u4F7F\u7528\u5E26\u6709\u8C03\u8BD5\u7AEF\u53E3\u7684\u65B0\u53C2\u6570\u542F\u52A8\u3002`));
267
- console.error(import_picocolors.default.yellow(`\u{1F449} \u89E3\u51B3\u529E\u6CD5\uFF1A\u8BF7\u5148\u5B8C\u5168\u9000\u51FA\u5F53\u524D\u7684 Chrome \u6D4F\u89C8\u5668\uFF08\u5728 Mac \u4E0A\u6309 Cmd+Q\uFF09\uFF0C\u7136\u540E\u518D\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u3002`));
289
+ console.error(import_picocolors.default.yellow(`\u{1F4A1} \u63D0\u793A\uFF1A\u7531\u4E8E\u6211\u4EEC\u8981\u4F7F\u7528\u4F60\u65E5\u5E38\u7684\u9ED8\u8BA4\u6D4F\u89C8\u5668\uFF08\u5305\u542B\u4F60\u7684\u4E66\u7B7E\u548C\u767B\u5F55\u6001\uFF09\uFF0C\u5982\u679C\u4F60\u7684 Chrome/Edge \u76EE\u524D\u6B63\u5904\u4E8E\u6253\u5F00\u72B6\u6001\uFF0C\u5B83\u4F1A\u62D2\u7EDD\u4F7F\u7528\u5E26\u6709\u8C03\u8BD5\u7AEF\u53E3\u7684\u65B0\u53C2\u6570\u542F\u52A8\u3002`));
290
+ console.error(import_picocolors.default.yellow(`\u{1F449} \u89E3\u51B3\u529E\u6CD5\uFF1A\u8BF7\u5148\u5B8C\u5168\u9000\u51FA\u5F53\u524D\u7684 Chrome \u6216 Edge \u6D4F\u89C8\u5668\uFF08\u5728 Mac \u4E0A\u6309 Cmd+Q\uFF0CWindows \u4E0A\u53F3\u952E\u4EFB\u52A1\u680F\u56FE\u6807\u9000\u51FA\uFF09\uFF0C\u7136\u540E\u518D\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u3002`));
268
291
  throw new Error("Browser connection failed.");
269
292
  }
270
293
  }
271
294
  }
272
295
  }
273
- async function getPageTargetId(page) {
274
- const session = await page.target().createCDPSession();
296
+ async function getPageTargetId(page2) {
297
+ const session = await page2.target().createCDPSession();
275
298
  try {
276
299
  const { targetInfo } = await session.send("Target.getTargetInfo");
277
300
  return targetInfo.targetId;
@@ -280,8 +303,8 @@ async function getPageTargetId(page) {
280
303
  });
281
304
  }
282
305
  }
283
- function getPageTargets(browser) {
284
- return browser.targets().filter((t) => {
306
+ function getPageTargets(browser2) {
307
+ return browser2.targets().filter((t) => {
285
308
  try {
286
309
  const type = (typeof t.type === "function" ? t.type() : t.type) || "";
287
310
  const url = (typeof t.url === "function" ? t.url() : t.url) || "";
@@ -294,26 +317,26 @@ function getPageTargets(browser) {
294
317
  function getTargetIdFromTarget(target) {
295
318
  return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
296
319
  }
297
- function findPageTargetByTabId(browser, tabid) {
298
- const pageTargets = getPageTargets(browser);
320
+ function findPageTargetByTabId(browser2, tabid2) {
321
+ const pageTargets = getPageTargets(browser2);
299
322
  return pageTargets.find((t) => {
300
323
  const tid = getTargetIdFromTarget(t);
301
- return tid === tabid || tid.includes(tabid);
324
+ return tid === tabid2 || tid.includes(tabid2);
302
325
  }) ?? null;
303
326
  }
304
- async function activateTabById(browser, tabid) {
305
- const target = findPageTargetByTabId(browser, tabid);
327
+ async function activateTabById(browser2, tabid2) {
328
+ const target = findPageTargetByTabId(browser2, tabid2);
306
329
  if (!target) {
307
- throw new Error(`Tab with targetId "${tabid}" not found.`);
330
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
308
331
  }
309
332
  const realTabId = getTargetIdFromTarget(target);
310
- const pages = await browser.pages();
333
+ const pages = await browser2.pages();
311
334
  let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
312
335
  if (!sessionPage && pages.length > 0) {
313
336
  sessionPage = pages[0];
314
337
  }
315
338
  if (!sessionPage) {
316
- sessionPage = await browser.newPage();
339
+ sessionPage = await browser2.newPage();
317
340
  }
318
341
  const session = await sessionPage.createCDPSession();
319
342
  try {
@@ -323,18 +346,18 @@ async function activateTabById(browser, tabid) {
323
346
  });
324
347
  }
325
348
  }
326
- async function getTargetPage(browser, tabid) {
327
- const pageTargets = getPageTargets(browser);
349
+ async function getTargetPage(browser2, tabid2) {
350
+ const pageTargets = getPageTargets(browser2);
328
351
  if (pageTargets.length === 0) {
329
- const newPage = await browser.newPage();
352
+ const newPage = await browser2.newPage();
330
353
  await injectWebMCPPolyfillAndTools(newPage);
331
354
  return newPage;
332
355
  }
333
356
  let targetPage = null;
334
- if (tabid !== void 0) {
335
- const target = findPageTargetByTabId(browser, tabid);
357
+ if (tabid2 !== void 0) {
358
+ const target = findPageTargetByTabId(browser2, tabid2);
336
359
  if (!target) {
337
- throw new Error(`Tab with targetId "${tabid}" not found.`);
360
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
338
361
  }
339
362
  targetPage = await target.page();
340
363
  } else {
@@ -379,11 +402,11 @@ async function getTargetPage(browser, tabid) {
379
402
  await injectWebMCPPolyfillAndTools(targetPage);
380
403
  return targetPage;
381
404
  }
382
- async function injectIntoPage(page) {
383
- await injectWebMCPPolyfillAndTools(page, true);
405
+ async function injectIntoPage(page2) {
406
+ await injectWebMCPPolyfillAndTools(page2, true);
384
407
  }
385
- async function injectWebMCPPolyfillAndTools(page, force = false) {
386
- const polyfillReady = !force && await page.evaluate(() => {
408
+ async function injectWebMCPPolyfillAndTools(page2, force = false) {
409
+ const polyfillReady = !force && await page2.evaluate(() => {
387
410
  return !!window.__webmcpcli_init;
388
411
  }).catch(() => false);
389
412
  if (!polyfillReady) {
@@ -394,19 +417,19 @@ async function injectWebMCPPolyfillAndTools(page, force = false) {
394
417
  }
395
418
  const scriptContent = import_fs.default.readFileSync(injectScriptPath, "utf-8");
396
419
  try {
397
- await page.evaluate(scriptContent);
420
+ await page2.evaluate(scriptContent);
398
421
  } catch (err) {
399
422
  const msg = err instanceof Error ? err.message : String(err);
400
423
  throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
401
424
  }
402
425
  await new Promise((resolve) => setTimeout(resolve, 300));
403
426
  }
404
- await injectDomainTools(page);
427
+ await injectDomainTools(page2);
405
428
  }
406
- async function injectDomainTools(page) {
429
+ async function injectDomainTools(page2) {
407
430
  let hostname;
408
431
  try {
409
- const url = new URL(page.url());
432
+ const url = new URL(page2.url());
410
433
  hostname = url.hostname;
411
434
  } catch {
412
435
  return;
@@ -418,7 +441,7 @@ async function injectDomainTools(page) {
418
441
  console.log(import_picocolors.default.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
419
442
  const toolScript = import_fs.default.readFileSync(toolBundlePath, "utf-8");
420
443
  try {
421
- await page.evaluate(toolScript);
444
+ await page2.evaluate(toolScript);
422
445
  console.log(import_picocolors.default.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
423
446
  } catch (err) {
424
447
  const msg = err instanceof Error ? err.message : String(err);
@@ -427,11 +450,12 @@ async function injectDomainTools(page) {
427
450
  }
428
451
 
429
452
  // src/commands/state.ts
430
- async function stateCommand({ tabid }) {
431
- const browser = await connectBrowser();
453
+ async function stateCommand({ tabid: tabid2 }) {
454
+ const browser2 = await connectBrowser();
432
455
  try {
433
- const page = await getTargetPage(browser, tabid);
434
- const state = await page.evaluate(async () => {
456
+ const page2 = await getTargetPage(browser2, tabid2);
457
+ await injectIntoPage(page2);
458
+ const state = await page2.evaluate(async () => {
435
459
  const url = document.URL;
436
460
  const title = document.title;
437
461
  const mcp = navigator.modelContextTesting || navigator.modelContext;
@@ -479,27 +503,29 @@ async function stateCommand({ tabid }) {
479
503
  webmcpTools
480
504
  };
481
505
  });
482
- const pages = await browser.pages();
483
- const tabs2 = await Promise.all(pages.map(async (p) => {
484
- const pUrl = p.url();
485
- if (pUrl.startsWith("devtools://")) return null;
486
- const pTitle = await Promise.race([
487
- p.title().catch(() => "Unknown"),
488
- new Promise((resolve) => setTimeout(() => resolve("Unknown"), 500))
489
- ]);
490
- return {
491
- // 使用真实的 Chrome target ID,而非数组下标
492
- tabid: await getPageTargetId(p).catch(() => pUrl),
493
- title: pTitle,
494
- url: pUrl
495
- };
496
- }));
506
+ const pages = await browser2.pages();
507
+ const tabs2 = await Promise.all(
508
+ pages.map(async (p) => {
509
+ const pUrl = p.url();
510
+ if (pUrl.startsWith("devtools://")) return null;
511
+ const pTitle = await Promise.race([
512
+ p.title().catch(() => "Unknown"),
513
+ new Promise((resolve) => setTimeout(() => resolve("Unknown"), 500))
514
+ ]);
515
+ return {
516
+ // 使用真实的 Chrome target ID,而非数组下标
517
+ tabid: await getPageTargetId(p).catch(() => pUrl),
518
+ title: pTitle,
519
+ url: pUrl
520
+ };
521
+ })
522
+ );
497
523
  return {
498
524
  ...state,
499
525
  tabs: tabs2.filter(Boolean)
500
526
  };
501
527
  } finally {
502
- await browser.disconnect();
528
+ await browser2.disconnect();
503
529
  }
504
530
  }
505
531
 
@@ -507,17 +533,17 @@ async function stateCommand({ tabid }) {
507
533
  async function runCommand({
508
534
  toolName,
509
535
  argsJson,
510
- tabid
536
+ tabid: tabid2
511
537
  }) {
512
- const browser = await connectBrowser();
538
+ const browser2 = await connectBrowser();
513
539
  try {
514
- const page = await getTargetPage(browser, tabid);
540
+ const page2 = await getTargetPage(browser2, tabid2);
515
541
  try {
516
542
  JSON.parse(argsJson);
517
543
  } catch (e) {
518
544
  throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
519
545
  }
520
- const result = await page.evaluate(async (name, inputString) => {
546
+ const result2 = await page2.evaluate(async (name, inputString) => {
521
547
  const mcp = navigator.modelContextTesting || navigator.modelContext;
522
548
  if (!mcp || typeof mcp.executeTool !== "function") {
523
549
  throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
@@ -531,12 +557,51 @@ async function runCommand({
531
557
  }
532
558
  return res;
533
559
  }, toolName, argsJson);
560
+ return result2;
561
+ } finally {
562
+ await browser2.disconnect();
563
+ }
564
+ }
565
+
566
+ // src/commands/evaluate.ts
567
+ async function evaluateCommand({
568
+ jsScript,
569
+ tabid
570
+ }) {
571
+ const browser = await connectBrowser();
572
+ try {
573
+ const page = await getTargetPage(browser, tabid);
574
+ const result = await page.evaluate(async (script) => {
575
+ try {
576
+ const result = eval(script);
577
+ if (result instanceof Promise) {
578
+ return await result;
579
+ }
580
+ return result;
581
+ } catch (error) {
582
+ return {
583
+ error: true,
584
+ message: error.message,
585
+ stack: error.stack
586
+ };
587
+ }
588
+ }, jsScript);
534
589
  return result;
535
590
  } finally {
536
591
  await browser.disconnect();
537
592
  }
538
593
  }
539
594
 
595
+ // src/commands/clipboard.ts
596
+ var import_clipboardy = __toESM(require("clipboardy"), 1);
597
+ async function setClipboard(content) {
598
+ try {
599
+ await import_clipboardy.default.write(content);
600
+ } catch (error) {
601
+ throw new Error(`\u8BBE\u7F6E\u526A\u8D34\u677F\u5931\u8D25: ${error.message}`);
602
+ }
603
+ }
604
+
540
605
  // src/commands/tabs.ts
541
606
  function normalizeUrl(url) {
542
607
  if (!url.startsWith("http://") && !url.startsWith("https://")) {
@@ -545,114 +610,114 @@ function normalizeUrl(url) {
545
610
  return url;
546
611
  }
547
612
  async function tabsOpenCommand(url) {
548
- const browser = await connectBrowser();
613
+ const browser2 = await connectBrowser();
549
614
  try {
550
- const page = await browser.newPage();
615
+ const page2 = await browser2.newPage();
551
616
  url = normalizeUrl(url);
552
- await page.goto(url, { waitUntil: "domcontentloaded" });
553
- await injectIntoPage(page);
617
+ await page2.goto(url, { waitUntil: "domcontentloaded" });
618
+ await injectIntoPage(page2);
554
619
  await new Promise((resolve) => setTimeout(resolve, 500));
555
620
  return {
556
621
  success: true,
557
- tabid: await getPageTargetId(page),
558
- url: page.url(),
559
- title: await page.title()
622
+ tabid: await getPageTargetId(page2),
623
+ url: page2.url(),
624
+ title: await page2.title()
560
625
  };
561
626
  } finally {
562
- await browser.disconnect();
627
+ await browser2.disconnect();
563
628
  }
564
629
  }
565
- async function tabsCloseCommand(tabid) {
566
- const browser = await connectBrowser();
630
+ async function tabsCloseCommand(tabid2) {
631
+ const browser2 = await connectBrowser();
567
632
  try {
568
- const target = findPageTargetByTabId(browser, tabid);
633
+ const target = findPageTargetByTabId(browser2, tabid2);
569
634
  if (!target) {
570
- throw new Error(`Tab with targetId "${tabid}" not found.`);
635
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
571
636
  }
572
- const page = await target.page();
573
- if (!page) {
574
- throw new Error(`Tab with targetId "${tabid}" not found.`);
637
+ const page2 = await target.page();
638
+ if (!page2) {
639
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
575
640
  }
576
- const closedTabid = await getPageTargetId(page).catch(() => tabid);
577
- await page.close();
641
+ const closedTabid = await getPageTargetId(page2).catch(() => tabid2);
642
+ await page2.close();
578
643
  return {
579
644
  success: true,
580
645
  tabid: closedTabid
581
646
  };
582
647
  } finally {
583
- await browser.disconnect();
648
+ await browser2.disconnect();
584
649
  }
585
650
  }
586
- async function tabsSwitchCommand(tabid) {
587
- const browser = await connectBrowser();
651
+ async function tabsSwitchCommand(tabid2) {
652
+ const browser2 = await connectBrowser();
588
653
  try {
589
- await activateTabById(browser, tabid);
590
- const page = await getTargetPage(browser, tabid);
654
+ await activateTabById(browser2, tabid2);
655
+ const page2 = await getTargetPage(browser2, tabid2);
591
656
  return {
592
657
  success: true,
593
- tabid: await getPageTargetId(page),
594
- url: page.url(),
595
- title: await page.title()
658
+ tabid: await getPageTargetId(page2),
659
+ url: page2.url(),
660
+ title: await page2.title()
596
661
  };
597
662
  } finally {
598
- await browser.disconnect();
663
+ await browser2.disconnect();
599
664
  }
600
665
  }
601
- async function tabsBackCommand(tabid) {
602
- const browser = await connectBrowser();
666
+ async function tabsBackCommand(tabid2) {
667
+ const browser2 = await connectBrowser();
603
668
  try {
604
- const page = await getTargetPage(browser, tabid);
605
- const response = await page.goBack();
669
+ const page2 = await getTargetPage(browser2, tabid2);
670
+ const response = await page2.goBack();
606
671
  if (!response) {
607
672
  return {
608
673
  success: false,
609
674
  error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
610
- tabid: await getPageTargetId(page).catch(() => tabid),
611
- url: page.url(),
612
- title: await page.title()
675
+ tabid: await getPageTargetId(page2).catch(() => tabid2),
676
+ url: page2.url(),
677
+ title: await page2.title()
613
678
  };
614
679
  }
615
- await injectIntoPage(page);
680
+ await injectIntoPage(page2);
616
681
  return {
617
682
  success: true,
618
- tabid: await getPageTargetId(page),
619
- url: page.url(),
620
- title: await page.title()
683
+ tabid: await getPageTargetId(page2),
684
+ url: page2.url(),
685
+ title: await page2.title()
621
686
  };
622
687
  } finally {
623
- await browser.disconnect();
688
+ await browser2.disconnect();
624
689
  }
625
690
  }
626
- async function tabsForwardCommand(tabid) {
627
- const browser = await connectBrowser();
691
+ async function tabsForwardCommand(tabid2) {
692
+ const browser2 = await connectBrowser();
628
693
  try {
629
- const page = await getTargetPage(browser, tabid);
630
- const response = await page.goForward();
694
+ const page2 = await getTargetPage(browser2, tabid2);
695
+ const response = await page2.goForward();
631
696
  if (!response) {
632
697
  return {
633
698
  success: false,
634
699
  error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
635
- tabid: await getPageTargetId(page).catch(() => tabid),
636
- url: page.url(),
637
- title: await page.title()
700
+ tabid: await getPageTargetId(page2).catch(() => tabid2),
701
+ url: page2.url(),
702
+ title: await page2.title()
638
703
  };
639
704
  }
640
- await injectIntoPage(page);
705
+ await injectIntoPage(page2);
641
706
  return {
642
707
  success: true,
643
- tabid: await getPageTargetId(page),
644
- url: page.url(),
645
- title: await page.title()
708
+ tabid: await getPageTargetId(page2),
709
+ url: page2.url(),
710
+ title: await page2.title()
646
711
  };
647
712
  } finally {
648
- await browser.disconnect();
713
+ await browser2.disconnect();
649
714
  }
650
715
  }
651
716
 
652
717
  // package.json
653
718
  var package_default = {
654
719
  name: "@opentiny/webmcp-cli",
655
- version: "0.0.1-alpha.2",
720
+ version: "0.0.1-alpha.3",
656
721
  type: "module",
657
722
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
658
723
  main: "dist/index.js",
@@ -689,6 +754,7 @@ var package_default = {
689
754
  "@mcp-b/webmcp-polyfill": "catalog:",
690
755
  "@opentiny/next-sdk": "workspace:^",
691
756
  "@page-agent/page-controller": "catalog:",
757
+ clipboardy: "^5.3.1",
692
758
  commander: "^11.1.0",
693
759
  picocolors: "^1.0.0",
694
760
  "puppeteer-core": "^22.0.0"
@@ -721,61 +787,80 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
721
787
  });
722
788
  program.command("state").description("\u83B7\u53D6\u6D4F\u89C8\u5668\u5F53\u524D\u9875\u7B7E\u6216\u6307\u5B9A\u9875\u7B7E\u7684\u72B6\u6001\uFF08\u5185\u5BB9\u3001\u6240\u6709\u9875\u7B7E\u5217\u8868\u3001\u53EF\u7528 WebMCP \u5DE5\u5177\u5217\u8868\uFF09").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (options) => {
723
789
  try {
724
- const result = await stateCommand({ tabid: parseTabId(options.tabid) });
725
- console.log(JSON.stringify(result, null, 2));
790
+ const result2 = await stateCommand({ tabid: parseTabId(options.tabid) });
791
+ console.log(JSON.stringify(result2, null, 2));
726
792
  } catch (error) {
727
793
  handleCommandError(error, "state");
728
794
  }
729
795
  });
730
796
  program.command("run <toolName> <argsJson>").description("\u5411\u6307\u5B9A\u9875\u7B7E\u8C03\u7528\u6307\u5B9A\u7684 WebMCP \u5DE5\u5177\u6267\u884C\u64CD\u4F5C").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (toolName, argsJson, options) => {
731
797
  try {
732
- const result = await runCommand({
798
+ const result2 = await runCommand({
733
799
  toolName,
734
800
  argsJson,
735
801
  tabid: parseTabId(options.tabid)
736
802
  });
737
- console.log(JSON.stringify(result, null, 2));
803
+ console.log(JSON.stringify(result2, null, 2));
738
804
  } catch (error) {
739
805
  handleCommandError(error, "run");
740
806
  }
741
807
  });
808
+ program.command("evaluate <jsScript>").description("\u5411\u5F53\u524D\u7F51\u9875\u6216\u6307\u5B9A\u9875\u7B7E\u6CE8\u5165\u5E76\u6267\u884C JavaScript \u4EE3\u7801").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").action(async (jsScript2, options) => {
809
+ try {
810
+ const result2 = await evaluateCommand({
811
+ jsScript: jsScript2,
812
+ tabid: parseTabId(options.tabid)
813
+ });
814
+ console.log(JSON.stringify(result2, null, 2));
815
+ } catch (error) {
816
+ handleCommandError(error, "evaluate");
817
+ }
818
+ });
819
+ program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
820
+ try {
821
+ await setClipboard(content);
822
+ console.log(JSON.stringify({ success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" }, null, 2));
823
+ } catch (error) {
824
+ handleCommandError(error, "clipboard");
825
+ }
826
+ });
742
827
  var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
743
828
  tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
744
829
  try {
745
- const result = await tabsOpenCommand(url);
746
- console.log(JSON.stringify(result, null, 2));
830
+ const result2 = await tabsOpenCommand(url);
831
+ console.log(JSON.stringify(result2, null, 2));
747
832
  } catch (error) {
748
833
  handleCommandError(error, "tabs open");
749
834
  }
750
835
  });
751
- tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
836
+ tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
752
837
  try {
753
- const result = await tabsCloseCommand(tabid);
754
- console.log(JSON.stringify(result, null, 2));
838
+ const result2 = await tabsCloseCommand(tabid2);
839
+ console.log(JSON.stringify(result2, null, 2));
755
840
  } catch (error) {
756
841
  handleCommandError(error, "tabs close");
757
842
  }
758
843
  });
759
- tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
844
+ tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
760
845
  try {
761
- const result = await tabsSwitchCommand(tabid);
762
- console.log(JSON.stringify(result, null, 2));
846
+ const result2 = await tabsSwitchCommand(tabid2);
847
+ console.log(JSON.stringify(result2, null, 2));
763
848
  } catch (error) {
764
849
  handleCommandError(error, "tabs switch");
765
850
  }
766
851
  });
767
- tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid) => {
852
+ tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid2) => {
768
853
  try {
769
- const result = await tabsBackCommand(parseTabId(tabid));
770
- console.log(JSON.stringify(result, null, 2));
854
+ const result2 = await tabsBackCommand(parseTabId(tabid2));
855
+ console.log(JSON.stringify(result2, null, 2));
771
856
  } catch (error) {
772
857
  handleCommandError(error, "tabs back");
773
858
  }
774
859
  });
775
- tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid) => {
860
+ tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid2) => {
776
861
  try {
777
- const result = await tabsForwardCommand(parseTabId(tabid));
778
- console.log(JSON.stringify(result, null, 2));
862
+ const result2 = await tabsForwardCommand(parseTabId(tabid2));
863
+ console.log(JSON.stringify(result2, null, 2));
779
864
  } catch (error) {
780
865
  handleCommandError(error, "tabs forward");
781
866
  }