@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 +331 -279
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +331 -285
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +127 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +127 -173
- 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.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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 = [
|
|
128
|
-
|
|
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
|
|
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");
|
|
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
|
|
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
|
-
|
|
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://
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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((
|
|
165
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
168
166
|
}
|
|
169
|
-
throw new Error(
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
223
|
+
`puppeteer.connect to ${browserURL} after launch timed out`
|
|
220
224
|
);
|
|
221
|
-
console.log(import_picocolors.default.green(
|
|
222
|
-
return
|
|
223
|
-
} catch
|
|
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(
|
|
274
|
-
const session = await
|
|
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(
|
|
284
|
-
return
|
|
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(
|
|
298
|
-
const pageTargets = getPageTargets(
|
|
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 ===
|
|
260
|
+
return tid === tabid2 || tid.includes(tabid2);
|
|
302
261
|
}) ?? null;
|
|
303
262
|
}
|
|
304
|
-
async function activateTabById(
|
|
305
|
-
const target = findPageTargetByTabId(
|
|
263
|
+
async function activateTabById(browser2, tabid2) {
|
|
264
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
306
265
|
if (!target) {
|
|
307
|
-
throw new Error(`Tab with targetId "${
|
|
266
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
308
267
|
}
|
|
309
268
|
const realTabId = getTargetIdFromTarget(target);
|
|
310
|
-
const pages = await
|
|
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
|
|
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(
|
|
327
|
-
const pageTargets = getPageTargets(
|
|
285
|
+
async function getTargetPage(browser2, tabid2) {
|
|
286
|
+
const pageTargets = getPageTargets(browser2);
|
|
328
287
|
if (pageTargets.length === 0) {
|
|
329
|
-
const newPage = await
|
|
288
|
+
const newPage = await browser2.newPage();
|
|
330
289
|
await injectWebMCPPolyfillAndTools(newPage);
|
|
331
290
|
return newPage;
|
|
332
291
|
}
|
|
333
292
|
let targetPage = null;
|
|
334
|
-
if (
|
|
335
|
-
const target = findPageTargetByTabId(
|
|
293
|
+
if (tabid2 !== void 0) {
|
|
294
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
336
295
|
if (!target) {
|
|
337
|
-
throw new Error(`Tab with targetId "${
|
|
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
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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(
|
|
383
|
-
await injectWebMCPPolyfillAndTools(
|
|
339
|
+
async function injectIntoPage(page2) {
|
|
340
|
+
await injectWebMCPPolyfillAndTools(page2, true);
|
|
384
341
|
}
|
|
385
|
-
async function injectWebMCPPolyfillAndTools(
|
|
386
|
-
const polyfillReady = !force && await
|
|
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
|
|
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((
|
|
359
|
+
await new Promise((resolve2) => setTimeout(resolve2, 300));
|
|
403
360
|
}
|
|
404
|
-
await injectDomainTools(
|
|
361
|
+
await injectDomainTools(page2);
|
|
405
362
|
}
|
|
406
|
-
async function injectDomainTools(
|
|
363
|
+
async function injectDomainTools(page2) {
|
|
407
364
|
let hostname;
|
|
408
365
|
try {
|
|
409
|
-
const url = new 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
|
|
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
|
|
387
|
+
async function stateCommand({ tabid: tabid2 }) {
|
|
388
|
+
const browser2 = await connectBrowser();
|
|
432
389
|
try {
|
|
433
|
-
const
|
|
434
|
-
|
|
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
|
|
483
|
-
const tabs2 = await Promise.all(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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
|
|
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
|
|
472
|
+
const browser2 = await connectBrowser();
|
|
513
473
|
try {
|
|
514
|
-
const
|
|
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
|
|
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
|
|
547
|
+
const browser2 = await connectBrowser();
|
|
549
548
|
try {
|
|
550
|
-
const
|
|
549
|
+
const page2 = await browser2.newPage();
|
|
551
550
|
url = normalizeUrl(url);
|
|
552
|
-
await
|
|
553
|
-
await injectIntoPage(
|
|
554
|
-
await new Promise((
|
|
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(
|
|
558
|
-
url:
|
|
559
|
-
title: await
|
|
556
|
+
tabid: await getPageTargetId(page2),
|
|
557
|
+
url: page2.url(),
|
|
558
|
+
title: await page2.title()
|
|
560
559
|
};
|
|
561
560
|
} finally {
|
|
562
|
-
await
|
|
561
|
+
await browser2.disconnect();
|
|
563
562
|
}
|
|
564
563
|
}
|
|
565
|
-
async function tabsCloseCommand(
|
|
566
|
-
const
|
|
564
|
+
async function tabsCloseCommand(tabid2) {
|
|
565
|
+
const browser2 = await connectBrowser();
|
|
567
566
|
try {
|
|
568
|
-
const target = findPageTargetByTabId(
|
|
567
|
+
const target = findPageTargetByTabId(browser2, tabid2);
|
|
569
568
|
if (!target) {
|
|
570
|
-
throw new Error(`Tab with targetId "${
|
|
569
|
+
throw new Error(`Tab with targetId "${tabid2}" not found.`);
|
|
571
570
|
}
|
|
572
|
-
const
|
|
573
|
-
if (!
|
|
574
|
-
throw new Error(`Tab with targetId "${
|
|
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(
|
|
577
|
-
await
|
|
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
|
|
582
|
+
await browser2.disconnect();
|
|
584
583
|
}
|
|
585
584
|
}
|
|
586
|
-
async function tabsSwitchCommand(
|
|
587
|
-
const
|
|
585
|
+
async function tabsSwitchCommand(tabid2) {
|
|
586
|
+
const browser2 = await connectBrowser();
|
|
588
587
|
try {
|
|
589
|
-
await activateTabById(
|
|
590
|
-
const
|
|
588
|
+
await activateTabById(browser2, tabid2);
|
|
589
|
+
const page2 = await getTargetPage(browser2, tabid2);
|
|
591
590
|
return {
|
|
592
591
|
success: true,
|
|
593
|
-
tabid: await getPageTargetId(
|
|
594
|
-
url:
|
|
595
|
-
title: await
|
|
592
|
+
tabid: await getPageTargetId(page2),
|
|
593
|
+
url: page2.url(),
|
|
594
|
+
title: await page2.title()
|
|
596
595
|
};
|
|
597
596
|
} finally {
|
|
598
|
-
await
|
|
597
|
+
await browser2.disconnect();
|
|
599
598
|
}
|
|
600
599
|
}
|
|
601
|
-
async function tabsBackCommand(
|
|
602
|
-
const
|
|
600
|
+
async function tabsBackCommand(tabid2) {
|
|
601
|
+
const browser2 = await connectBrowser();
|
|
603
602
|
try {
|
|
604
|
-
const
|
|
605
|
-
const response = await
|
|
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(
|
|
611
|
-
url:
|
|
612
|
-
title: await
|
|
609
|
+
tabid: await getPageTargetId(page2).catch(() => tabid2),
|
|
610
|
+
url: page2.url(),
|
|
611
|
+
title: await page2.title()
|
|
613
612
|
};
|
|
614
613
|
}
|
|
615
|
-
await injectIntoPage(
|
|
614
|
+
await injectIntoPage(page2);
|
|
616
615
|
return {
|
|
617
616
|
success: true,
|
|
618
|
-
tabid: await getPageTargetId(
|
|
619
|
-
url:
|
|
620
|
-
title: await
|
|
617
|
+
tabid: await getPageTargetId(page2),
|
|
618
|
+
url: page2.url(),
|
|
619
|
+
title: await page2.title()
|
|
621
620
|
};
|
|
622
621
|
} finally {
|
|
623
|
-
await
|
|
622
|
+
await browser2.disconnect();
|
|
624
623
|
}
|
|
625
624
|
}
|
|
626
|
-
async function tabsForwardCommand(
|
|
627
|
-
const
|
|
625
|
+
async function tabsForwardCommand(tabid2) {
|
|
626
|
+
const browser2 = await connectBrowser();
|
|
628
627
|
try {
|
|
629
|
-
const
|
|
630
|
-
const response = await
|
|
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(
|
|
636
|
-
url:
|
|
637
|
-
title: await
|
|
634
|
+
tabid: await getPageTargetId(page2).catch(() => tabid2),
|
|
635
|
+
url: page2.url(),
|
|
636
|
+
title: await page2.title()
|
|
638
637
|
};
|
|
639
638
|
}
|
|
640
|
-
await injectIntoPage(
|
|
639
|
+
await injectIntoPage(page2);
|
|
641
640
|
return {
|
|
642
641
|
success: true,
|
|
643
|
-
tabid: await getPageTargetId(
|
|
644
|
-
url:
|
|
645
|
-
title: await
|
|
642
|
+
tabid: await getPageTargetId(page2),
|
|
643
|
+
url: page2.url(),
|
|
644
|
+
title: await page2.title()
|
|
646
645
|
};
|
|
647
646
|
} finally {
|
|
648
|
-
await
|
|
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.
|
|
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
|
|
725
|
-
console.log(JSON.stringify(
|
|
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>
|
|
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
|
-
|
|
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(
|
|
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
|
|
746
|
-
console.log(JSON.stringify(
|
|
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 (
|
|
803
|
+
tabs.command("close <tabid>").description("\u5173\u95ED\u6307\u5B9A tabid \u7684\u6807\u7B7E\u9875").action(async (tabid2) => {
|
|
752
804
|
try {
|
|
753
|
-
const
|
|
754
|
-
console.log(JSON.stringify(
|
|
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 (
|
|
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
|
|
762
|
-
console.log(JSON.stringify(
|
|
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 (
|
|
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
|
|
770
|
-
console.log(JSON.stringify(
|
|
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 (
|
|
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
|
|
778
|
-
console.log(JSON.stringify(
|
|
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
|
}
|