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