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