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