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

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
@@ -1,14 +1,10 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
2
 
9
3
  // src/bin.ts
10
4
  import { Command } from "commander";
11
5
  import pc2 from "picocolors";
6
+ import { readFileSync } from "fs";
7
+ import { resolve } from "path";
12
8
 
13
9
  // src/browser.ts
14
10
  import puppeteer from "puppeteer-core";
@@ -16,20 +12,34 @@ import pc from "picocolors";
16
12
  import fs from "fs";
17
13
  import os from "os";
18
14
  import path from "path";
15
+ import http from "http";
19
16
  import { spawn } from "child_process";
20
17
  import { fileURLToPath } from "url";
21
18
  var __filename = fileURLToPath(import.meta.url);
22
19
  var __dirname = path.dirname(__filename);
23
20
  var CDP_PORT = 9222;
24
21
  var CDP_URL = `http://localhost:${CDP_PORT}`;
25
- async function fetchWithTimeout(url, timeoutMs = 1500) {
26
- const controller = new AbortController();
27
- const id = setTimeout(() => controller.abort(), timeoutMs);
28
- try {
29
- return await fetch(url, { signal: controller.signal });
30
- } finally {
31
- clearTimeout(id);
32
- }
22
+ function httpGet(url, timeoutMs = 1500) {
23
+ return new Promise((resolve2, reject) => {
24
+ const req = http.get(url, { timeout: timeoutMs }, (res) => {
25
+ let data = "";
26
+ res.on("data", (chunk) => {
27
+ data += chunk.toString();
28
+ });
29
+ res.on("end", () => {
30
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
31
+ resolve2(data);
32
+ } else {
33
+ reject(new Error(`HTTP ${res.statusCode}`));
34
+ }
35
+ });
36
+ });
37
+ req.on("error", reject);
38
+ req.on("timeout", () => {
39
+ req.destroy();
40
+ reject(new Error("request timeout"));
41
+ });
42
+ });
33
43
  }
