@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.js CHANGED
@@ -94,31 +94,54 @@ async function killProcessOnPortIfZombie(port) {
94
94
  } catch (e) {
95
95
  }
96
96
  }
97
- function getDefaultChromePath() {
97
+ function getDefaultBrowserPath() {
98
98
  const platform = os.platform();
99
99
  if (platform === "darwin") {
100
- return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
100
+ const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
101
+ if (fs.existsSync(chromePath)) return { path: chromePath, name: "Chrome" };
102
+ const edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
103
+ if (fs.existsSync(edgePath)) return { path: edgePath, name: "Edge" };
104
+ return null;
101
105
  } else if (platform === "win32") {
102
- const paths = [
106
+ const chromePaths = [
103
107
  process.env.LOCALAPPDATA + "\\Google\\Chrome\\Application\\chrome.exe",
104
108
  process.env.PROGRAMFILES + "\\Google\\Chrome\\Application\\chrome.exe",
105
109
  process.env["PROGRAMFILES(X86)"] + "\\Google\\Chrome\\Application\\chrome.exe"
106
110
  ];
107
- return paths.find((p) => fs.existsSync(p)) || null;
111
+ const foundChrome = chromePaths.find((p) => fs.existsSync(p));
112
+ if (foundChrome) return { path: foundChrome, name: "Chrome" };
113
+ const edgePaths = [
114
+ process.env.LOCALAPPDATA + "\\Microsoft\\Edge\\Application\\msedge.exe",
115
+ process.env.PROGRAMFILES + "\\Microsoft\\Edge\\Application\\msedge.exe",
116
+ process.env["PROGRAMFILES(X86)"] + "\\Microsoft\\Edge\\Application\\msedge.exe",
117
+ "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
118
+ "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe"
119
+ ];
120
+ const foundEdge = edgePaths.find((p) => fs.existsSync(p));
121
+ if (foundEdge) return { path: foundEdge, name: "Edge" };
122
+ return null;
108
123
  } else {
109
- const paths = ["/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium", "/usr/bin/chromium-browser"];
110
- return paths.find((p) => fs.existsSync(p)) || null;
124
+ const paths = [
125
+ { p: "/usr/bin/google-chrome", name: "Chrome" },
126
+ { p: "/usr/bin/google-chrome-stable", name: "Chrome" },
127
+ { p: "/usr/bin/chromium", name: "Chromium" },
128
+ { p: "/usr/bin/chromium-browser", name: "Chromium" },
129
+ { p: "/usr/bin/microsoft-edge", name: "Edge" },
130
+ { p: "/usr/bin/microsoft-edge-stable", name: "Edge" }
131
+ ];
132
+ const found = paths.find(({ p }) => fs.existsSync(p));
133
+ return found ? { path: found.p, name: found.name } : null;
111
134
  }
112
135
  }
113
- async function startChromeInBackground() {
114
- const chromePath = getDefaultChromePath();
115
- if (!chromePath || !fs.existsSync(chromePath)) {
116
- throw new Error("\u65E0\u6CD5\u5728\u7CFB\u7EDF\u4E2D\u627E\u5230 Chrome \u6D4F\u89C8\u5668\u7684\u9ED8\u8BA4\u5B89\u88C5\u8DEF\u5F84\u3002");
136
+ async function startBrowserInBackground() {
137
+ const browserInfo = getDefaultBrowserPath();
138
+ if (!browserInfo || !fs.existsSync(browserInfo.path)) {
139
+ 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");
117
140
  }
118
- console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 Chrome \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
141
+ console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
119
142
  const userDataDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), ".webmcp_chrome_profile");
120
143
  const child = spawn(
121
- chromePath,
144
+ browserInfo.path,
122
145
  [
123
146
  `--remote-debugging-port=${CDP_PORT}`,
124
147
  `--user-data-dir=${userDataDir}`,
@@ -138,7 +161,7 @@ async function startChromeInBackground() {
138
161
  try {
139
162
  const response = await fetchWithTimeout(url, 1e3);
140
163
  if (response.ok) {
141
- console.log(pc.green("Chrome \u542F\u52A8\u5E76\u5C31\u7EEA\u3002"));
164
+ console.log(pc.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
142
165
  return;
143
166
  }
144
167
  } catch (err) {
@@ -148,7 +171,7 @@ async function startChromeInBackground() {
148
171
  }
149
172
  await new Promise((resolve) => setTimeout(resolve, 500));
150
173
  }
151
- throw new Error("Chrome \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002");
174
+ throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
152
175
  }
153
176
  async function connectBrowser() {
154
177
  const targetFilter = (target) => {
@@ -173,7 +196,7 @@ async function connectBrowser() {
173
196
  throw new Error("127.0.0.1 CDP port not responding");
174
197
  }
175
198
  console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
176
- const browser = await promiseWithTimeout(
199
+ const browser2 = await promiseWithTimeout(
177
200
  puppeteer.connect({
178
201
  browserURL: `http://127.0.0.1:${CDP_PORT}`,
179
202
  defaultViewport: null,
@@ -183,7 +206,7 @@ async function connectBrowser() {
183
206
  "puppeteer.connect to 127.0.0.1 timed out"
184
207
  );
185
208
  console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
186
- return browser;
209
+ return browser2;
187
210
  } catch (error) {
188
211
  try {
189
212
  const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
@@ -191,7 +214,7 @@ async function connectBrowser() {
191
214
  throw new Error("localhost CDP port not responding");
192
215
  }
193
216
  console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
194
- const browser = await promiseWithTimeout(
217
+ const browser2 = await promiseWithTimeout(
195
218
  puppeteer.connect({
196
219
  browserURL: `http://localhost:${CDP_PORT}`,
197
220
  defaultViewport: null,
@@ -201,19 +224,19 @@ async function connectBrowser() {
201
224
  "puppeteer.connect to localhost timed out"
202
225
  );
203
226
  console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
204
- return browser;
227
+ return browser2;
205
228
  } catch (error2) {
206
229
  console.log(pc.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)}`));
207
230
  try {
208
231
  await killProcessOnPortIfZombie(CDP_PORT);
209
- await startChromeInBackground();
232
+ await startBrowserInBackground();
210
233
  try {
211
234
  const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
212
235
  if (!is127Ready) {
213
236
  throw new Error("127.0.0.1 CDP port not responding after launch");
214
237
  }
215
238
  console.log(pc.yellow("connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
216
- const browser = await promiseWithTimeout(
239
+ const browser2 = await promiseWithTimeout(
217
240
  puppeteer.connect({
218
241
  browserURL: `http://127.0.0.1:${CDP_PORT}`,
219
242
  defaultViewport: null,
@@ -223,14 +246,14 @@ async function connectBrowser() {
223
246
  "puppeteer.connect to 127.0.0.1 after launch timed out"
224
247
  );
225
248
  console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
226
- return browser;
249
+ return browser2;
227
250
  } catch (e) {
228
251
  const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
229
252
  if (!isLocalhostReady) {
230
253
  throw new Error("localhost CDP port not responding after launch");
231
254
  }
232
255
  console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
233
- const browser = await promiseWithTimeout(
256
+ const browser2 = await promiseWithTimeout(
234
257
  puppeteer.connect({
235
258
  browserURL: `http://localhost:${CDP_PORT}`,
236
259
  defaultViewport: null,
@@ -240,20 +263,20 @@ async function connectBrowser() {
240
263
  "puppeteer.connect to localhost after launch timed out"
241
264
  );
242
265
  console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
243
- return browser;
266
+ return browser2;
244
267
  }
245
268
  } catch (launchError) {
246
269
  const msg = launchError instanceof Error ? launchError.message : String(launchError);
247
270
  console.error(pc.red(`\u65E0\u6CD5\u8FDE\u63A5\u6216\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
248
- console.error(pc.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`));
249
- console.error(pc.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`));
271
+ console.error(pc.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`));
272
+ console.error(pc.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`));
250
273
  throw new Error("Browser connection failed.");
251
274
  }
252
275
  }
253
276
  }
254
277
  }
255
- async function getPageTargetId(page) {
256
- const session = await page.target().createCDPSession();
278
+ async function getPageTargetId(page2) {
279
+ const session = await page2.target().createCDPSession();
257
280
  try {
258
281
  const { targetInfo } = await session.send("Target.getTargetInfo");
259
282
  return targetInfo.targetId;
@@ -262,8 +285,8 @@ async function getPageTargetId(page) {
262
285
  });
263
286
  }
264
287
  }
265
- function getPageTargets(browser) {
266
- return browser.targets().filter((t) => {
288
+ function getPageTargets(browser2) {
289
+ return browser2.targets().filter((t) => {
267
290
  try {
268
291
  const type = (typeof t.type === "function" ? t.type() : t.type) || "";
269
292
  const url = (typeof t.url === "function" ? t.url() : t.url) || "";
@@ -276,26 +299,26 @@ function getPageTargets(browser) {
276
299
  function getTargetIdFromTarget(target) {
277
300
  return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
278
301
  }
279
- function findPageTargetByTabId(browser, tabid) {
280
- const pageTargets = getPageTargets(browser);
302
+ function findPageTargetByTabId(browser2, tabid2) {
303
+ const pageTargets = getPageTargets(browser2);
281
304
  return pageTargets.find((t) => {
282
305
  const tid = getTargetIdFromTarget(t);
283
- return tid === tabid || tid.includes(tabid);
306
+ return tid === tabid2 || tid.includes(tabid2);
284
307
  }) ?? null;
285
308
  }
286
- async function activateTabById(browser, tabid) {
287
- const target = findPageTargetByTabId(browser, tabid);
309
+ async function activateTabById(browser2, tabid2) {
310
+ const target = findPageTargetByTabId(browser2, tabid2);
288
311
  if (!target) {
289
- throw new Error(`Tab with targetId "${tabid}" not found.`);
312
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
290
313
  }
291
314
  const realTabId = getTargetIdFromTarget(target);
292
- const pages = await browser.pages();
315
+ const pages = await browser2.pages();
293
316
  let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
294
317
  if (!sessionPage && pages.length > 0) {
295
318
  sessionPage = pages[0];
296
319
  }
297
320
  if (!sessionPage) {
298
- sessionPage = await browser.newPage();
321
+ sessionPage = await browser2.newPage();
299
322
  }
300
323
  const session = await sessionPage.createCDPSession();
301
324
  try {
@@ -305,18 +328,18 @@ async function activateTabById(browser, tabid) {
305
328
  });
306
329
  }
307
330
  }
308
- async function getTargetPage(browser, tabid) {
309
- const pageTargets = getPageTargets(browser);
331
+ async function getTargetPage(browser2, tabid2) {
332
+ const pageTargets = getPageTargets(browser2);
310
333
  if (pageTargets.length === 0) {
311
- const newPage = await browser.newPage();
334
+ const newPage = await browser2.newPage();
312
335
  await injectWebMCPPolyfillAndTools(newPage);
313
336
  return newPage;
314
337
  }
315
338
  let targetPage = null;
316
- if (tabid !== void 0) {
317
- const target = findPageTargetByTabId(browser, tabid);
339
+ if (tabid2 !== void 0) {
340
+ const target = findPageTargetByTabId(browser2, tabid2);
318
341
  if (!target) {
319
- throw new Error(`Tab with targetId "${tabid}" not found.`);
342
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
320
343
  }
321
344
  targetPage = await target.page();
322
345
  } else {
@@ -361,11 +384,11 @@ async function getTargetPage(browser, tabid) {
361
384
  await injectWebMCPPolyfillAndTools(targetPage);
362
385
  return targetPage;
363
386
  }
364
- async function injectIntoPage(page) {
365
- await injectWebMCPPolyfillAndTools(page, true);
387
+ async function injectIntoPage(page2) {
388
+ await injectWebMCPPolyfillAndTools(page2, true);
366
389
  }
367
- async function injectWebMCPPolyfillAndTools(page, force = false) {
368
- const polyfillReady = !force && await page.evaluate(() => {
390
+ async function injectWebMCPPolyfillAndTools(page2, force = false) {
391
+ const polyfillReady = !force && await page2.evaluate(() => {
369
392
  return !!window.__webmcpcli_init;
370
393
  }).catch(() => false);
371
394
  if (!polyfillReady) {
@@ -376,19 +399,19 @@ async function injectWebMCPPolyfillAndTools(page, force = false) {
376
399
  }
377
400
  const scriptContent = fs.readFileSync(injectScriptPath, "utf-8");
378
401
  try {
379
- await page.evaluate(scriptContent);
402
+ await page2.evaluate(scriptContent);
380
403
  } catch (err) {
381
404
  const msg = err instanceof Error ? err.message : String(err);
382
405
  throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
383
406
  }
384
407
  await new Promise((resolve) => setTimeout(resolve, 300));
385
408
  }
386
- await injectDomainTools(page);
409
+ await injectDomainTools(page2);
387
410
  }
388
- async function injectDomainTools(page) {
411
+ async function injectDomainTools(page2) {
389
412
  let hostname;
390
413
  try {
391
- const url = new URL(page.url());
414
+ const url = new URL(page2.url());
392
415
  hostname = url.hostname;
393
416
  } catch {
394
417
  return;
@@ -400,7 +423,7 @@ async function injectDomainTools(page) {
400
423
  console.log(pc.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
401
424
  const toolScript = fs.readFileSync(toolBundlePath, "utf-8");
402
425
  try {
403
- await page.evaluate(toolScript);
426
+ await page2.evaluate(toolScript);
404
427
  console.log(pc.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
405
428
  } catch (err) {
406
429
  const msg = err instanceof Error ? err.message : String(err);
@@ -409,11 +432,12 @@ async function injectDomainTools(page) {
409
432
  }
410
433
 
411
434
  // src/commands/state.ts
412
- async function stateCommand({ tabid }) {
413
- const browser = await connectBrowser();
435
+ async function stateCommand({ tabid: tabid2 }) {
436
+ const browser2 = await connectBrowser();
414
437
  try {
415
- const page = await getTargetPage(browser, tabid);
416
- const state = await page.evaluate(async () => {
438
+ const page2 = await getTargetPage(browser2, tabid2);
439
+ await injectIntoPage(page2);
440
+ const state = await page2.evaluate(async () => {
417
441
  const url = document.URL;
418
442
  const title = document.title;
419
443
  const mcp = navigator.modelContextTesting || navigator.modelContext;
@@ -461,27 +485,29 @@ async function stateCommand({ tabid }) {
461
485
  webmcpTools
462
486
  };
463
487
  });
464
- const pages = await browser.pages();
465
- const tabs2 = await Promise.all(pages.map(async (p) => {
466
- const pUrl = p.url();
467
- if (pUrl.startsWith("devtools://")) return null;
468
- const pTitle = await Promise.race([
469
- p.title().catch(() => "Unknown"),
470
- new Promise((resolve) => setTimeout(() => resolve("Unknown"), 500))
471
- ]);
472
- return {
473
- // 使用真实的 Chrome target ID,而非数组下标
474
- tabid: await getPageTargetId(p).catch(() => pUrl),
475
- title: pTitle,
476
- url: pUrl
477
- };
478
- }));
488
+ const pages = await browser2.pages();
489
+ const tabs2 = await Promise.all(
490
+ pages.map(async (p) => {
491
+ const pUrl = p.url();
492
+ if (pUrl.startsWith("devtools://")) return null;
493
+ const pTitle = await Promise.race([
494
+ p.title().catch(() => "Unknown"),
495
+ new Promise((resolve) => setTimeout(() => resolve("Unknown"), 500))
496
+ ]);
497
+ return {
498
+ // 使用真实的 Chrome target ID,而非数组下标
499
+ tabid: await getPageTargetId(p).catch(() => pUrl),
500
+ title: pTitle,
501
+ url: pUrl
502
+ };
503
+ })
504
+ );
479
505
  return {
480
506
  ...state,
481
507
  tabs: tabs2.filter(Boolean)
482
508
  };
483
509
  } finally {
484
- await browser.disconnect();
510
+ await browser2.disconnect();
485
511
  }
486
512
  }
487
513
 
@@ -489,17 +515,17 @@ async function stateCommand({ tabid }) {
489
515
  async function runCommand({
490
516
  toolName,
491
517
  argsJson,
492
- tabid
518
+ tabid: tabid2
493
519
  }) {
494
- const browser = await connectBrowser();
520
+ const browser2 = await connectBrowser();
495
521
  try {
496
- const page = await getTargetPage(browser, tabid);
522
+ const page2 = await getTargetPage(browser2, tabid2);
497
523
  try {
498
524
  JSON.parse(argsJson);
499
525
  } catch (e) {
500
526
  throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
501
527
  }
502
- const result = await page.evaluate(async (name, inputString) => {
528
+ const result2 = await page2.evaluate(async (name, inputString) => {
503
529
  const mcp = navigator.modelContextTesting || navigator.modelContext;
504
530
  if (!mcp || typeof mcp.executeTool !== "function") {
505
531
  throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
@@ -513,12 +539,51 @@ async function runCommand({
513
539
  }
514
540
  return res;
515
541
  }, toolName, argsJson);
542
+ return result2;
543
+ } finally {
544
+ await browser2.disconnect();
545
+ }
546
+ }
547
+
548
+ // src/commands/evaluate.ts
549
+ async function evaluateCommand({
550
+ jsScript,
551
+ tabid
552
+ }) {
553
+ const browser = await connectBrowser();
554
+ try {
555
+ const page = await getTargetPage(browser, tabid);
556
+ const result = await page.evaluate(async (script) => {
557
+ try {
558
+ const result = eval(script);
559
+ if (result instanceof Promise) {
560
+ return await result;
561
+ }
562
+ return result;
563
+ } catch (error) {
564
+ return {
565
+ error: true,
566
+ message: error.message,
567
+ stack: error.stack
568
+ };
569
+ }
570
+ }, jsScript);
516
571
  return result;
517
572
  } finally {
518
573
  await browser.disconnect();
519
574
  }
520
575
  }
521
576
 
577
+ // src/commands/clipboard.ts
578
+ import clipboard from "clipboardy";
579
+ async function setClipboard(content) {
580
+ try {
581
+ await clipboard.write(content);
582
+ } catch (error) {
583
+ throw new Error(`\u8BBE\u7F6E\u526A\u8D34\u677F\u5931\u8D25: ${error.message}`);
584
+ }
585
+ }
586
+
522
587
  // src/commands/tabs.ts
523
588
  function normalizeUrl(url) {
524
589
  if (!url.startsWith("http://") && !url.startsWith("https://")) {
@@ -527,114 +592,114 @@ function normalizeUrl(url) {
527
592
  return url;
528
593
  }
529
594
  async function tabsOpenCommand(url) {
530
- const browser = await connectBrowser();
595
+ const browser2 = await connectBrowser();
531
596
  try {
532
- const page = await browser.newPage();
597
+ const page2 = await browser2.newPage();
533
598
  url = normalizeUrl(url);
534
- await page.goto(url, { waitUntil: "domcontentloaded" });
535
- await injectIntoPage(page);
599
+ await page2.goto(url, { waitUntil: "domcontentloaded" });
600
+ await injectIntoPage(page2);
536
601
  await new Promise((resolve) => setTimeout(resolve, 500));
537
602
  return {
538
603
  success: true,
539
- tabid: await getPageTargetId(page),
540
- url: page.url(),
541
- title: await page.title()
604
+ tabid: await getPageTargetId(page2),
605
+ url: page2.url(),
606
+ title: await page2.title()
542
607
  };
543
608
  } finally {
544
- await browser.disconnect();
609
+ await browser2.disconnect();
545
610
  }
546
611
  }
547
- async function tabsCloseCommand(tabid) {
548
- const browser = await connectBrowser();
612
+ async function tabsCloseCommand(tabid2) {
613
+ const browser2 = await connectBrowser();
549
614
  try {
550
- const target = findPageTargetByTabId(browser, tabid);
615
+ const target = findPageTargetByTabId(browser2, tabid2);
551
616
  if (!target) {
552
- throw new Error(`Tab with targetId "${tabid}" not found.`);
617
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
553
618
  }
554
- const page = await target.page();
555
- if (!page) {
556
- throw new Error(`Tab with targetId "${tabid}" not found.`);
619
+ const page2 = await target.page();
620
+ if (!page2) {
621
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
557
622
  }
558
- const closedTabid = await getPageTargetId(page).catch(() => tabid);
559
- await page.close();
623
+ const closedTabid = await getPageTargetId(page2).catch(() => tabid2);
624
+ await page2.close();
560
625
  return {
561
626
  success: true,
562
627
  tabid: closedTabid
563
628
  };
564
629
  } finally {
565
- await browser.disconnect();
630
+ await browser2.disconnect();
566
631
  }
567
632
  }
568
- async function tabsSwitchCommand(tabid) {
569
- const browser = await connectBrowser();
633
+ async function tabsSwitchCommand(tabid2) {
634
+ const browser2 = await connectBrowser();
570
635
  try {
571
- await activateTabById(browser, tabid);
572
- const page = await getTargetPage(browser, tabid);
636
+ await activateTabById(browser2, tabid2);
637
+ const page2 = await getTargetPage(browser2, tabid2);
573
638
  return {
574
639
  success: true,
575
- tabid: await getPageTargetId(page),
576
- url: page.url(),
577
- title: await page.title()
640
+ tabid: await getPageTargetId(page2),
641
+ url: page2.url(),
642
+ title: await page2.title()
578
643
  };
579
644
  } finally {
580
- await browser.disconnect();
645
+ await browser2.disconnect();
581
646
  }
582
647
  }
583
- async function tabsBackCommand(tabid) {
584
- const browser = await connectBrowser();
648
+ async function tabsBackCommand(tabid2) {
649
+ const browser2 = await connectBrowser();
585
650
  try {
586
- const page = await getTargetPage(browser, tabid);
587
- const response = await page.goBack();
651
+ const page2 = await getTargetPage(browser2, tabid2);
652
+ const response = await page2.goBack();
588
653
  if (!response) {
589
654
  return {
590
655
  success: false,
591
656
  error: "\u65E0\u6CD5\u540E\u9000\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u8D77\u70B9",
592
- tabid: await getPageTargetId(page).catch(() => tabid),
593
- url: page.url(),
594
- title: await page.title()
657
+ tabid: await getPageTargetId(page2).catch(() => tabid2),
658
+ url: page2.url(),
659
+ title: await page2.title()
595
660
  };
596
661
  }
597
- await injectIntoPage(page);
662
+ await injectIntoPage(page2);
598
663
  return {
599
664
  success: true,
600
- tabid: await getPageTargetId(page),
601
- url: page.url(),
602
- title: await page.title()
665
+ tabid: await getPageTargetId(page2),
666
+ url: page2.url(),
667
+ title: await page2.title()
603
668
  };
604
669
  } finally {
605
- await browser.disconnect();
670
+ await browser2.disconnect();
606
671
  }
607
672
  }
608
- async function tabsForwardCommand(tabid) {
609
- const browser = await connectBrowser();
673
+ async function tabsForwardCommand(tabid2) {
674
+ const browser2 = await connectBrowser();
610
675
  try {
611
- const page = await getTargetPage(browser, tabid);
612
- const response = await page.goForward();
676
+ const page2 = await getTargetPage(browser2, tabid2);
677
+ const response = await page2.goForward();
613
678
  if (!response) {
614
679
  return {
615
680
  success: false,
616
681
  error: "\u65E0\u6CD5\u524D\u8FDB\uFF1A\u5DF2\u5728\u5386\u53F2\u8BB0\u5F55\u672B\u5C3E",
617
- tabid: await getPageTargetId(page).catch(() => tabid),
618
- url: page.url(),
619
- title: await page.title()
682
+ tabid: await getPageTargetId(page2).catch(() => tabid2),
683
+ url: page2.url(),
684
+ title: await page2.title()
620
685
  };
621
686
  }
622
- await injectIntoPage(page);
687
+ await injectIntoPage(page2);
623
688
  return {
624
689
  success: true,
625
- tabid: await getPageTargetId(page),
626
- url: page.url(),
627
- title: await page.title()
690
+ tabid: await getPageTargetId(page2),
691
+ url: page2.url(),
692
+ title: await page2.title()
628
693
  };
629
694
  } finally {
630
- await browser.disconnect();
695
+ await browser2.disconnect();
631
696
  }
632
697
  }
633
698
 
634
699
  // package.json
635
700
  var package_default = {
636
701
  name: "@opentiny/webmcp-cli",
637
- version: "0.0.1-alpha.2",
702
+ version: "0.0.1-alpha.3",
638
703
  type: "module",
639
704
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
640
705
  main: "dist/index.js",
@@ -671,6 +736,7 @@ var package_default = {
671
736
  "@mcp-b/webmcp-polyfill": "catalog:",
672
737
  "@opentiny/next-sdk": "workspace:^",
673
738
  "@page-agent/page-controller": "catalog:",
739
+ clipboardy: "^5.3.1",
674
740
  commander: "^11.1.0",
675
741
  picocolors: "^1.0.0",
676
742
  "puppeteer-core": "^22.0.0"
@@ -703,61 +769,80 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
703
769
  });
704
770
  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) => {
705
771
  try {
706
- const result = await stateCommand({ tabid: parseTabId(options.tabid) });
707
- console.log(JSON.stringify(result, null, 2));
772
+ const result2 = await stateCommand({ tabid: parseTabId(options.tabid) });
773
+ console.log(JSON.stringify(result2, null, 2));
708
774
  } catch (error) {
709
775
  handleCommandError(error, "state");
710
776
  }
711
777
  });
712
778
  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) => {
713
779
  try {
714
- const result = await runCommand({
780
+ const result2 = await runCommand({
715
781
  toolName,
716
782
  argsJson,
717
783
  tabid: parseTabId(options.tabid)
718
784
  });
719
- console.log(JSON.stringify(result, null, 2));
785
+ console.log(JSON.stringify(result2, null, 2));
720
786
  } catch (error) {
721
787
  handleCommandError(error, "run");
722
788
  }
723
789
  });
790
+ 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) => {
791
+ try {
792
+ const result2 = await evaluateCommand({
793
+ jsScript: jsScript2,
794
+ tabid: parseTabId(options.tabid)
795
+ });
796
+ console.log(JSON.stringify(result2, null, 2));
797
+ } catch (error) {
798
+ handleCommandError(error, "evaluate");
799
+ }
800
+ });
801
+ program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
802
+ try {
803
+ await setClipboard(content);
804
+ console.log(JSON.stringify({ success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" }, null, 2));
805
+ } catch (error) {
806
+ handleCommandError(error, "clipboard");
807
+ }
808
+ });
724
809
  var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
725
810
  tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
726
811
  try {
727
- const result = await tabsOpenCommand(url);
728
- console.log(JSON.stringify(result, null, 2));
812
+ const result2 = await tabsOpenCommand(url);
813
+ console.log(JSON.stringify(result2, null, 2));
729
814
  } catch (error) {
730
815
  handleCommandError(error, "tabs open");
731
816
  }
732
817
  });
733
- tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
818
+ tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
734
819
  try {
735
- const result = await tabsCloseCommand(tabid);
736
- console.log(JSON.stringify(result, null, 2));
820
+ const result2 = await tabsCloseCommand(tabid2);
821
+ console.log(JSON.stringify(result2, null, 2));
737
822
  } catch (error) {
738
823
  handleCommandError(error, "tabs close");
739
824
  }
740
825
  });
741
- tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
826
+ tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
742
827
  try {
743
- const result = await tabsSwitchCommand(tabid);
744
- console.log(JSON.stringify(result, null, 2));
828
+ const result2 = await tabsSwitchCommand(tabid2);
829
+ console.log(JSON.stringify(result2, null, 2));
745
830
  } catch (error) {
746
831
  handleCommandError(error, "tabs switch");
747
832
  }
748
833
  });
749
- tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid) => {
834
+ tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid2) => {
750
835
  try {
751
- const result = await tabsBackCommand(parseTabId(tabid));
752
- console.log(JSON.stringify(result, null, 2));
836
+ const result2 = await tabsBackCommand(parseTabId(tabid2));
837
+ console.log(JSON.stringify(result2, null, 2));
753
838
  } catch (error) {
754
839
  handleCommandError(error, "tabs back");
755
840
  }
756
841
  });
757
- tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid) => {
842
+ tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid2) => {
758
843
  try {
759
- const result = await tabsForwardCommand(parseTabId(tabid));
760
- console.log(JSON.stringify(result, null, 2));
844
+ const result2 = await tabsForwardCommand(parseTabId(tabid2));
845
+ console.log(JSON.stringify(result2, null, 2));
761
846
  } catch (error) {
762
847
  handleCommandError(error, "tabs forward");
763
848
  }