34
44
  function promiseWithTimeout(promise, timeoutMs = 5e3, errorMsg = "Operation timed out") {
35
45
  let timeoutId;
@@ -45,8 +55,8 @@ function promiseWithTimeout(promise, timeoutMs = 5e3, errorMsg = "Operation time
45
55
  async function checkCdpReady(url, retries = 3) {
46
56
  for (let i = 0; i < retries; i++) {
47
57
  try {
48
- const res = await fetchWithTimeout(url, 1500);
49
- if (res.ok) return true;
58
+ await httpGet(url, 1500);
59
+ return true;
50
60
  } catch {
51
61
  }
52
62
  if (i < retries - 1) {
@@ -55,70 +65,54 @@ async function checkCdpReady(url, retries = 3) {
55
65
  }
56
66
  return false;
57
67
  }
58
- async function killProcessOnPortIfZombie(port) {
59
- const isResponding = await checkCdpReady(`http://127.0.0.1:${port}/json/version`, 1);
60
- if (isResponding) {
61
- console.log(pc.green(`connectBrowser: \u7AEF\u53E3 ${port} \u4E0A\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B\u4ECD\u5728\u6B63\u5E38\u54CD\u5E94\uFF0C\u8DF3\u8FC7\u5F3A\u6740\uFF0C\u5C1D\u8BD5\u76F4\u63A5\u63A5\u7BA1\u3002`));
62
- return;
63
- }
64
- try {
65
- const platform = os.platform();
66
- const { execSync } = __require("child_process");
67
- if (platform === "darwin" || platform === "linux") {
68
- console.log(pc.yellow(`\u6B63\u5728\u68C0\u6D4B\u5E76\u6E05\u7406\u5360\u7528 ${port} \u7AEF\u53E3\u7684\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B...`));
69
- const pids = execSync(`lsof -t -i :${port}`).toString().trim();
70
- if (pids) {
71
- console.log(pc.yellow(`\u53D1\u73B0\u50F5\u5C38 PID: ${pids.split("\n").join(", ")}\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`));
72
- execSync(`kill -9 ${pids.split("\n").join(" ")}`);
73
- console.log(pc.green(`\u6210\u529F\u6E05\u7406\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B`));
74
- }
75
- } else if (platform === "win32") {
76
- console.log(pc.yellow(`\u6B63\u5728\u68C0\u6D4B\u5E76\u6E05\u7406 Windows \u4E0A\u5360\u7528 ${port} \u7AEF\u53E3\u7684\u6B8B\u7559\u50F5\u5C38\u8FDB\u7A0B...`));
77
- const output = execSync(`netstat -ano | findstr :${port}`).toString().trim();
78
- if (output) {
79
- const lines = output.split("\n");
80
- const pids = /* @__PURE__ */ new Set();
81
- lines.forEach((line) => {
82
- const parts = line.trim().split(/\s+/);
83
- const pid = parts[parts.length - 1];
84
- if (pid && /^\d+$/.test(pid) && pid !== "0") {
85
- pids.add(pid);
86
- }
87
- });
88
- pids.forEach((pid) => {
89
- console.log(pc.yellow(`\u53D1\u73B0 Windows \u6B8B\u7559\u50F5\u5C38 PID: ${pid}\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`));
90
- execSync(`taskkill /F /PID ${pid}`);
91
- });
92
- }
93
- }
94
- } catch (e) {
95
- }
96
- }
97
- function getDefaultChromePath() {
68
+ function getDefaultBrowserPath() {
98
69
  const platform = os.platform();
99
70
  if (platform === "darwin") {
100
- return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
71
+ const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
72
+ if (fs.existsSync(chromePath)) return { path: chromePath, name: "Chrome" };
73
+ const edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
74
+ if (fs.existsSync(edgePath)) return { path: edgePath, name: "Edge" };
75
+ return null;
101
76
  } else if (platform === "win32") {
102
- const paths = [
77
+ const chromePaths = [
103
78
  process.env.LOCALAPPDATA + "\\Google\\Chrome\\Application\\chrome.exe",
104
79
  process.env.PROGRAMFILES + "\\Google\\Chrome\\Application\\chrome.exe",
105
80
  process.env["PROGRAMFILES(X86)"] + "\\Google\\Chrome\\Application\\chrome.exe"
106
81
  ];
107
- return paths.find((p) => fs.existsSync(p)) || null;
82
+ const foundChrome = chromePaths.find((p) => fs.existsSync(p));
83
+ if (foundChrome) return { path: foundChrome, name: "Chrome" };
84
+ const edgePaths = [
85
+ process.env.LOCALAPPDATA + "\\Microsoft\\Edge\\Application\\msedge.exe",
86
+ process.env.PROGRAMFILES + "\\Microsoft\\Edge\\Application\\msedge.exe",
87
+ process.env["PROGRAMFILES(X86)"] + "\\Microsoft\\Edge\\Application\\msedge.exe",
88
+ "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
89
+ "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe"
90
+ ];
91
+ const foundEdge = edgePaths.find((p) => fs.existsSync(p));
92
+ if (foundEdge) return { path: foundEdge, name: "Edge" };
93
+ return null;
108
94
  } 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;
95
+ const paths = [
96
+ { p: "/usr/bin/google-chrome", name: "Chrome" },
97
+ { p: "/usr/bin/google-chrome-stable", name: "Chrome" },
98
+ { p: "/usr/bin/chromium", name: "Chromium" },
99
+ { p: "/usr/bin/chromium-browser", name: "Chromium" },
100
+ { p: "/usr/bin/microsoft-edge", name: "Edge" },
101
+ { p: "/usr/bin/microsoft-edge-stable", name: "Edge" }
102
+ ];
103
+ const found = paths.find(({ p }) => fs.existsSync(p));
104
+ return found ? { path: found.p, name: found.name } : null;
111
105
  }
112
106
  }
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");
107
+ async function startBrowserInBackground() {
108
+ const browserInfo = getDefaultBrowserPath();
109
+ if (!browserInfo || !fs.existsSync(browserInfo.path)) {
110
+ 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
111
  }
118
- console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 Chrome \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
112
+ console.log(pc.yellow(`\u6B63\u5728\u542F\u52A8\u540E\u53F0 ${browserInfo.name} \u5B9E\u4F8B (\u7AEF\u53E3: ${CDP_PORT})...`));
119
113
  const userDataDir = process.env.WEBMCP_WORKSPACE || path.join(os.homedir(), ".webmcp_chrome_profile");
120
114
  const child = spawn(
121
- chromePath,
115
+ browserInfo.path,
122
116
  [
123
117
  `--remote-debugging-port=${CDP_PORT}`,
124
118
  `--user-data-dir=${userDataDir}`,
@@ -133,22 +127,20 @@ async function startChromeInBackground() {
133
127
  child.unref();
134
128
  for (let i = 0; i < 20; i++) {
135
129
  try {
136
- const urls = [`http://localhost:${CDP_PORT}/json/version`, `http://127.0.0.1:${CDP_PORT}/json/version`];
130
+ const urls = [`http://127.0.0.1:${CDP_PORT}/json/version`, `http://localhost:${CDP_PORT}/json/version`];
137
131
  for (const url of urls) {
138
132
  try {
139
- const response = await fetchWithTimeout(url, 1e3);
140
- if (response.ok) {
141
- console.log(pc.green("Chrome \u542F\u52A8\u5E76\u5C31\u7EEA\u3002"));
142
- return;
143
- }
133
+ await httpGet(url, 1e3);
134
+ console.log(pc.green(`${browserInfo.name} \u542F\u52A8\u5E76\u5C31\u7EEA\u3002`));
135
+ return;
144
136
  } catch (err) {
145
137
  }
146
138
  }
147
139
  } catch (e) {
148
140
  }
149
- await new Promise((resolve) => setTimeout(resolve, 500));
141
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
150
142
  }
151
- throw new Error("Chrome \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002");
143
+ throw new Error(`${browserInfo.name} \u542F\u52A8\u8D85\u65F6\uFF0C\u65E0\u6CD5\u8FDE\u63A5\u5230 CDP \u7AEF\u53E3\u3002`);
152
144
  }
153
145
  async function connectBrowser() {
154
146
  const targetFilter = (target) => {
@@ -167,93 +159,54 @@ async function connectBrowser() {
167
159
  return false;
168
160
  }
169
161
  };
170
- try {
171
- const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
172
- if (!is127Ready) {
173
- throw new Error("127.0.0.1 CDP port not responding");
162
+ const portAddresses = [
163
+ `http://127.0.0.1:${CDP_PORT}/json/version`,
164
+ `http://localhost:${CDP_PORT}/json/version`
165
+ ];
166
+ for (const addr of portAddresses) {
167
+ const isReady = await checkCdpReady(addr, 2);
168
+ if (!isReady) continue;
169
+ const browserURL = addr.replace("/json/version", "");
170
+ try {
171
+ console.log(pc.yellow(`connectBrowser: \u68C0\u6D4B\u5230\u7AEF\u53E3 ${CDP_PORT} \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
172
+ const browser2 = await promiseWithTimeout(
173
+ puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
174
+ 1e4,
175
+ `puppeteer.connect to ${browserURL} timed out`
176
+ );
177
+ console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
178
+ return browser2;
179
+ } catch {
174
180
  }
175
- console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 127.0.0.1:9222..."));
176
- const browser = await promiseWithTimeout(
177
- puppeteer.connect({
178
- browserURL: `http://127.0.0.1:${CDP_PORT}`,
179
- defaultViewport: null,
180
- targetFilter
181
- }),
182
- 1e4,
183
- "puppeteer.connect to 127.0.0.1 timed out"
184
- );
185
- console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
186
- return browser;
187
- } catch (error) {
181
+ }
182
+ console.log(pc.yellow(`connectBrowser: \u7AEF\u53E3 ${CDP_PORT} \u65E0\u54CD\u5E94\uFF0C\u6B63\u5728\u542F\u52A8\u65B0\u7684\u6D4F\u89C8\u5668\u5B9E\u4F8B...`));
183
+ try {
184
+ await startBrowserInBackground();
185
+ } catch (launchError) {
186
+ const msg = launchError instanceof Error ? launchError.message : String(launchError);
187
+ console.error(pc.red(`\u65E0\u6CD5\u542F\u52A8\u6D4F\u89C8\u5668: ${msg}`));
188
+ throw new Error("Browser launch failed.");
189
+ }
190
+ for (const addr of portAddresses) {
191
+ const isReady = await checkCdpReady(addr, 3);
192
+ if (!isReady) continue;
193
+ const browserURL = addr.replace("/json/version", "");
188
194
  try {
189
- const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
190
- if (!isLocalhostReady) {
191
- throw new Error("localhost CDP port not responding");
192
- }
193
- console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
194
- const browser = await promiseWithTimeout(
195
- puppeteer.connect({
196
- browserURL: `http://localhost:${CDP_PORT}`,
197
- defaultViewport: null,
198
- targetFilter
199
- }),
195
+ console.log(pc.yellow(`connectBrowser: \u6D4F\u89C8\u5668\u5DF2\u542F\u52A8\uFF0C\u6B63\u5728\u8FDE\u63A5 ${browserURL}...`));
196
+ const browser2 = await promiseWithTimeout(
197
+ puppeteer.connect({ browserURL, defaultViewport: null, targetFilter }),
200
198
  1e4,
201
- "puppeteer.connect to localhost timed out"
199
+ `puppeteer.connect to ${browserURL} after launch timed out`
202
200
  );
203
- console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
204
- return browser;
205
- } catch (error2) {
206
- 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
- try {
208
- await killProcessOnPortIfZombie(CDP_PORT);
209
- await startChromeInBackground();
210
- try {
211
- const is127Ready = await checkCdpReady(`http://127.0.0.1:${CDP_PORT}/json/version`, 3);
212
- if (!is127Ready) {
213
- throw new Error("127.0.0.1 CDP port not responding after launch");
214
- }
215
- 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(
217
- puppeteer.connect({
218
- browserURL: `http://127.0.0.1:${CDP_PORT}`,
219
- defaultViewport: null,
220
- targetFilter
221
- }),
222
- 1e4,
223
- "puppeteer.connect to 127.0.0.1 after launch timed out"
224
- );
225
- console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 127.0.0.1:9222"));
226
- return browser;
227
- } catch (e) {
228
- const isLocalhostReady = await checkCdpReady(`http://localhost:${CDP_PORT}/json/version`, 3);
229
- if (!isLocalhostReady) {
230
- throw new Error("localhost CDP port not responding after launch");
231
- }
232
- console.log(pc.yellow("connectBrowser: \u6B63\u5728\u5C1D\u8BD5\u8FDE\u63A5 localhost:9222..."));
233
- const browser = await promiseWithTimeout(
234
- puppeteer.connect({
235
- browserURL: `http://localhost:${CDP_PORT}`,
236
- defaultViewport: null,
237
- targetFilter
238
- }),
239
- 1e4,
240
- "puppeteer.connect to localhost after launch timed out"
241
- );
242
- console.log(pc.green("connectBrowser: \u6210\u529F\u8FDE\u63A5 localhost:9222"));
243
- return browser;
244
- }
245
- } catch (launchError) {
246
- const msg = launchError instanceof Error ? launchError.message : String(launchError);
247
- 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`));
250
- throw new Error("Browser connection failed.");
251
- }
201
+ console.log(pc.green(`connectBrowser: \u6210\u529F\u8FDE\u63A5 ${browserURL}`));
202
+ return browser2;
203
+ } catch {
252
204
  }
253
205
  }
206
+ 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`);
254
207
  }
255
- async function getPageTargetId(page) {
256
- const session = await page.target().createCDPSession();
208
+ async function getPageTargetId(page2) {
209
+ const session = await page2.target().createCDPSession();
257
210
  try {
258
211
  const { targetInfo } = await session.send("Target.getTargetInfo");
259
212
  return targetInfo.targetId;
@@ -262,8 +215,8 @@ async function getPageTargetId(page) {
262
215
  });
263
216
  }
264
217
  }
265
- function getPageTargets(browser) {
266
- return browser.targets().filter((t) => {
218
+ function getPageTargets(browser2) {
219
+ return browser2.targets().filter((t) => {
267
220
  try {
268
221
  const type = (typeof t.type === "function" ? t.type() : t.type) || "";
269
222
  const url = (typeof t.url === "function" ? t.url() : t.url) || "";
@@ -276,26 +229,26 @@ function getPageTargets(browser) {
276
229
  function getTargetIdFromTarget(target) {
277
230
  return typeof target._getTargetInfo === "function" ? target._getTargetInfo().targetId : target._targetId || target.targetId || "";
278
231
  }
279
- function findPageTargetByTabId(browser, tabid) {
280
- const pageTargets = getPageTargets(browser);
232
+ function findPageTargetByTabId(browser2, tabid2) {
233
+ const pageTargets = getPageTargets(browser2);
281
234
  return pageTargets.find((t) => {
282
235
  const tid = getTargetIdFromTarget(t);
283
- return tid === tabid || tid.includes(tabid);
236
+ return tid === tabid2 || tid.includes(tabid2);
284
237
  }) ?? null;
285
238
  }
286
- async function activateTabById(browser, tabid) {
287
- const target = findPageTargetByTabId(browser, tabid);
239
+ async function activateTabById(browser2, tabid2) {
240
+ const target = findPageTargetByTabId(browser2, tabid2);
288
241
  if (!target) {
289
- throw new Error(`Tab with targetId "${tabid}" not found.`);
242
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
290
243
  }
291
244
  const realTabId = getTargetIdFromTarget(target);
292
- const pages = await browser.pages();
245
+ const pages = await browser2.pages();
293
246
  let sessionPage = pages.find((p) => !p.url().startsWith("devtools://"));
294
247
  if (!sessionPage && pages.length > 0) {
295
248
  sessionPage = pages[0];
296
249
  }
297
250
  if (!sessionPage) {
298
- sessionPage = await browser.newPage();
251
+ sessionPage = await browser2.newPage();
299
252
  }
300
253
  const session = await sessionPage.createCDPSession();
301
254
  try {
@@ -305,18 +258,18 @@ async function activateTabById(browser, tabid) {
305
258
  });
306
259
  }
307
260
  }
308
- async function getTargetPage(browser, tabid) {
309
- const pageTargets = getPageTargets(browser);
261
+ async function getTargetPage(browser2, tabid2) {
262
+ const pageTargets = getPageTargets(browser2);
310
263
  if (pageTargets.length === 0) {
311
- const newPage = await browser.newPage();
264
+ const newPage = await browser2.newPage();
312
265
  await injectWebMCPPolyfillAndTools(newPage);
313
266
  return newPage;
314
267
  }
315
268
  let targetPage = null;
316
- if (tabid !== void 0) {
317
- const target = findPageTargetByTabId(browser, tabid);
269
+ if (tabid2 !== void 0) {
270
+ const target = findPageTargetByTabId(browser2, tabid2);
318
271
  if (!target) {
319
- throw new Error(`Tab with targetId "${tabid}" not found.`);
272
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
320
273
  }
321
274
  targetPage = await target.page();
322
275
  } else {
@@ -328,14 +281,12 @@ async function getTargetPage(browser, tabid) {
328
281
  let activeTargetId = null;
329
282
  for (const url of urls) {
330
283
  try {
331
- const res = await fetchWithTimeout(url, 1e3);
332
- if (res.ok) {
333
- const targetsData = await res.json();
334
- const active = targetsData.find((t) => t.type === "page" && !t.url.startsWith("devtools://"));
335
- if (active) {
336
- activeTargetId = active.id;
337
- break;
338
- }
284
+ const body = await httpGet(url, 1e3);
285
+ const targetsData = JSON.parse(body);
286
+ const active = targetsData.find((t) => t.type === "page" && !t.url.startsWith("devtools://"));
287
+ if (active) {
288
+ activeTargetId = active.id;
289
+ break;
339
290
  }
340
291
  } catch {
341
292
  }
@@ -361,11 +312,11 @@ async function getTargetPage(browser, tabid) {
361
312
  await injectWebMCPPolyfillAndTools(targetPage);
362
313
  return targetPage;
363
314
  }
364
- async function injectIntoPage(page) {
365
- await injectWebMCPPolyfillAndTools(page, true);
315
+ async function injectIntoPage(page2) {
316
+ await injectWebMCPPolyfillAndTools(page2, true);
366
317
  }
367
- async function injectWebMCPPolyfillAndTools(page, force = false) {
368
- const polyfillReady = !force && await page.evaluate(() => {
318
+ async function injectWebMCPPolyfillAndTools(page2, force = false) {
319
+ const polyfillReady = !force && await page2.evaluate(() => {
369
320
  return !!window.__webmcpcli_init;
370
321
  }).catch(() => false);
371
322
  if (!polyfillReady) {
@@ -376,19 +327,19 @@ async function injectWebMCPPolyfillAndTools(page, force = false) {
376
327
  }
377
328
  const scriptContent = fs.readFileSync(injectScriptPath, "utf-8");
378
329
  try {
379
- await page.evaluate(scriptContent);
330
+ await page2.evaluate(scriptContent);
380
331
  } catch (err) {
381
332
  const msg = err instanceof Error ? err.message : String(err);
382
333
  throw new Error("\u81EA\u52A8\u6CE8\u5165\u811A\u672C\u6267\u884C\u5931\u8D25: " + msg);
383
334
  }
384
- await new Promise((resolve) => setTimeout(resolve, 300));
335
+ await new Promise((resolve2) => setTimeout(resolve2, 300));
385
336
  }
386
- await injectDomainTools(page);
337
+ await injectDomainTools(page2);
387
338
  }
388
- async function injectDomainTools(page) {
339
+ async function injectDomainTools(page2) {
389
340
  let hostname;
390
341
  try {
391
- const url = new URL(page.url());
342
+ const url = new URL(page2.url());
392
343
  hostname = url.hostname;
393
344
  } catch {
394
345
  return;
@@ -400,7 +351,7 @@ async function injectDomainTools(page) {
400
351
  console.log(pc.cyan(`\u68C0\u6D4B\u5230\u57DF\u540D ${hostname} \u6709\u9884\u7F6E\u5DE5\u5177\uFF0C\u6B63\u5728\u6CE8\u5165...`));
401
352
  const toolScript = fs.readFileSync(toolBundlePath, "utf-8");
402
353
  try {
403
- await page.evaluate(toolScript);
354
+ await page2.evaluate(toolScript);
404
355
  console.log(pc.green(`\u5DF2\u4E3A ${hostname} \u6CE8\u5165\u9884\u7F6E\u5DE5\u5177`));
405
356
  } catch (err) {
406
357
  const msg = err instanceof Error ? err.message : String(err);
@@ -409,11 +360,12 @@ async function injectDomainTools(page) {
409
360
  }
410
361
 
411
362
  // src/commands/state.ts
412
- async function stateCommand({ tabid }) {
413
- const browser = await connectBrowser();
363
+ async function stateCommand({ tabid: tabid2 }) {
364
+ const browser2 = await connectBrowser();
414
365
  try {
415
- const page = await getTargetPage(browser, tabid);
416
- const state = await page.evaluate(async () => {
366
+ const page2 = await getTargetPage(browser2, tabid2);
367
+ await injectIntoPage(page2);
368
+ const state = await page2.evaluate(async () => {
417
369
  const url = document.URL;
418
370
  const title = document.title;
419
371
  const mcp = navigator.modelContextTesting || navigator.modelContext;
@@ -461,27 +413,29 @@ async function stateCommand({ tabid }) {
461
413
  webmcpTools
462
414
  };
463
415
  });
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
- }));
416
+ const pages = await browser2.pages();
417
+ const tabs2 = await Promise.all(
418
+ pages.map(async (p) => {
419
+ const pUrl = p.url();
420
+ if (pUrl.startsWith("devtools://")) return null;
421
+ const pTitle = await Promise.race([
422
+ p.title().catch(() => "Unknown"),
423
+ new Promise((resolve2) => setTimeout(() => resolve2("Unknown"), 500))
424
+ ]);
425
+ return {
426
+ // 使用真实的 Chrome target ID,而非数组下标
427
+ tabid: await getPageTargetId(p).catch(() => pUrl),
428
+ title: pTitle,
429
+ url: pUrl
430
+ };
431
+ })
432
+ );
479
433
  return {
480
434
  ...state,
481
435
  tabs: tabs2.filter(Boolean)
482
436
  };
483
437
  } finally {
484
- await browser.disconnect();
438
+ await browser2.disconnect();
485
439
  }
486
440
  }
487
441
 
@@ -489,17 +443,17 @@ async function stateCommand({ tabid }) {
489
443
  async function runCommand({
490
444
  toolName,
491
445
  argsJson,
492
- tabid
446
+ tabid: tabid2
493
447
  }) {
494
- const browser = await connectBrowser();
448
+ const browser2 = await connectBrowser();
495
449
  try {
496
- const page = await getTargetPage(browser, tabid);
450
+ const page2 = await getTargetPage(browser2, tabid2);
497
451
  try {
498
452
  JSON.parse(argsJson);
499
453
  } catch (e) {
500
454
  throw new Error(`\u53C2\u6570\u4E0D\u662F\u6709\u6548\u7684 JSON: ${e.message}`);
501
455
  }
502
- const result = await page.evaluate(async (name, inputString) => {
456
+ const result2 = await page2.evaluate(async (name, inputString) => {
503
457
  const mcp = navigator.modelContextTesting || navigator.modelContext;
504
458
  if (!mcp || typeof mcp.executeTool !== "function") {
505
459
  throw new Error("\u5F53\u524D\u9875\u9762\u6CA1\u6709\u6CE8\u5165 WebMCP \u73AF\u5883 (navigator.modelContextTesting.executeTool \u672A\u627E\u5230)");
@@ -513,12 +467,51 @@ async function runCommand({
513
467
  }
514
468
  return res;
515
469
  }, toolName, argsJson);
470
+ return result2;
471
+ } finally {
472
+ await browser2.disconnect();
473
+ }
474
+ }
475
+
476
+ // src/commands/evaluate.ts
477
+ async function evaluateCommand({
478
+ jsScript,
479
+ tabid
480
+ }) {
481
+ const browser = await connectBrowser();
482
+ try {
483
+ const page = await getTargetPage(browser, tabid);
484
+ const result = await page.evaluate(async (script) => {
485
+ try {
486
+ const result = eval(script);
487
+ if (result instanceof Promise) {
488
+ return await result;
489
+ }
490
+ return result;
491
+ } catch (error) {
492
+ return {
493
+ error: true,
494
+ message: error.message,
495
+ stack: error.stack
496
+ };
497
+ }
498
+ }, jsScript);
516
499
  return result;
517
500
  } finally {
518
501
  await browser.disconnect();
519
502
  }
520
503
  }
521
504
 
505
+ // src/commands/clipboard.ts
506
+ import clipboard from "clipboardy";
507
+ async function setClipboard(content) {
508
+ try {
509
+ await clipboard.write(content);
510
+ } catch (error) {
511
+ throw new Error(`\u8BBE\u7F6E\u526A\u8D34\u677F\u5931\u8D25: ${error.message}`);
512
+ }
513
+ }
514
+
522
515
  // src/commands/tabs.ts
523
516
  function normalizeUrl(url) {
524
517
  if (!url.startsWith("http://") && !url.startsWith("https://")) {
@@ -527,114 +520,114 @@ function normalizeUrl(url) {
527
520
  return url;
528
521
  }
529
522
  async function tabsOpenCommand(url) {
530
- const browser = await connectBrowser();
523
+ const browser2 = await connectBrowser();
531
524
  try {
532
- const page = await browser.newPage();
525
+ const page2 = await browser2.newPage();
533
526
  url = normalizeUrl(url);
534
- await page.goto(url, { waitUntil: "domcontentloaded" });
535
- await injectIntoPage(page);
536
- await new Promise((resolve) => setTimeout(resolve, 500));
527
+ await page2.goto(url, { waitUntil: "domcontentloaded" });
528
+ await injectIntoPage(page2);
529
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
537
530
  return {
538
531
  success: true,
539
- tabid: await getPageTargetId(page),
540
- url: page.url(),
541
- title: await page.title()
532
+ tabid: await getPageTargetId(page2),
533
+ url: page2.url(),
534
+ title: await page2.title()
542
535
  };
543
536
  } finally {
544
- await browser.disconnect();
537
+ await browser2.disconnect();
545
538
  }
546
539
  }
547
- async function tabsCloseCommand(tabid) {
548
- const browser = await connectBrowser();
540
+ async function tabsCloseCommand(tabid2) {
541
+ const browser2 = await connectBrowser();
549
542
  try {
550
- const target = findPageTargetByTabId(browser, tabid);
543
+ const target = findPageTargetByTabId(browser2, tabid2);
551
544
  if (!target) {
552
- throw new Error(`Tab with targetId "${tabid}" not found.`);
545
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
553
546
  }
554
- const page = await target.page();
555
- if (!page) {
556
- throw new Error(`Tab with targetId "${tabid}" not found.`);
547
+ const page2 = await target.page();
548
+ if (!page2) {
549
+ throw new Error(`Tab with targetId "${tabid2}" not found.`);
557
550
  }
558
- const closedTabid = await getPageTargetId(page).catch(() => tabid);
559
- await page.close();
551
+ const closedTabid = await getPageTargetId(page2).catch(() => tabid2);
552
+ await page2.close();
560
553
  return {
561
554
  success: true,
562
555
  tabid: closedTabid
563
556
  };
564
557
  } finally {
565
- await browser.disconnect();
558
+ await browser2.disconnect();
566
559
  }
567
560
  }
568
- async function tabsSwitchCommand(tabid) {
569
- const browser = await connectBrowser();
561
+ async function tabsSwitchCommand(tabid2) {
562
+ const browser2 = await connectBrowser();
570
563
  try {
571
- await activateTabById(browser, tabid);
572
- const page = await getTargetPage(browser, tabid);
564
+ await activateTabById(browser2, tabid2);
565
+ const page2 = await getTargetPage(browser2, tabid2);
573
566
  return {
574
567
  success: true,
575
- tabid: await getPageTargetId(page),
576
- url: page.url(),
577
- title: await page.title()
568
+ tabid: await getPageTargetId(page2),
569
+ url: page2.url(),
570
+ title: await page2.title()
578
571
  };
579
572
  } finally {
580
- await browser.disconnect();
573
+ await browser2.disconnect();
581
574
  }
582
575
  }
583
- async function tabsBackCommand(tabid) {
584
- const browser = await connectBrowser();
576
+ async function tabsBackCommand(tabid2) {
577
+ const browser2 = await connectBrowser();
585
578
  try {
586
- const page = await getTargetPage(browser, tabid);
587
- const response = await page.goBack();
579
+ const page2 = await getTargetPage(browser2, tabid2);
580
+ const response = await page2.goBack();
588
581
  if (!response) {
589
582
  return {
590
583
  success: false,
591
584
  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()
585
+ tabid: await getPageTargetId(page2).catch(() => tabid2),
586
+ url: page2.url(),
587
+ title: await page2.title()
595
588
  };
596
589
  }
597
- await injectIntoPage(page);
590
+ await injectIntoPage(page2);
598
591
  return {
599
592
  success: true,
600
- tabid: await getPageTargetId(page),
601
- url: page.url(),
602
- title: await page.title()
593
+ tabid: await getPageTargetId(page2),
594
+ url: page2.url(),
595
+ title: await page2.title()
603
596
  };
604
597
  } finally {
605
- await browser.disconnect();
598
+ await browser2.disconnect();
606
599
  }
607
600
  }
608
- async function tabsForwardCommand(tabid) {
609
- const browser = await connectBrowser();
601
+ async function tabsForwardCommand(tabid2) {
602
+ const browser2 = await connectBrowser();
610
603
  try {
611
- const page = await getTargetPage(browser, tabid);
612
- const response = await page.goForward();
604
+ const page2 = await getTargetPage(browser2, tabid2);
605
+ const response = await page2.goForward();
613
606
  if (!response) {
614
607
  return {
615
608
  success: false,
616
609
  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()
610
+ tabid: await getPageTargetId(page2).catch(() => tabid2),
611
+ url: page2.url(),
612
+ title: await page2.title()
620
613
  };
621
614
  }
622
- await injectIntoPage(page);
615
+ await injectIntoPage(page2);
623
616
  return {
624
617
  success: true,
625
- tabid: await getPageTargetId(page),
626
- url: page.url(),
627
- title: await page.title()
618
+ tabid: await getPageTargetId(page2),
619
+ url: page2.url(),
620
+ title: await page2.title()
628
621
  };
629
622
  } finally {
630
- await browser.disconnect();
623
+ await browser2.disconnect();
631
624
  }
632
625
  }
633
626
 
634
627
  // package.json
635
628
  var package_default = {
636
629
  name: "@opentiny/webmcp-cli",
637
- version: "0.0.1-alpha.2",
630
+ version: "0.0.1-alpha.4",
638
631
  type: "module",
639
632
  description: "WebMCP CLI for AI Agents to interact with browser via Puppeteer",
640
633
  main: "dist/index.js",
@@ -671,6 +664,7 @@ var package_default = {
671
664
  "@mcp-b/webmcp-polyfill": "catalog:",
672
665
  "@opentiny/next-sdk": "workspace:^",
673
666
  "@page-agent/page-controller": "catalog:",
667
+ clipboardy: "^5.3.1",
674
668
  commander: "^11.1.0",
675
669
  picocolors: "^1.0.0",
676
670
  "puppeteer-core": "^22.0.0"
@@ -695,6 +689,19 @@ function handleCommandError(error, commandName) {
695
689
  console.error(pc2.red(`Error executing ${commandName} command: ${msg}`));
696
690
  process.exit(1);
697
691
  }
692
+ function expandFileRefs(argsJson) {
693
+ return argsJson.replace(/"@(base64)?file:([^"]+)"/g, (_match, base64Flag, filePath) => {
694
+ const absPath = resolve(process.cwd(), filePath);
695
+ let content;
696
+ try {
697
+ content = readFileSync(absPath, "utf-8");
698
+ } catch (e) {
699
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5F15\u7528 "${filePath}": ${e.message}`);
700
+ }
701
+ const value = base64Flag ? Buffer.from(content, "utf-8").toString("base64") : content;
702
+ return JSON.stringify(value);
703
+ });
704
+ }
698
705
  program.name("webmcp-cli").description("WebMCP CLI for interacting with browser via CDP").version(package_default.version).option("-w, --workspace <path>", "\u6307\u5B9A\u81EA\u5B9A\u4E49\u7684\u6D4F\u89C8\u5668\u5DE5\u4F5C\u7A7A\u95F4\uFF08\u7528\u6237\u914D\u7F6E\u76EE\u5F55\uFF09\u8DEF\u5F84").hook("preAction", (thisCommand) => {
699
706
  const opts = thisCommand.opts();
700
707
  if (opts.workspace) {
@@ -703,61 +710,100 @@ program.name("webmcp-cli").description("WebMCP CLI for interacting with browser
703
710
  });
704
711
  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
712
  try {
706
- const result = await stateCommand({ tabid: parseTabId(options.tabid) });
707
- console.log(JSON.stringify(result, null, 2));
713
+ const result2 = await stateCommand({ tabid: parseTabId(options.tabid) });
714
+ console.log(JSON.stringify(result2, null, 2));
708
715
  } catch (error) {
709
716
  handleCommandError(error, "state");
710
717
  }
711
718
  });
712
- 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) => {
719
+ program.command("run <toolName> [argsJson]").description("\u5411\u6307\u5B9A\u9875\u7B7E\u8C03\u7528\u6307\u5B9A\u7684 WebMCP \u5DE5\u5177\u6267\u884C\u64CD\u4F5C").option("-t, --tabid <id>", "\u6307\u5B9A\u9875\u7B7E\u7684 ID").option(
720
+ "-f, --file <path>",
721
+ `\u4ECE\u6307\u5B9A .json \u6587\u4EF6\u8BFB\u53D6\u6574\u4E2A argsJson\uFF08\u6587\u4EF6\u5185\u5BB9\u5373\u4E3A\u53C2\u6570 JSON\uFF09\u3002
722
+ \u5982\u9700\u5728 argsJson \u4E2D\u5185\u8054\u5F15\u7528\u6587\u4EF6\uFF0C\u4F7F\u7528\u5360\u4F4D\u7B26\u8BED\u6CD5\uFF1A
723
+ @file:<path> \u8BFB\u53D6\u6587\u4EF6\u539F\u59CB\u6587\u672C
724
+ @base64file:<path> \u8BFB\u53D6\u6587\u4EF6\u5E76 Base64 \u7F16\u7801
725
+ \u793A\u4F8B\uFF1Awebmcp-cli run mytool '{"content":"@base64file:./doc.md"}'`
726
+ ).action(async (toolName, argsJson, options) => {
713
727
  try {
714
- const result = await runCommand({
728
+ let finalArgsJson = argsJson;
729
+ if (options.file) {
730
+ const filePath = resolve(process.cwd(), options.file);
731
+ try {
732
+ finalArgsJson = readFileSync(filePath, "utf-8").trim();
733
+ } catch (e) {
734
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 "${filePath}": ${e.message}`);
735
+ }
736
+ }
737
+ if (!finalArgsJson) {
738
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B argsJson \u53C2\u6570\u6216\u901A\u8FC7 -f/--file \u6307\u5B9A\u53C2\u6570\u6587\u4EF6");
739
+ }
740
+ finalArgsJson = expandFileRefs(finalArgsJson);
741
+ const result2 = await runCommand({
715
742
  toolName,
716
- argsJson,
743
+ argsJson: finalArgsJson,
717
744
  tabid: parseTabId(options.tabid)
718
745
  });
719
- console.log(JSON.stringify(result, null, 2));
746
+ console.log(JSON.stringify(result2, null, 2));
720
747
  } catch (error) {
721
748
  handleCommandError(error, "run");
722
749
  }
723
750
  });
751
+ 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) => {
752
+ try {
753
+ const result2 = await evaluateCommand({
754
+ jsScript: jsScript2,
755
+ tabid: parseTabId(options.tabid)
756
+ });
757
+ console.log(JSON.stringify(result2, null, 2));
758
+ } catch (error) {
759
+ handleCommandError(error, "evaluate");
760
+ }
761
+ });
762
+ program.command("clipboard <content>").description("\u5C06\u5185\u5BB9\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F").action(async (content) => {
763
+ try {
764
+ await setClipboard(content);
765
+ console.log(JSON.stringify({ success: true, message: "\u5185\u5BB9\u5DF2\u8BBE\u7F6E\u5230\u7CFB\u7EDF\u526A\u8D34\u677F" }, null, 2));
766
+ } catch (error) {
767
+ handleCommandError(error, "clipboard");
768
+ }
769
+ });
724
770
  var tabs = program.command("tabs").description("\u7BA1\u7406\u6D4F\u89C8\u5668\u6807\u7B7E\u9875");
725
771
  tabs.command("open <url>").description("\u6253\u5F00\u65B0\u7F51\u9875").action(async (url) => {
726
772
  try {
727
- const result = await tabsOpenCommand(url);
728
- console.log(JSON.stringify(result, null, 2));
773
+ const result2 = await tabsOpenCommand(url);
774
+ console.log(JSON.stringify(result2, null, 2));
729
775
  } catch (error) {
730
776
  handleCommandError(error, "tabs open");
731
777
  }
732
778
  });
733
- tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
779
+ tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
734
780
  try {
735
- const result = await tabsCloseCommand(tabid);
736
- console.log(JSON.stringify(result, null, 2));
781
+ const result2 = await tabsCloseCommand(tabid2);
782
+ console.log(JSON.stringify(result2, null, 2));
737
783
  } catch (error) {
738
784
  handleCommandError(error, "tabs close");
739
785
  }
740
786
  });
741
- tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid) => {
787
+ tabs.command("switch <tabid>").description("\u6FC0\u6D3B\u5E76\u5207\u6362\u5230\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
742
788
  try {
743
- const result = await tabsSwitchCommand(tabid);
744
- console.log(JSON.stringify(result, null, 2));
789
+ const result2 = await tabsSwitchCommand(tabid2);
790
+ console.log(JSON.stringify(result2, null, 2));
745
791
  } catch (error) {
746
792
  handleCommandError(error, "tabs switch");
747
793
  }
748
794
  });
749
- tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid) => {
795
+ tabs.command("back [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u540E\u9000\u4E00\u6B65").action(async (tabid2) => {
750
796
  try {
751
- const result = await tabsBackCommand(parseTabId(tabid));
752
- console.log(JSON.stringify(result, null, 2));
797
+ const result2 = await tabsBackCommand(parseTabId(tabid2));
798
+ console.log(JSON.stringify(result2, null, 2));
753
799
  } catch (error) {
754
800
  handleCommandError(error, "tabs back");
755
801
  }
756
802
  });
757
- tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid) => {
803
+ tabs.command("forward [tabid]").description("\u5C06\u5F53\u524D\u6216\u6307\u5B9A\u6807\u7B7E\u9875\u5BFC\u822A\u524D\u8FDB\u4E00\u6B65").action(async (tabid2) => {
758
804
  try {
759
- const result = await tabsForwardCommand(parseTabId(tabid));
760
- console.log(JSON.stringify(result, null, 2));
805
+ const result2 = await tabsForwardCommand(parseTabId(tabid2));
806
+ console.log(JSON.stringify(result2, null, 2));
761
807
  } catch (error) {
762
808
  handleCommandError(error, "tabs forward");
763
809
  }