web-probe-cli 0.1.0
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 +162 -0
- package/dist/index.js +1641 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
- package/src/skill/SKILL.md +84 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1641 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
10
|
+
var __esm = (fn, res) => function __init() {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
};
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/core/state.ts
|
|
19
|
+
var state_exports = {};
|
|
20
|
+
__export(state_exports, {
|
|
21
|
+
checkTimeout: () => checkTimeout,
|
|
22
|
+
clearState: () => clearState,
|
|
23
|
+
getStateDir: () => getStateDir,
|
|
24
|
+
loadState: () => loadState,
|
|
25
|
+
saveState: () => saveState,
|
|
26
|
+
updateActivity: () => updateActivity
|
|
27
|
+
});
|
|
28
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, unlinkSync } from "fs";
|
|
29
|
+
import { join } from "path";
|
|
30
|
+
import { homedir } from "os";
|
|
31
|
+
function ensureDir() {
|
|
32
|
+
if (!existsSync(STATE_DIR)) {
|
|
33
|
+
mkdirSync(STATE_DIR, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function loadState() {
|
|
37
|
+
try {
|
|
38
|
+
if (!existsSync(STATE_FILE)) return null;
|
|
39
|
+
const raw = readFileSync(STATE_FILE, "utf-8");
|
|
40
|
+
return JSON.parse(raw);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function saveState(port, pid, timeoutMinutes = 30) {
|
|
46
|
+
ensureDir();
|
|
47
|
+
const state = {
|
|
48
|
+
port,
|
|
49
|
+
pid,
|
|
50
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
51
|
+
timeoutMinutes,
|
|
52
|
+
lastActivityAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
53
|
+
};
|
|
54
|
+
writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
|
|
55
|
+
try {
|
|
56
|
+
chmodSync(STATE_FILE, 384);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function updateActivity() {
|
|
61
|
+
const state = loadState();
|
|
62
|
+
if (!state) return;
|
|
63
|
+
state.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
64
|
+
ensureDir();
|
|
65
|
+
writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
|
|
66
|
+
}
|
|
67
|
+
function clearState() {
|
|
68
|
+
try {
|
|
69
|
+
if (existsSync(STATE_FILE)) {
|
|
70
|
+
unlinkSync(STATE_FILE);
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function getStateDir() {
|
|
76
|
+
return STATE_DIR;
|
|
77
|
+
}
|
|
78
|
+
function checkTimeout() {
|
|
79
|
+
const state = loadState();
|
|
80
|
+
if (!state) return { expired: false, minutesIdle: 0 };
|
|
81
|
+
const lastActivity = new Date(state.lastActivityAt).getTime();
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const minutesIdle = (now - lastActivity) / 1e3 / 60;
|
|
84
|
+
if (minutesIdle >= state.timeoutMinutes) {
|
|
85
|
+
return { expired: true, minutesIdle: Math.round(minutesIdle) };
|
|
86
|
+
}
|
|
87
|
+
return { expired: false, minutesIdle: Math.round(minutesIdle) };
|
|
88
|
+
}
|
|
89
|
+
var STATE_DIR, STATE_FILE;
|
|
90
|
+
var init_state = __esm({
|
|
91
|
+
"src/core/state.ts"() {
|
|
92
|
+
"use strict";
|
|
93
|
+
STATE_DIR = join(homedir(), ".web-probe");
|
|
94
|
+
STATE_FILE = join(STATE_DIR, "state.json");
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// src/utils/platform.ts
|
|
99
|
+
import { existsSync as existsSync2 } from "fs";
|
|
100
|
+
import { execSync } from "child_process";
|
|
101
|
+
import { platform } from "os";
|
|
102
|
+
function getPlatform() {
|
|
103
|
+
return platform();
|
|
104
|
+
}
|
|
105
|
+
function findChromePath() {
|
|
106
|
+
const plat = getPlatform();
|
|
107
|
+
const candidates = CHROME_PATHS[plat] ?? [];
|
|
108
|
+
for (const p of candidates) {
|
|
109
|
+
if (p && existsSync2(p)) return p;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
if (plat === "win32") {
|
|
113
|
+
const result = execSync("where chrome.exe 2>nul", { encoding: "utf-8" }).trim();
|
|
114
|
+
const first = result.split("\n")[0]?.trim();
|
|
115
|
+
if (first && existsSync2(first)) return first;
|
|
116
|
+
} else {
|
|
117
|
+
const result = execSync("which google-chrome 2>/dev/null || which chromium 2>/dev/null", {
|
|
118
|
+
encoding: "utf-8"
|
|
119
|
+
}).trim();
|
|
120
|
+
if (result) return result;
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
function getChromeVersion(chromePath) {
|
|
127
|
+
const plat = getPlatform();
|
|
128
|
+
try {
|
|
129
|
+
if (plat === "darwin") {
|
|
130
|
+
const plistPath = chromePath.replace("/MacOS/Google Chrome", "/Info.plist");
|
|
131
|
+
const output2 = execSync(
|
|
132
|
+
`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}" 2>/dev/null`,
|
|
133
|
+
{ encoding: "utf-8" }
|
|
134
|
+
).trim();
|
|
135
|
+
return output2 || null;
|
|
136
|
+
}
|
|
137
|
+
if (plat === "win32") {
|
|
138
|
+
const output2 = execSync(
|
|
139
|
+
`powershell -NoProfile -Command "(Get-Item '${chromePath}').VersionInfo.FileVersion"`,
|
|
140
|
+
{ encoding: "utf-8", stdio: "pipe" }
|
|
141
|
+
).trim();
|
|
142
|
+
return output2 || null;
|
|
143
|
+
}
|
|
144
|
+
const output = execSync(`"${chromePath}" --version 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
145
|
+
const match = output.match(/[\d.]+/);
|
|
146
|
+
return match ? match[0] : null;
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
var CHROME_PATHS;
|
|
152
|
+
var init_platform = __esm({
|
|
153
|
+
"src/utils/platform.ts"() {
|
|
154
|
+
"use strict";
|
|
155
|
+
CHROME_PATHS = {
|
|
156
|
+
darwin: [
|
|
157
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
158
|
+
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
|
|
159
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
|
160
|
+
],
|
|
161
|
+
linux: [
|
|
162
|
+
"/usr/bin/google-chrome",
|
|
163
|
+
"/usr/bin/google-chrome-stable",
|
|
164
|
+
"/usr/bin/chromium-browser",
|
|
165
|
+
"/usr/bin/chromium",
|
|
166
|
+
"/snap/bin/chromium"
|
|
167
|
+
],
|
|
168
|
+
win32: [
|
|
169
|
+
`${process.env.PROGRAMFILES}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
170
|
+
`${process.env["PROGRAMFILES(X86)"]}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
171
|
+
`${process.env.LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe`
|
|
172
|
+
]
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// src/core/port-manager.ts
|
|
178
|
+
var port_manager_exports = {};
|
|
179
|
+
__export(port_manager_exports, {
|
|
180
|
+
discoverFromProcess: () => discoverFromProcess,
|
|
181
|
+
discoverPort: () => discoverPort,
|
|
182
|
+
generatePort: () => generatePort,
|
|
183
|
+
verifyPort: () => verifyPort,
|
|
184
|
+
waitForPort: () => waitForPort
|
|
185
|
+
});
|
|
186
|
+
import { execSync as execSync2 } from "child_process";
|
|
187
|
+
function generatePort() {
|
|
188
|
+
return Math.floor(Math.random() * (PORT_MAX - PORT_MIN + 1)) + PORT_MIN;
|
|
189
|
+
}
|
|
190
|
+
async function verifyPort(port) {
|
|
191
|
+
try {
|
|
192
|
+
const resp = await fetch(`http://127.0.0.1:${port}/json/version`, {
|
|
193
|
+
signal: AbortSignal.timeout(3e3)
|
|
194
|
+
});
|
|
195
|
+
if (!resp.ok) return false;
|
|
196
|
+
const data = await resp.json();
|
|
197
|
+
return typeof data["Browser"] === "string";
|
|
198
|
+
} catch {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function discoverFromProcess() {
|
|
203
|
+
const plat = getPlatform();
|
|
204
|
+
try {
|
|
205
|
+
if (plat === "win32") {
|
|
206
|
+
const output = execSync2(
|
|
207
|
+
`powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \\"Name='chrome.exe'\\" | Select-Object -ExpandProperty CommandLine"`,
|
|
208
|
+
{ encoding: "utf-8", stdio: "pipe" }
|
|
209
|
+
);
|
|
210
|
+
const match = output.match(/--remote-debugging-port=(\d+)/);
|
|
211
|
+
if (match) return parseInt(match[1], 10);
|
|
212
|
+
} else {
|
|
213
|
+
const output = execSync2(
|
|
214
|
+
"ps aux | grep -E 'remote-debugging-port=' | grep -v grep",
|
|
215
|
+
{ encoding: "utf-8" }
|
|
216
|
+
);
|
|
217
|
+
const match = output.match(/--remote-debugging-port=(\d+)/);
|
|
218
|
+
if (match) return parseInt(match[1], 10);
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
async function discoverPort() {
|
|
225
|
+
const state = loadState();
|
|
226
|
+
if (state?.port) {
|
|
227
|
+
const valid = await verifyPort(state.port);
|
|
228
|
+
if (valid) return state.port;
|
|
229
|
+
}
|
|
230
|
+
const fromProcess = discoverFromProcess();
|
|
231
|
+
if (fromProcess) {
|
|
232
|
+
const valid = await verifyPort(fromProcess);
|
|
233
|
+
if (valid) return fromProcess;
|
|
234
|
+
}
|
|
235
|
+
for (const fallback of [9222, 19222]) {
|
|
236
|
+
const valid = await verifyPort(fallback);
|
|
237
|
+
if (valid) return fallback;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
async function waitForPort(port, timeoutMs = 1e4) {
|
|
242
|
+
const start = Date.now();
|
|
243
|
+
while (Date.now() - start < timeoutMs) {
|
|
244
|
+
if (await verifyPort(port)) return true;
|
|
245
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
var PORT_MIN, PORT_MAX;
|
|
250
|
+
var init_port_manager = __esm({
|
|
251
|
+
"src/core/port-manager.ts"() {
|
|
252
|
+
"use strict";
|
|
253
|
+
init_state();
|
|
254
|
+
init_platform();
|
|
255
|
+
PORT_MIN = 2e4;
|
|
256
|
+
PORT_MAX = 29999;
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// src/core/chrome-launcher.ts
|
|
261
|
+
import { spawn, execSync as execSync3 } from "child_process";
|
|
262
|
+
function isChromeRunning() {
|
|
263
|
+
const plat = getPlatform();
|
|
264
|
+
try {
|
|
265
|
+
if (plat === "darwin") {
|
|
266
|
+
const out = execSync3("pgrep -f 'Google Chrome' 2>/dev/null || true", { encoding: "utf-8" });
|
|
267
|
+
return out.trim().length > 0;
|
|
268
|
+
}
|
|
269
|
+
if (plat === "linux") {
|
|
270
|
+
const out = execSync3("pgrep -f '(chrome|chromium)' 2>/dev/null || true", { encoding: "utf-8" });
|
|
271
|
+
return out.trim().length > 0;
|
|
272
|
+
}
|
|
273
|
+
if (plat === "win32") {
|
|
274
|
+
const out = execSync3('tasklist /FI "IMAGENAME eq chrome.exe" /NH 2>nul', { encoding: "utf-8" });
|
|
275
|
+
return out.toLowerCase().includes("chrome.exe");
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function launchChrome(requestedPort) {
|
|
283
|
+
const existingPort = await discoverPort();
|
|
284
|
+
if (existingPort) {
|
|
285
|
+
const state = loadState();
|
|
286
|
+
return {
|
|
287
|
+
port: existingPort,
|
|
288
|
+
pid: state?.pid ?? 0,
|
|
289
|
+
alreadyRunning: true
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const port = requestedPort ?? generatePort();
|
|
293
|
+
const chromePath = findChromePath();
|
|
294
|
+
if (!chromePath) {
|
|
295
|
+
throw new Error("\u672A\u627E\u5230 Chrome \u6D4F\u89C8\u5668\uFF0C\u8BF7\u5148\u5B89\u88C5 Google Chrome");
|
|
296
|
+
}
|
|
297
|
+
const running = isChromeRunning();
|
|
298
|
+
if (running) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
"Chrome \u5DF2\u5728\u8FD0\u884C\u4F46\u672A\u5F00\u542F\u8C03\u8BD5\u7AEF\u53E3\u3002\n\u8BF7\u5148\u5173\u95ED\u6240\u6709 Chrome \u7A97\u53E3\uFF0C\u7136\u540E\u91CD\u65B0\u8FD0\u884C web-probe chrome start\u3002\n\uFF08\u65E0\u6CD5\u4E3A\u5DF2\u8FD0\u884C\u7684 Chrome \u8FFD\u52A0\u8C03\u8BD5\u53C2\u6570\uFF09"
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
const plat = getPlatform();
|
|
304
|
+
const args = [
|
|
305
|
+
`--remote-debugging-port=${port}`,
|
|
306
|
+
"--no-first-run",
|
|
307
|
+
"--no-default-browser-check"
|
|
308
|
+
];
|
|
309
|
+
const child = spawn(chromePath, args, {
|
|
310
|
+
detached: true,
|
|
311
|
+
stdio: "ignore",
|
|
312
|
+
shell: plat === "win32"
|
|
313
|
+
});
|
|
314
|
+
child.unref();
|
|
315
|
+
const pid = child.pid ?? 0;
|
|
316
|
+
const ready = await waitForPort(port, 15e3);
|
|
317
|
+
if (!ready) {
|
|
318
|
+
throw new Error(`Chrome \u8C03\u8BD5\u7AEF\u53E3 ${port} \u542F\u52A8\u8D85\u65F6\uFF0815\u79D2\uFF09\uFF0C\u8BF7\u68C0\u67E5 Chrome \u662F\u5426\u6B63\u5E38\u8FD0\u884C`);
|
|
319
|
+
}
|
|
320
|
+
saveState(port, pid);
|
|
321
|
+
return { port, pid, alreadyRunning: false };
|
|
322
|
+
}
|
|
323
|
+
async function stopChrome() {
|
|
324
|
+
const state = loadState();
|
|
325
|
+
if (!state) return null;
|
|
326
|
+
const { port, pid } = state;
|
|
327
|
+
if (pid > 0) {
|
|
328
|
+
try {
|
|
329
|
+
process.kill(pid, "SIGTERM");
|
|
330
|
+
} catch {
|
|
331
|
+
if (getPlatform() === "win32") {
|
|
332
|
+
try {
|
|
333
|
+
execSync3(`taskkill /PID ${pid} /F 2>nul`, { stdio: "pipe" });
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
clearState();
|
|
340
|
+
return { port };
|
|
341
|
+
}
|
|
342
|
+
async function getChromeStatus() {
|
|
343
|
+
const state = loadState();
|
|
344
|
+
if (!state) {
|
|
345
|
+
const discovered = await discoverPort();
|
|
346
|
+
if (discovered) {
|
|
347
|
+
return { running: true, port: discovered };
|
|
348
|
+
}
|
|
349
|
+
return { running: false };
|
|
350
|
+
}
|
|
351
|
+
const valid = await verifyPort(state.port);
|
|
352
|
+
if (!valid) {
|
|
353
|
+
clearState();
|
|
354
|
+
return { running: false };
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
running: true,
|
|
358
|
+
port: state.port,
|
|
359
|
+
pid: state.pid,
|
|
360
|
+
startedAt: state.startedAt
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
var init_chrome_launcher = __esm({
|
|
364
|
+
"src/core/chrome-launcher.ts"() {
|
|
365
|
+
"use strict";
|
|
366
|
+
init_platform();
|
|
367
|
+
init_state();
|
|
368
|
+
init_port_manager();
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// src/core/output.ts
|
|
373
|
+
import chalk from "chalk";
|
|
374
|
+
function printJSON(data) {
|
|
375
|
+
console.log(JSON.stringify(data, null, 2));
|
|
376
|
+
}
|
|
377
|
+
function printTable(headers, rows) {
|
|
378
|
+
const colWidths = headers.map((h, i) => {
|
|
379
|
+
const maxData = rows.reduce((max, row) => Math.max(max, (row[i] ?? "").length), 0);
|
|
380
|
+
return Math.max(h.length, maxData);
|
|
381
|
+
});
|
|
382
|
+
const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(" ");
|
|
383
|
+
console.log(chalk.bold(headerLine));
|
|
384
|
+
for (const row of rows) {
|
|
385
|
+
const line = row.map((cell, i) => (cell ?? "").padEnd(colWidths[i])).join(" ");
|
|
386
|
+
console.log(line);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function printSuccess(message) {
|
|
390
|
+
console.log(chalk.green("\u2705 " + message));
|
|
391
|
+
}
|
|
392
|
+
function printError(message) {
|
|
393
|
+
console.error(chalk.red("\u274C " + message));
|
|
394
|
+
}
|
|
395
|
+
function printWarn(message) {
|
|
396
|
+
console.log(chalk.yellow("\u26A0\uFE0F " + message));
|
|
397
|
+
}
|
|
398
|
+
function printInfo(message) {
|
|
399
|
+
console.log(chalk.blue("\u2139\uFE0F " + message));
|
|
400
|
+
}
|
|
401
|
+
function printStatus(label, ok, detail) {
|
|
402
|
+
const badge = ok ? chalk.green("[OK] ") : chalk.yellow("[WARN] ");
|
|
403
|
+
console.log(` ${badge} ${label}${detail ? ` (${detail})` : ""}`);
|
|
404
|
+
}
|
|
405
|
+
function printHeader(title) {
|
|
406
|
+
console.log();
|
|
407
|
+
console.log(chalk.bold(title));
|
|
408
|
+
console.log();
|
|
409
|
+
}
|
|
410
|
+
function truncate(str, maxLen) {
|
|
411
|
+
if (str.length <= maxLen) return str;
|
|
412
|
+
return str.slice(0, maxLen - 3) + "...";
|
|
413
|
+
}
|
|
414
|
+
var init_output = __esm({
|
|
415
|
+
"src/core/output.ts"() {
|
|
416
|
+
"use strict";
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// src/commands/chrome.ts
|
|
421
|
+
var chrome_exports = {};
|
|
422
|
+
__export(chrome_exports, {
|
|
423
|
+
chromeRestart: () => chromeRestart,
|
|
424
|
+
chromeStart: () => chromeStart,
|
|
425
|
+
chromeStatus: () => chromeStatus,
|
|
426
|
+
chromeStop: () => chromeStop
|
|
427
|
+
});
|
|
428
|
+
async function chromeStart(options) {
|
|
429
|
+
try {
|
|
430
|
+
const port = options.port ? parseInt(options.port, 10) : void 0;
|
|
431
|
+
const result = await launchChrome(port);
|
|
432
|
+
if (options.json) {
|
|
433
|
+
printJSON(result);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (result.alreadyRunning) {
|
|
437
|
+
printInfo(`\u8C03\u8BD5\u7AEF\u53E3\u5DF2\u5F00\u542F (\u7AEF\u53E3 ${result.port})`);
|
|
438
|
+
} else {
|
|
439
|
+
printSuccess(`Chrome \u8C03\u8BD5\u7AEF\u53E3\u5DF2\u542F\u52A8 (\u7AEF\u53E3 ${result.port}, PID ${result.pid})`);
|
|
440
|
+
}
|
|
441
|
+
} catch (err) {
|
|
442
|
+
if (options.json) {
|
|
443
|
+
printJSON({ error: err.message });
|
|
444
|
+
process.exitCode = 1;
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
printError(err.message);
|
|
448
|
+
process.exitCode = 1;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async function chromeStop(options) {
|
|
452
|
+
const result = await stopChrome();
|
|
453
|
+
if (options.json) {
|
|
454
|
+
printJSON(result ? { stopped: true, port: result.port } : { stopped: false });
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (result) {
|
|
458
|
+
printSuccess(`\u8C03\u8BD5\u7AEF\u53E3\u5DF2\u5173\u95ED (\u7AEF\u53E3 ${result.port})`);
|
|
459
|
+
printInfo("Chrome \u6D4F\u89C8\u5668\u7EE7\u7EED\u8FD0\u884C\uFF0C\u767B\u5F55\u72B6\u6001\u4E0D\u53D7\u5F71\u54CD");
|
|
460
|
+
} else {
|
|
461
|
+
printInfo("\u6CA1\u6709\u6D3B\u8DC3\u7684\u8C03\u8BD5\u7AEF\u53E3");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async function chromeStatus(options) {
|
|
465
|
+
const status = await getChromeStatus();
|
|
466
|
+
if (options.json) {
|
|
467
|
+
printJSON(status);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (status.running) {
|
|
471
|
+
printSuccess(`\u8C03\u8BD5\u7AEF\u53E3\u8FD0\u884C\u4E2D (\u7AEF\u53E3 ${status.port}${status.pid ? `, PID ${status.pid}` : ""})`);
|
|
472
|
+
if (status.startedAt) {
|
|
473
|
+
printInfo(`\u542F\u52A8\u65F6\u95F4: ${status.startedAt}`);
|
|
474
|
+
}
|
|
475
|
+
} else {
|
|
476
|
+
printInfo("\u8C03\u8BD5\u7AEF\u53E3\u672A\u8FD0\u884C");
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async function chromeRestart(options) {
|
|
480
|
+
await stopChrome();
|
|
481
|
+
await chromeStart({ json: options.json });
|
|
482
|
+
}
|
|
483
|
+
var init_chrome = __esm({
|
|
484
|
+
"src/commands/chrome.ts"() {
|
|
485
|
+
"use strict";
|
|
486
|
+
init_chrome_launcher();
|
|
487
|
+
init_output();
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// src/core/cdp-client.ts
|
|
492
|
+
import CDP from "chrome-remote-interface";
|
|
493
|
+
async function getPort() {
|
|
494
|
+
const port = await discoverPort();
|
|
495
|
+
if (!port) {
|
|
496
|
+
throw new Error(
|
|
497
|
+
"\u672A\u53D1\u73B0 Chrome \u8C03\u8BD5\u7AEF\u53E3\u3002\u8BF7\u5148\u8FD0\u884C\uFF1Aweb-probe chrome start"
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
updateActivity();
|
|
501
|
+
return port;
|
|
502
|
+
}
|
|
503
|
+
async function listTabs() {
|
|
504
|
+
const port = await getPort();
|
|
505
|
+
const targets = await CDP.List({ port });
|
|
506
|
+
return targets.filter((t) => t.type === "page").map((t) => ({
|
|
507
|
+
id: t.id,
|
|
508
|
+
url: t.url,
|
|
509
|
+
title: t.title,
|
|
510
|
+
type: t.type
|
|
511
|
+
}));
|
|
512
|
+
}
|
|
513
|
+
function resolveTabId(tabs, target) {
|
|
514
|
+
return tabs.find((t) => t.id === target) ?? tabs.find((t) => t.url.includes(target)) ?? tabs[parseInt(target, 10)];
|
|
515
|
+
}
|
|
516
|
+
async function connectTab(tabId) {
|
|
517
|
+
const port = await getPort();
|
|
518
|
+
const client = await CDP({ port, target: tabId });
|
|
519
|
+
return client;
|
|
520
|
+
}
|
|
521
|
+
async function evaluateInTab(tabId, expression) {
|
|
522
|
+
const client = await connectTab(tabId);
|
|
523
|
+
try {
|
|
524
|
+
await client.Runtime.enable();
|
|
525
|
+
const result = await client.Runtime.evaluate({
|
|
526
|
+
expression,
|
|
527
|
+
returnByValue: true,
|
|
528
|
+
awaitPromise: true
|
|
529
|
+
});
|
|
530
|
+
if (result.exceptionDetails) {
|
|
531
|
+
throw new Error(
|
|
532
|
+
result.exceptionDetails.text ?? result.exceptionDetails.exception?.description ?? "JavaScript \u6267\u884C\u9519\u8BEF"
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return result.result.value;
|
|
536
|
+
} finally {
|
|
537
|
+
await client.close();
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async function screenshotTab(tabId) {
|
|
541
|
+
const client = await connectTab(tabId);
|
|
542
|
+
try {
|
|
543
|
+
await client.Page.enable();
|
|
544
|
+
const { data } = await client.Page.captureScreenshot({ format: "png" });
|
|
545
|
+
return Buffer.from(data, "base64");
|
|
546
|
+
} finally {
|
|
547
|
+
await client.close();
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async function inspectPage(tabId) {
|
|
551
|
+
const expression = `
|
|
552
|
+
(() => {
|
|
553
|
+
const getMeta = () => {
|
|
554
|
+
const metas = {};
|
|
555
|
+
document.querySelectorAll('meta[name], meta[property]').forEach(m => {
|
|
556
|
+
const key = m.getAttribute('name') || m.getAttribute('property');
|
|
557
|
+
if (key) metas[key] = m.getAttribute('content') || '';
|
|
558
|
+
});
|
|
559
|
+
return metas;
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
const getHeadings = () =>
|
|
563
|
+
[...document.querySelectorAll('h1,h2,h3,h4,h5,h6')].slice(0, 20).map(h => ({
|
|
564
|
+
tag: h.tagName,
|
|
565
|
+
text: h.textContent?.trim().slice(0, 100) || ''
|
|
566
|
+
}));
|
|
567
|
+
|
|
568
|
+
const getButtons = () =>
|
|
569
|
+
[...document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]')]
|
|
570
|
+
.slice(0, 30)
|
|
571
|
+
.map(b => b.textContent?.trim().slice(0, 50) || b.getAttribute('aria-label') || '')
|
|
572
|
+
.filter(Boolean);
|
|
573
|
+
|
|
574
|
+
const getLinks = () =>
|
|
575
|
+
[...document.querySelectorAll('a[href]')].slice(0, 30).map(a => ({
|
|
576
|
+
text: (a.textContent?.trim().slice(0, 50) || a.getAttribute('aria-label') || ''),
|
|
577
|
+
href: a.getAttribute('href') || ''
|
|
578
|
+
})).filter(l => l.text);
|
|
579
|
+
|
|
580
|
+
const getInputs = () =>
|
|
581
|
+
[...document.querySelectorAll('input, textarea, select')].slice(0, 20).map(el => ({
|
|
582
|
+
type: el.tagName.toLowerCase() === 'textarea' ? 'textarea' :
|
|
583
|
+
el.tagName.toLowerCase() === 'select' ? 'select' :
|
|
584
|
+
el.getAttribute('type') || 'text',
|
|
585
|
+
placeholder: el.getAttribute('placeholder') || '',
|
|
586
|
+
name: el.getAttribute('name') || el.getAttribute('id') || ''
|
|
587
|
+
}));
|
|
588
|
+
|
|
589
|
+
const detectFrameworks = () => {
|
|
590
|
+
const f = [];
|
|
591
|
+
if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__ || document.querySelector('[data-reactroot]')) f.push('React');
|
|
592
|
+
if (window.__VUE__) f.push('Vue');
|
|
593
|
+
if (window.angular || document.querySelector('[ng-app], [data-ng-app]')) f.push('Angular');
|
|
594
|
+
if (document.querySelector('.ant-btn, .ant-layout, .ant-table')) f.push('Ant Design');
|
|
595
|
+
if (document.querySelector('.el-button, .el-table, .el-input')) f.push('Element UI');
|
|
596
|
+
if (document.querySelector('.v-application, .v-btn')) f.push('Vuetify');
|
|
597
|
+
if (document.querySelector('[class*="MuiButton"], [class*="MuiPaper"]')) f.push('Material UI');
|
|
598
|
+
return f;
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
const bodyText = document.body?.innerText?.trim().slice(0, 500) || '';
|
|
602
|
+
|
|
603
|
+
return JSON.stringify({
|
|
604
|
+
url: location.href,
|
|
605
|
+
title: document.title,
|
|
606
|
+
meta: getMeta(),
|
|
607
|
+
headings: getHeadings(),
|
|
608
|
+
buttons: getButtons(),
|
|
609
|
+
links: getLinks(),
|
|
610
|
+
inputs: getInputs(),
|
|
611
|
+
images: document.querySelectorAll('img').length,
|
|
612
|
+
frameworks: detectFrameworks(),
|
|
613
|
+
bodyTextPreview: bodyText
|
|
614
|
+
});
|
|
615
|
+
})()
|
|
616
|
+
`;
|
|
617
|
+
const result = await evaluateInTab(tabId, expression);
|
|
618
|
+
return JSON.parse(result);
|
|
619
|
+
}
|
|
620
|
+
async function clickElement(tabId, selector) {
|
|
621
|
+
await evaluateInTab(tabId, `
|
|
622
|
+
(() => {
|
|
623
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
624
|
+
if (!el) throw new Error('\u5143\u7D20\u672A\u627E\u5230: ' + ${JSON.stringify(selector)});
|
|
625
|
+
el.click();
|
|
626
|
+
return 'clicked';
|
|
627
|
+
})()
|
|
628
|
+
`);
|
|
629
|
+
}
|
|
630
|
+
async function typeInElement(tabId, selector, text) {
|
|
631
|
+
await evaluateInTab(tabId, `
|
|
632
|
+
(() => {
|
|
633
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
634
|
+
if (!el) throw new Error('\u5143\u7D20\u672A\u627E\u5230: ' + ${JSON.stringify(selector)});
|
|
635
|
+
el.focus();
|
|
636
|
+
el.value = ${JSON.stringify(text)};
|
|
637
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
638
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
639
|
+
return 'typed';
|
|
640
|
+
})()
|
|
641
|
+
`);
|
|
642
|
+
}
|
|
643
|
+
async function navigateTab(tabId, url) {
|
|
644
|
+
const client = await connectTab(tabId);
|
|
645
|
+
try {
|
|
646
|
+
await client.Page.enable();
|
|
647
|
+
await client.Page.navigate({ url });
|
|
648
|
+
await client.Page.loadEventFired();
|
|
649
|
+
} finally {
|
|
650
|
+
await client.close();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
async function getHTML(tabId, selector) {
|
|
654
|
+
if (selector) {
|
|
655
|
+
return await evaluateInTab(tabId, `
|
|
656
|
+
(() => {
|
|
657
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
658
|
+
return el ? el.outerHTML : null;
|
|
659
|
+
})()
|
|
660
|
+
`);
|
|
661
|
+
}
|
|
662
|
+
return await evaluateInTab(tabId, "document.documentElement.outerHTML");
|
|
663
|
+
}
|
|
664
|
+
var init_cdp_client = __esm({
|
|
665
|
+
"src/core/cdp-client.ts"() {
|
|
666
|
+
"use strict";
|
|
667
|
+
init_port_manager();
|
|
668
|
+
init_state();
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
// src/commands/tabs.ts
|
|
673
|
+
var tabs_exports = {};
|
|
674
|
+
__export(tabs_exports, {
|
|
675
|
+
tabsCommand: () => tabsCommand
|
|
676
|
+
});
|
|
677
|
+
async function tabsCommand(options) {
|
|
678
|
+
try {
|
|
679
|
+
const tabs = await listTabs();
|
|
680
|
+
if (options.json) {
|
|
681
|
+
printJSON(tabs);
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (tabs.length === 0) {
|
|
685
|
+
console.log("\u6CA1\u6709\u6253\u5F00\u7684\u6807\u7B7E\u9875");
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
printTable(
|
|
689
|
+
["ID", "URL", "TITLE"],
|
|
690
|
+
tabs.map((t) => [t.id.slice(0, 8), truncate(t.url, 55), truncate(t.title, 40)])
|
|
691
|
+
);
|
|
692
|
+
} catch (err) {
|
|
693
|
+
if (options.json) {
|
|
694
|
+
printJSON({ error: err.message });
|
|
695
|
+
process.exitCode = 1;
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
printError(err.message);
|
|
699
|
+
process.exitCode = 1;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
var init_tabs = __esm({
|
|
703
|
+
"src/commands/tabs.ts"() {
|
|
704
|
+
"use strict";
|
|
705
|
+
init_cdp_client();
|
|
706
|
+
init_output();
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
// src/commands/inspect.ts
|
|
711
|
+
var inspect_exports = {};
|
|
712
|
+
__export(inspect_exports, {
|
|
713
|
+
inspectCommand: () => inspectCommand
|
|
714
|
+
});
|
|
715
|
+
async function inspectCommand(target, options) {
|
|
716
|
+
try {
|
|
717
|
+
const tabs = await listTabs();
|
|
718
|
+
const tab = resolveTabId(tabs, target);
|
|
719
|
+
if (!tab) {
|
|
720
|
+
throw new Error(`\u672A\u627E\u5230\u6807\u7B7E\u9875: ${target}
|
|
721
|
+
\u4F7F\u7528 web-probe tabs \u67E5\u770B\u53EF\u7528\u6807\u7B7E\u9875`);
|
|
722
|
+
}
|
|
723
|
+
const info = await inspectPage(tab.id);
|
|
724
|
+
if (options.json !== false) {
|
|
725
|
+
printJSON(info);
|
|
726
|
+
} else {
|
|
727
|
+
printJSON(info);
|
|
728
|
+
}
|
|
729
|
+
} catch (err) {
|
|
730
|
+
if (options.json) {
|
|
731
|
+
printJSON({ error: err.message });
|
|
732
|
+
process.exitCode = 1;
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
printError(err.message);
|
|
736
|
+
process.exitCode = 1;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
var init_inspect = __esm({
|
|
740
|
+
"src/commands/inspect.ts"() {
|
|
741
|
+
"use strict";
|
|
742
|
+
init_cdp_client();
|
|
743
|
+
init_output();
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
// src/commands/shot.ts
|
|
748
|
+
var shot_exports = {};
|
|
749
|
+
__export(shot_exports, {
|
|
750
|
+
shotCommand: () => shotCommand
|
|
751
|
+
});
|
|
752
|
+
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
753
|
+
import { join as join2 } from "path";
|
|
754
|
+
import { tmpdir } from "os";
|
|
755
|
+
async function shotByUrl(url, outputPath) {
|
|
756
|
+
const { chromium } = await import("playwright-core");
|
|
757
|
+
const browser = await chromium.launch({ headless: true });
|
|
758
|
+
try {
|
|
759
|
+
const page = await browser.newPage();
|
|
760
|
+
await page.goto(url, { waitUntil: "networkidle" });
|
|
761
|
+
const outDir = join2(tmpdir(), "web-probe");
|
|
762
|
+
mkdirSync2(outDir, { recursive: true });
|
|
763
|
+
const filename = outputPath ?? join2(outDir, `shot-url-${Date.now()}.png`);
|
|
764
|
+
await page.screenshot({ path: filename, fullPage: true });
|
|
765
|
+
return filename;
|
|
766
|
+
} finally {
|
|
767
|
+
await browser.close();
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async function shotCommand(target, options) {
|
|
771
|
+
try {
|
|
772
|
+
let filePath;
|
|
773
|
+
if (target.startsWith("http://") || target.startsWith("https://")) {
|
|
774
|
+
filePath = await shotByUrl(target, options.output);
|
|
775
|
+
} else {
|
|
776
|
+
const tabs = await listTabs();
|
|
777
|
+
const tab = resolveTabId(tabs, target);
|
|
778
|
+
if (!tab) {
|
|
779
|
+
throw new Error(`\u672A\u627E\u5230\u6807\u7B7E\u9875: ${target}
|
|
780
|
+
\u4F7F\u7528 web-probe tabs \u67E5\u770B\u53EF\u7528\u6807\u7B7E\u9875`);
|
|
781
|
+
}
|
|
782
|
+
const buffer = await screenshotTab(tab.id);
|
|
783
|
+
const outDir = join2(tmpdir(), "web-probe");
|
|
784
|
+
mkdirSync2(outDir, { recursive: true });
|
|
785
|
+
filePath = options.output ?? join2(outDir, `shot-${tab.id.slice(0, 8)}-${Date.now()}.png`);
|
|
786
|
+
writeFileSync2(filePath, buffer);
|
|
787
|
+
}
|
|
788
|
+
if (options.json) {
|
|
789
|
+
printJSON({ path: filePath });
|
|
790
|
+
} else {
|
|
791
|
+
printSuccess(`\u622A\u56FE\u5DF2\u4FDD\u5B58: ${filePath}`);
|
|
792
|
+
}
|
|
793
|
+
} catch (err) {
|
|
794
|
+
if (options.json) {
|
|
795
|
+
printJSON({ error: err.message });
|
|
796
|
+
process.exitCode = 1;
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
printError(err.message);
|
|
800
|
+
process.exitCode = 1;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
var init_shot = __esm({
|
|
804
|
+
"src/commands/shot.ts"() {
|
|
805
|
+
"use strict";
|
|
806
|
+
init_cdp_client();
|
|
807
|
+
init_output();
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// src/commands/eval.ts
|
|
812
|
+
var eval_exports = {};
|
|
813
|
+
__export(eval_exports, {
|
|
814
|
+
evalCommand: () => evalCommand
|
|
815
|
+
});
|
|
816
|
+
async function evalCommand(target, javascript, options) {
|
|
817
|
+
try {
|
|
818
|
+
const tabs = await listTabs();
|
|
819
|
+
const tab = resolveTabId(tabs, target);
|
|
820
|
+
if (!tab) {
|
|
821
|
+
throw new Error(`\u672A\u627E\u5230\u6807\u7B7E\u9875: ${target}
|
|
822
|
+
\u4F7F\u7528 web-probe tabs \u67E5\u770B\u53EF\u7528\u6807\u7B7E\u9875`);
|
|
823
|
+
}
|
|
824
|
+
const result = await evaluateInTab(tab.id, javascript);
|
|
825
|
+
if (options.json) {
|
|
826
|
+
printJSON({ result });
|
|
827
|
+
} else {
|
|
828
|
+
if (typeof result === "string") {
|
|
829
|
+
console.log(result);
|
|
830
|
+
} else {
|
|
831
|
+
printJSON(result);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
} catch (err) {
|
|
835
|
+
if (options.json) {
|
|
836
|
+
printJSON({ error: err.message });
|
|
837
|
+
process.exitCode = 1;
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
printError(err.message);
|
|
841
|
+
process.exitCode = 1;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
var init_eval = __esm({
|
|
845
|
+
"src/commands/eval.ts"() {
|
|
846
|
+
"use strict";
|
|
847
|
+
init_cdp_client();
|
|
848
|
+
init_output();
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
// src/commands/click.ts
|
|
853
|
+
var click_exports = {};
|
|
854
|
+
__export(click_exports, {
|
|
855
|
+
clickCommand: () => clickCommand
|
|
856
|
+
});
|
|
857
|
+
async function clickCommand(target, selector, options) {
|
|
858
|
+
try {
|
|
859
|
+
const tabs = await listTabs();
|
|
860
|
+
const tab = resolveTabId(tabs, target);
|
|
861
|
+
if (!tab) {
|
|
862
|
+
throw new Error(`\u672A\u627E\u5230\u6807\u7B7E\u9875: ${target}
|
|
863
|
+
\u4F7F\u7528 web-probe tabs \u67E5\u770B\u53EF\u7528\u6807\u7B7E\u9875`);
|
|
864
|
+
}
|
|
865
|
+
await clickElement(tab.id, selector);
|
|
866
|
+
if (options.json) {
|
|
867
|
+
printJSON({ success: true, selector });
|
|
868
|
+
} else {
|
|
869
|
+
printSuccess(`\u5DF2\u70B9\u51FB: ${selector}`);
|
|
870
|
+
}
|
|
871
|
+
} catch (err) {
|
|
872
|
+
if (options.json) {
|
|
873
|
+
printJSON({ error: err.message });
|
|
874
|
+
process.exitCode = 1;
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
printError(err.message);
|
|
878
|
+
process.exitCode = 1;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
var init_click = __esm({
|
|
882
|
+
"src/commands/click.ts"() {
|
|
883
|
+
"use strict";
|
|
884
|
+
init_cdp_client();
|
|
885
|
+
init_output();
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
// src/commands/type.ts
|
|
890
|
+
var type_exports = {};
|
|
891
|
+
__export(type_exports, {
|
|
892
|
+
typeCommand: () => typeCommand
|
|
893
|
+
});
|
|
894
|
+
async function typeCommand(target, selector, text, options) {
|
|
895
|
+
try {
|
|
896
|
+
const tabs = await listTabs();
|
|
897
|
+
const tab = resolveTabId(tabs, target);
|
|
898
|
+
if (!tab) {
|
|
899
|
+
throw new Error(`\u672A\u627E\u5230\u6807\u7B7E\u9875: ${target}
|
|
900
|
+
\u4F7F\u7528 web-probe tabs \u67E5\u770B\u53EF\u7528\u6807\u7B7E\u9875`);
|
|
901
|
+
}
|
|
902
|
+
await typeInElement(tab.id, selector, text);
|
|
903
|
+
if (options.json) {
|
|
904
|
+
printJSON({ success: true, selector, text });
|
|
905
|
+
} else {
|
|
906
|
+
printSuccess(`\u5DF2\u8F93\u5165: "${text}" \u2192 ${selector}`);
|
|
907
|
+
}
|
|
908
|
+
} catch (err) {
|
|
909
|
+
if (options.json) {
|
|
910
|
+
printJSON({ error: err.message });
|
|
911
|
+
process.exitCode = 1;
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
printError(err.message);
|
|
915
|
+
process.exitCode = 1;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
var init_type = __esm({
|
|
919
|
+
"src/commands/type.ts"() {
|
|
920
|
+
"use strict";
|
|
921
|
+
init_cdp_client();
|
|
922
|
+
init_output();
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
// src/commands/nav.ts
|
|
927
|
+
var nav_exports = {};
|
|
928
|
+
__export(nav_exports, {
|
|
929
|
+
navCommand: () => navCommand
|
|
930
|
+
});
|
|
931
|
+
async function navCommand(target, url, options) {
|
|
932
|
+
try {
|
|
933
|
+
const tabs = await listTabs();
|
|
934
|
+
const tab = resolveTabId(tabs, target);
|
|
935
|
+
if (!tab) {
|
|
936
|
+
throw new Error(`\u672A\u627E\u5230\u6807\u7B7E\u9875: ${target}
|
|
937
|
+
\u4F7F\u7528 web-probe tabs \u67E5\u770B\u53EF\u7528\u6807\u7B7E\u9875`);
|
|
938
|
+
}
|
|
939
|
+
await navigateTab(tab.id, url);
|
|
940
|
+
if (options.json) {
|
|
941
|
+
printJSON({ success: true, url });
|
|
942
|
+
} else {
|
|
943
|
+
printSuccess(`\u5DF2\u5BFC\u822A\u5230: ${url}`);
|
|
944
|
+
}
|
|
945
|
+
} catch (err) {
|
|
946
|
+
if (options.json) {
|
|
947
|
+
printJSON({ error: err.message });
|
|
948
|
+
process.exitCode = 1;
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
printError(err.message);
|
|
952
|
+
process.exitCode = 1;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
var init_nav = __esm({
|
|
956
|
+
"src/commands/nav.ts"() {
|
|
957
|
+
"use strict";
|
|
958
|
+
init_cdp_client();
|
|
959
|
+
init_output();
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// src/commands/html.ts
|
|
964
|
+
var html_exports = {};
|
|
965
|
+
__export(html_exports, {
|
|
966
|
+
htmlCommand: () => htmlCommand
|
|
967
|
+
});
|
|
968
|
+
async function htmlCommand(target, selector, options) {
|
|
969
|
+
try {
|
|
970
|
+
const tabs = await listTabs();
|
|
971
|
+
const tab = resolveTabId(tabs, target);
|
|
972
|
+
if (!tab) {
|
|
973
|
+
throw new Error(`\u672A\u627E\u5230\u6807\u7B7E\u9875: ${target}
|
|
974
|
+
\u4F7F\u7528 web-probe tabs \u67E5\u770B\u53EF\u7528\u6807\u7B7E\u9875`);
|
|
975
|
+
}
|
|
976
|
+
const html = await getHTML(tab.id, selector);
|
|
977
|
+
if (options?.json) {
|
|
978
|
+
printJSON({ html });
|
|
979
|
+
} else {
|
|
980
|
+
console.log(html);
|
|
981
|
+
}
|
|
982
|
+
} catch (err) {
|
|
983
|
+
if (options?.json) {
|
|
984
|
+
printJSON({ error: err.message });
|
|
985
|
+
process.exitCode = 1;
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
printError(err.message);
|
|
989
|
+
process.exitCode = 1;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
var init_html = __esm({
|
|
993
|
+
"src/commands/html.ts"() {
|
|
994
|
+
"use strict";
|
|
995
|
+
init_cdp_client();
|
|
996
|
+
init_output();
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// src/utils/detect-tools.ts
|
|
1001
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, copyFileSync, writeFileSync as writeFileSync3, readFileSync as readFileSync2 } from "fs";
|
|
1002
|
+
import { join as join3 } from "path";
|
|
1003
|
+
import { homedir as homedir2 } from "os";
|
|
1004
|
+
function checkAnyExists(paths) {
|
|
1005
|
+
return paths.some((p) => existsSync3(p));
|
|
1006
|
+
}
|
|
1007
|
+
function detectAITools() {
|
|
1008
|
+
const home = homedir2();
|
|
1009
|
+
const toolDefs = [
|
|
1010
|
+
{
|
|
1011
|
+
name: "cursor",
|
|
1012
|
+
displayName: "Cursor",
|
|
1013
|
+
skillDir: join3(home, ".cursor", "skills", "web-probe"),
|
|
1014
|
+
skillFile: "SKILL.md",
|
|
1015
|
+
format: "skill.md",
|
|
1016
|
+
detectPaths: [
|
|
1017
|
+
join3(home, ".cursor"),
|
|
1018
|
+
join3(home, ".cursor", "skills")
|
|
1019
|
+
]
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
name: "claude-code",
|
|
1023
|
+
displayName: "Claude Code",
|
|
1024
|
+
skillDir: join3(home, ".claude", "skills", "web-probe"),
|
|
1025
|
+
skillFile: "SKILL.md",
|
|
1026
|
+
format: "skill.md",
|
|
1027
|
+
detectPaths: [
|
|
1028
|
+
join3(home, ".claude"),
|
|
1029
|
+
join3(home, ".claude", "skills")
|
|
1030
|
+
]
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
name: "codex",
|
|
1034
|
+
displayName: "Codex (OpenAI)",
|
|
1035
|
+
skillDir: join3(home, ".codex", "skills", "web-probe"),
|
|
1036
|
+
skillFile: "SKILL.md",
|
|
1037
|
+
format: "skill.md",
|
|
1038
|
+
detectPaths: [
|
|
1039
|
+
join3(home, ".codex"),
|
|
1040
|
+
join3(home, ".codex", "skills")
|
|
1041
|
+
]
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
name: "agents",
|
|
1045
|
+
displayName: "Agents (\u901A\u7528\u6807\u51C6)",
|
|
1046
|
+
skillDir: join3(home, ".agents", "skills", "web-probe"),
|
|
1047
|
+
skillFile: "SKILL.md",
|
|
1048
|
+
format: "skill.md",
|
|
1049
|
+
detectPaths: [
|
|
1050
|
+
join3(home, ".agents"),
|
|
1051
|
+
join3(home, ".agents", "skills")
|
|
1052
|
+
]
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
name: "opencode",
|
|
1056
|
+
displayName: "OpenCode",
|
|
1057
|
+
skillDir: join3(home, ".config", "opencode", "skills", "web-probe"),
|
|
1058
|
+
skillFile: "SKILL.md",
|
|
1059
|
+
format: "skill.md",
|
|
1060
|
+
detectPaths: [
|
|
1061
|
+
join3(home, ".config", "opencode"),
|
|
1062
|
+
join3(home, ".config", "opencode", "skills")
|
|
1063
|
+
]
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
name: "windsurf",
|
|
1067
|
+
displayName: "Windsurf",
|
|
1068
|
+
skillDir: join3(home, ".codeium", "windsurf"),
|
|
1069
|
+
skillFile: "web-probe-rules.md",
|
|
1070
|
+
format: "rules-file",
|
|
1071
|
+
detectPaths: [
|
|
1072
|
+
join3(home, ".codeium", "windsurf"),
|
|
1073
|
+
join3(home, ".codeium")
|
|
1074
|
+
]
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
name: "copilot",
|
|
1078
|
+
displayName: "GitHub Copilot",
|
|
1079
|
+
skillDir: join3(home, ".github", "agents"),
|
|
1080
|
+
skillFile: "web-probe.md",
|
|
1081
|
+
format: "agents-md",
|
|
1082
|
+
detectPaths: [
|
|
1083
|
+
join3(home, ".github")
|
|
1084
|
+
]
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
name: "trae",
|
|
1088
|
+
displayName: "Trae",
|
|
1089
|
+
skillDir: join3(home, ".trae", "rules"),
|
|
1090
|
+
skillFile: "web-probe.md",
|
|
1091
|
+
format: "rules-file",
|
|
1092
|
+
detectPaths: [
|
|
1093
|
+
join3(home, ".trae")
|
|
1094
|
+
]
|
|
1095
|
+
}
|
|
1096
|
+
];
|
|
1097
|
+
return toolDefs.map((def) => {
|
|
1098
|
+
const detected = checkAnyExists(def.detectPaths);
|
|
1099
|
+
const installed = existsSync3(join3(def.skillDir, def.skillFile));
|
|
1100
|
+
return { ...def, detected, installed };
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
function installSkill(target, skillSourcePath) {
|
|
1104
|
+
mkdirSync3(target.skillDir, { recursive: true });
|
|
1105
|
+
const dest = join3(target.skillDir, target.skillFile);
|
|
1106
|
+
if (target.format === "skill.md") {
|
|
1107
|
+
copyFileSync(skillSourcePath, dest);
|
|
1108
|
+
} else {
|
|
1109
|
+
const content = readFileSync2(skillSourcePath, "utf-8");
|
|
1110
|
+
const stripped = content.replace(/^---[\s\S]*?---\n*/m, "");
|
|
1111
|
+
writeFileSync3(dest, stripped, "utf-8");
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
var init_detect_tools = __esm({
|
|
1115
|
+
"src/utils/detect-tools.ts"() {
|
|
1116
|
+
"use strict";
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
// src/commands/doctor.ts
|
|
1121
|
+
var doctor_exports = {};
|
|
1122
|
+
__export(doctor_exports, {
|
|
1123
|
+
doctorCommand: () => doctorCommand
|
|
1124
|
+
});
|
|
1125
|
+
import { execSync as execSync4 } from "child_process";
|
|
1126
|
+
async function diagnose() {
|
|
1127
|
+
const nodeVersion = process.version;
|
|
1128
|
+
let npxOk = false;
|
|
1129
|
+
try {
|
|
1130
|
+
execSync4("npx --version", { encoding: "utf-8", stdio: "pipe" });
|
|
1131
|
+
npxOk = true;
|
|
1132
|
+
} catch {
|
|
1133
|
+
}
|
|
1134
|
+
const chromePath = findChromePath();
|
|
1135
|
+
const chromeVersion = chromePath ? getChromeVersion(chromePath) : null;
|
|
1136
|
+
const state = loadState();
|
|
1137
|
+
let cdpOk = false;
|
|
1138
|
+
if (state?.port) {
|
|
1139
|
+
cdpOk = await verifyPort(state.port);
|
|
1140
|
+
}
|
|
1141
|
+
let playwrightVersion = null;
|
|
1142
|
+
try {
|
|
1143
|
+
playwrightVersion = execSync4("npx playwright-core --version 2>/dev/null", {
|
|
1144
|
+
encoding: "utf-8",
|
|
1145
|
+
stdio: "pipe"
|
|
1146
|
+
}).trim();
|
|
1147
|
+
} catch {
|
|
1148
|
+
try {
|
|
1149
|
+
const pkg = await import("playwright-core/package.json", { with: { type: "json" } });
|
|
1150
|
+
playwrightVersion = pkg.default?.version ?? null;
|
|
1151
|
+
} catch {
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
const aiTools = detectAITools();
|
|
1155
|
+
const tools = aiTools.map((t) => ({
|
|
1156
|
+
name: t.name,
|
|
1157
|
+
displayName: t.displayName,
|
|
1158
|
+
detected: t.detected,
|
|
1159
|
+
installed: t.installed
|
|
1160
|
+
}));
|
|
1161
|
+
return {
|
|
1162
|
+
node: { ok: true, version: nodeVersion },
|
|
1163
|
+
npx: { ok: npxOk },
|
|
1164
|
+
chrome: { ok: !!chromePath, version: chromeVersion, path: chromePath },
|
|
1165
|
+
cdp: { ok: cdpOk, port: state?.port ?? null, pid: state?.pid ?? null },
|
|
1166
|
+
playwright: { ok: !!playwrightVersion, version: playwrightVersion },
|
|
1167
|
+
tools
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
async function doctorCommand(options) {
|
|
1171
|
+
const result = await diagnose();
|
|
1172
|
+
if (options.json) {
|
|
1173
|
+
printJSON(result);
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
printHeader("\u{1F50D} web-probe \u2014 \u73AF\u5883\u8BCA\u65AD");
|
|
1177
|
+
console.log("\u57FA\u7840\u4F9D\u8D56");
|
|
1178
|
+
printStatus("Node.js", result.node.ok, result.node.version);
|
|
1179
|
+
printStatus("npx", result.npx.ok, result.npx.ok ? "" : "\u672A\u627E\u5230");
|
|
1180
|
+
console.log();
|
|
1181
|
+
console.log("Chrome \u6D4F\u89C8\u5668");
|
|
1182
|
+
printStatus("Chrome", result.chrome.ok, result.chrome.ok ? `\u5DF2\u5B89\u88C5${result.chrome.version ? ` (${result.chrome.version})` : ""}` : "\u672A\u627E\u5230");
|
|
1183
|
+
printStatus(
|
|
1184
|
+
"\u8C03\u8BD5\u7AEF\u53E3",
|
|
1185
|
+
result.cdp.ok,
|
|
1186
|
+
result.cdp.ok ? `\u5DF2\u5F00\u542F (\u7AEF\u53E3 ${result.cdp.port}, PID ${result.cdp.pid})` : "\u672A\u5F00\u542F"
|
|
1187
|
+
);
|
|
1188
|
+
console.log();
|
|
1189
|
+
console.log("\u5F15\u64CE");
|
|
1190
|
+
printStatus(
|
|
1191
|
+
"Playwright",
|
|
1192
|
+
result.playwright.ok,
|
|
1193
|
+
result.playwright.ok ? result.playwright.version ?? "" : "\u672A\u5B89\u88C5"
|
|
1194
|
+
);
|
|
1195
|
+
console.log();
|
|
1196
|
+
console.log("AI \u5DE5\u5177 Skill \u6CE8\u5165\u72B6\u6001");
|
|
1197
|
+
const detectedTools = result.tools.filter((t) => t.detected);
|
|
1198
|
+
const undetectedTools = result.tools.filter((t) => !t.detected);
|
|
1199
|
+
if (detectedTools.length > 0) {
|
|
1200
|
+
for (const tool of detectedTools) {
|
|
1201
|
+
printStatus(
|
|
1202
|
+
tool.displayName,
|
|
1203
|
+
tool.installed,
|
|
1204
|
+
tool.installed ? "\u5DF2\u6CE8\u5165" : "\u5DF2\u68C0\u6D4B\u5230\uFF0C\u672A\u6CE8\u5165 Skill"
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
if (undetectedTools.length > 0) {
|
|
1209
|
+
const names = undetectedTools.map((t) => t.displayName).join(", ");
|
|
1210
|
+
console.log(` ${" "} ${names} \u2014 \u672A\u68C0\u6D4B\u5230`);
|
|
1211
|
+
}
|
|
1212
|
+
const needsSkill = detectedTools.some((t) => !t.installed);
|
|
1213
|
+
console.log();
|
|
1214
|
+
const allCore = result.node.ok && result.chrome.ok;
|
|
1215
|
+
if (allCore) {
|
|
1216
|
+
printSuccess("\u6838\u5FC3\u7EC4\u4EF6\u5C31\u7EEA");
|
|
1217
|
+
if (needsSkill) {
|
|
1218
|
+
printWarn("\u90E8\u5206 AI \u5DE5\u5177\u672A\u6CE8\u5165 Skill\uFF0C\u8FD0\u884C web-probe skill install \u8FDB\u884C\u6CE8\u5165");
|
|
1219
|
+
}
|
|
1220
|
+
} else {
|
|
1221
|
+
printWarn("\u90E8\u5206\u7EC4\u4EF6\u7F3A\u5931\uFF0C\u8FD0\u884C web-probe setup \u8FDB\u884C\u5B89\u88C5");
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
var init_doctor = __esm({
|
|
1225
|
+
"src/commands/doctor.ts"() {
|
|
1226
|
+
"use strict";
|
|
1227
|
+
init_platform();
|
|
1228
|
+
init_state();
|
|
1229
|
+
init_port_manager();
|
|
1230
|
+
init_detect_tools();
|
|
1231
|
+
init_output();
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
// src/commands/setup.ts
|
|
1236
|
+
var setup_exports = {};
|
|
1237
|
+
__export(setup_exports, {
|
|
1238
|
+
setupCommand: () => setupCommand
|
|
1239
|
+
});
|
|
1240
|
+
import { execSync as execSync5 } from "child_process";
|
|
1241
|
+
import chalk2 from "chalk";
|
|
1242
|
+
function step(num, total, label, fn) {
|
|
1243
|
+
process.stdout.write(`[${num}/${total}] ${label}`);
|
|
1244
|
+
try {
|
|
1245
|
+
const result = fn();
|
|
1246
|
+
if (result === true || typeof result === "string") {
|
|
1247
|
+
console.log(chalk2.green(` \u2713${typeof result === "string" ? ` (${result})` : ""}`));
|
|
1248
|
+
return true;
|
|
1249
|
+
}
|
|
1250
|
+
console.log(chalk2.red(" \u2717"));
|
|
1251
|
+
return false;
|
|
1252
|
+
} catch (err) {
|
|
1253
|
+
console.log(chalk2.red(` \u2717 ${err.message}`));
|
|
1254
|
+
return false;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
async function setupCommand(options) {
|
|
1258
|
+
if (options.json) {
|
|
1259
|
+
const results = {};
|
|
1260
|
+
results.chrome = !!findChromePath();
|
|
1261
|
+
try {
|
|
1262
|
+
execSync5("npx playwright-core --version 2>/dev/null", { stdio: "pipe" });
|
|
1263
|
+
results.playwright = true;
|
|
1264
|
+
} catch {
|
|
1265
|
+
results.playwright = false;
|
|
1266
|
+
}
|
|
1267
|
+
printJSON(results);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
console.log();
|
|
1271
|
+
console.log(chalk2.bold("\u{1F680} web-probe \u2014 \u4E00\u952E\u5B89\u88C5"));
|
|
1272
|
+
console.log();
|
|
1273
|
+
const total = 3;
|
|
1274
|
+
step(1, total, "\u68C0\u6D4B Chrome...", () => {
|
|
1275
|
+
const path = findChromePath();
|
|
1276
|
+
if (!path) throw new Error("\u672A\u627E\u5230 Chrome\uFF0C\u8BF7\u5148\u5B89\u88C5");
|
|
1277
|
+
return true;
|
|
1278
|
+
});
|
|
1279
|
+
step(2, total, "\u68C0\u6D4B Playwright...", () => {
|
|
1280
|
+
try {
|
|
1281
|
+
__require.resolve("playwright-core");
|
|
1282
|
+
return "\u5DF2\u5B89\u88C5";
|
|
1283
|
+
} catch {
|
|
1284
|
+
return "\u4F5C\u4E3A web-probe \u4F9D\u8D56\u5DF2\u5305\u542B";
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
step(3, total, "\u68C0\u6D4B\u73AF\u5883...", () => {
|
|
1288
|
+
return true;
|
|
1289
|
+
});
|
|
1290
|
+
console.log();
|
|
1291
|
+
printSuccess("\u73AF\u5883\u68C0\u6D4B\u5B8C\u6210\uFF01");
|
|
1292
|
+
console.log();
|
|
1293
|
+
console.log(" \u8FD0\u884C " + chalk2.cyan("web-probe skill install") + " \u6CE8\u5165 Skill \u5230 AI \u5DE5\u5177");
|
|
1294
|
+
console.log(" \u8FD0\u884C " + chalk2.cyan("web-probe chrome start") + " \u542F\u52A8 Chrome \u8C03\u8BD5\u7AEF\u53E3");
|
|
1295
|
+
console.log();
|
|
1296
|
+
}
|
|
1297
|
+
var init_setup = __esm({
|
|
1298
|
+
"src/commands/setup.ts"() {
|
|
1299
|
+
"use strict";
|
|
1300
|
+
init_platform();
|
|
1301
|
+
init_output();
|
|
1302
|
+
}
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
// src/commands/explore.ts
|
|
1306
|
+
var explore_exports = {};
|
|
1307
|
+
__export(explore_exports, {
|
|
1308
|
+
exploreCommand: () => exploreCommand
|
|
1309
|
+
});
|
|
1310
|
+
async function loadStagehand() {
|
|
1311
|
+
try {
|
|
1312
|
+
return await import("@browserbasehq/stagehand");
|
|
1313
|
+
} catch {
|
|
1314
|
+
throw new Error(
|
|
1315
|
+
"explore \u547D\u4EE4\u9700\u8981\u5B89\u88C5 Stagehand\uFF08\u53EF\u9009\u4F9D\u8D56\uFF09\uFF1A\n\n npm install @browserbasehq/stagehand zod\n\n\u540C\u65F6\u9700\u8981\u8BBE\u7F6E LLM API Key \u73AF\u5883\u53D8\u91CF\uFF1A\n export OPENAI_API_KEY=sk-...\n # \u6216 export ANTHROPIC_API_KEY=sk-ant-..."
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
function checkApiKey() {
|
|
1320
|
+
if (process.env.OPENAI_API_KEY) {
|
|
1321
|
+
return { model: "openai/gpt-4o", provider: "OpenAI" };
|
|
1322
|
+
}
|
|
1323
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
1324
|
+
return { model: "anthropic/claude-sonnet-4-20250514", provider: "Anthropic" };
|
|
1325
|
+
}
|
|
1326
|
+
throw new Error(
|
|
1327
|
+
"\u672A\u68C0\u6D4B\u5230 LLM API Key\u3002explore \u547D\u4EE4\u9700\u8981 AI \u6A21\u578B\u9A71\u52A8\u3002\n\n\u8BF7\u8BBE\u7F6E\u4EE5\u4E0B\u4EFB\u4E00\u73AF\u5883\u53D8\u91CF\uFF1A\n export OPENAI_API_KEY=sk-...\n export ANTHROPIC_API_KEY=sk-ant-..."
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
async function exploreCommand(url, goal, options) {
|
|
1331
|
+
try {
|
|
1332
|
+
const { Stagehand } = await loadStagehand();
|
|
1333
|
+
const { model: defaultModel, provider } = checkApiKey();
|
|
1334
|
+
const model = options?.model ?? defaultModel;
|
|
1335
|
+
const chromePath = findChromePath();
|
|
1336
|
+
if (!options?.json) {
|
|
1337
|
+
printHeader("\u{1F50D} web-probe explore \u2014 AI \u81EA\u4E3B\u63A2\u7D22");
|
|
1338
|
+
printInfo(`\u76EE\u6807: ${url}`);
|
|
1339
|
+
if (goal) printInfo(`\u4EFB\u52A1: ${goal}`);
|
|
1340
|
+
printInfo(`\u6A21\u578B: ${model} (${provider})`);
|
|
1341
|
+
console.log();
|
|
1342
|
+
}
|
|
1343
|
+
const stagehand = new Stagehand({
|
|
1344
|
+
env: "LOCAL",
|
|
1345
|
+
modelName: model,
|
|
1346
|
+
headless: options?.headless ?? true,
|
|
1347
|
+
localBrowserLaunchOptions: {
|
|
1348
|
+
executablePath: chromePath ?? void 0
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
await stagehand.init();
|
|
1352
|
+
const page = stagehand.context.pages()[0];
|
|
1353
|
+
await page.goto(url, { waitUntil: "networkidle" });
|
|
1354
|
+
const results = [];
|
|
1355
|
+
const observations = await stagehand.observe(
|
|
1356
|
+
goal ?? "\u89C2\u5BDF\u9875\u9762\u4E0A\u6240\u6709\u53EF\u4EA4\u4E92\u7684\u5143\u7D20\u548C\u4E3B\u8981\u5185\u5BB9\u533A\u57DF"
|
|
1357
|
+
);
|
|
1358
|
+
results.push({ step: "observe", data: observations });
|
|
1359
|
+
if (goal) {
|
|
1360
|
+
try {
|
|
1361
|
+
await stagehand.act(goal);
|
|
1362
|
+
results.push({ step: "act", data: { action: goal, success: true } });
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
results.push({ step: "act", data: { action: goal, success: false, error: err.message } });
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const pageInfo = await page.evaluate(() => ({
|
|
1368
|
+
url: location.href,
|
|
1369
|
+
title: document.title,
|
|
1370
|
+
bodyText: document.body?.innerText?.trim().slice(0, 500) ?? ""
|
|
1371
|
+
}));
|
|
1372
|
+
results.push({ step: "page_info", data: pageInfo });
|
|
1373
|
+
await stagehand.close();
|
|
1374
|
+
if (options?.json) {
|
|
1375
|
+
printJSON({ url, goal, model, results });
|
|
1376
|
+
} else {
|
|
1377
|
+
printSuccess("\u63A2\u7D22\u5B8C\u6210");
|
|
1378
|
+
console.log();
|
|
1379
|
+
for (const r of results) {
|
|
1380
|
+
console.log(` [${r.step}]`);
|
|
1381
|
+
if (r.data) {
|
|
1382
|
+
const lines = JSON.stringify(r.data, null, 2).split("\n");
|
|
1383
|
+
for (const line of lines.slice(0, 15)) {
|
|
1384
|
+
console.log(` ${line}`);
|
|
1385
|
+
}
|
|
1386
|
+
if (lines.length > 15) console.log(` ... (${lines.length - 15} more lines)`);
|
|
1387
|
+
}
|
|
1388
|
+
console.log();
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
} catch (err) {
|
|
1392
|
+
if (options?.json) {
|
|
1393
|
+
printJSON({ error: err.message });
|
|
1394
|
+
process.exitCode = 1;
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
printError(err.message);
|
|
1398
|
+
process.exitCode = 1;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
var init_explore = __esm({
|
|
1402
|
+
"src/commands/explore.ts"() {
|
|
1403
|
+
"use strict";
|
|
1404
|
+
init_output();
|
|
1405
|
+
init_platform();
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
// src/commands/skill.ts
|
|
1410
|
+
var skill_exports = {};
|
|
1411
|
+
__export(skill_exports, {
|
|
1412
|
+
skillInstallCommand: () => skillInstallCommand
|
|
1413
|
+
});
|
|
1414
|
+
import { join as join4, dirname } from "path";
|
|
1415
|
+
import { fileURLToPath } from "url";
|
|
1416
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1417
|
+
import { createInterface } from "readline";
|
|
1418
|
+
import chalk3 from "chalk";
|
|
1419
|
+
function getSkillSource() {
|
|
1420
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
1421
|
+
const candidates = [
|
|
1422
|
+
join4(currentDir, "..", "src", "skill", "SKILL.md"),
|
|
1423
|
+
join4(currentDir, "skill", "SKILL.md"),
|
|
1424
|
+
join4(currentDir, "..", "skill", "SKILL.md")
|
|
1425
|
+
];
|
|
1426
|
+
for (const p of candidates) {
|
|
1427
|
+
if (existsSync4(p)) return p;
|
|
1428
|
+
}
|
|
1429
|
+
throw new Error(`Skill \u6A21\u677F\u6587\u4EF6\u672A\u627E\u5230\uFF0C\u641C\u7D22\u8DEF\u5F84: ${candidates.join(", ")}`);
|
|
1430
|
+
}
|
|
1431
|
+
async function askUser(question) {
|
|
1432
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1433
|
+
return new Promise((resolve) => {
|
|
1434
|
+
rl.question(question, (answer) => {
|
|
1435
|
+
rl.close();
|
|
1436
|
+
resolve(answer.trim());
|
|
1437
|
+
});
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
function printToolTable(tools) {
|
|
1441
|
+
console.log();
|
|
1442
|
+
console.log(chalk3.bold(" # \u5DE5\u5177 \u72B6\u6001"));
|
|
1443
|
+
console.log(" " + "\u2500".repeat(45));
|
|
1444
|
+
tools.forEach((t, i) => {
|
|
1445
|
+
const status = t.installed ? chalk3.green("\u5DF2\u6CE8\u5165 \u2713") : t.detected ? chalk3.yellow("\u5DF2\u68C0\u6D4B\u5230\uFF0C\u672A\u6CE8\u5165") : chalk3.gray("\u672A\u68C0\u6D4B\u5230");
|
|
1446
|
+
console.log(` ${String(i + 1).padStart(2)} ${t.displayName.padEnd(18)} ${status}`);
|
|
1447
|
+
});
|
|
1448
|
+
console.log();
|
|
1449
|
+
}
|
|
1450
|
+
async function skillInstallCommand(options) {
|
|
1451
|
+
try {
|
|
1452
|
+
const skillSource = getSkillSource();
|
|
1453
|
+
const tools = detectAITools();
|
|
1454
|
+
if (options.target) {
|
|
1455
|
+
const tool = tools.find((t) => t.name === options.target);
|
|
1456
|
+
if (!tool) {
|
|
1457
|
+
const names = tools.map((t) => t.name).join(", ");
|
|
1458
|
+
throw new Error(`\u672A\u77E5\u76EE\u6807: ${options.target}\uFF0C\u53EF\u7528: ${names}`);
|
|
1459
|
+
}
|
|
1460
|
+
installSkill(tool, skillSource);
|
|
1461
|
+
if (options.json) {
|
|
1462
|
+
printJSON({ installed: [tool.name], path: join4(tool.skillDir, tool.skillFile) });
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
printSuccess(`Skill \u5DF2\u6CE8\u5165\u5230 ${tool.displayName} (${join4(tool.skillDir, tool.skillFile)})`);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
const detected = tools.filter((t) => t.detected);
|
|
1469
|
+
const notInstalled = detected.filter((t) => !t.installed);
|
|
1470
|
+
if (options.json) {
|
|
1471
|
+
const installed = [];
|
|
1472
|
+
for (const tool of detected) {
|
|
1473
|
+
installSkill(tool, skillSource);
|
|
1474
|
+
installed.push(tool.name);
|
|
1475
|
+
}
|
|
1476
|
+
printJSON({
|
|
1477
|
+
detected: detected.map((t) => t.name),
|
|
1478
|
+
installed,
|
|
1479
|
+
all_tools: tools.map((t) => ({ name: t.name, detected: t.detected, installed: t.installed }))
|
|
1480
|
+
});
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
console.log();
|
|
1484
|
+
console.log(chalk3.bold("\u{1F50D} web-probe \u2014 AI \u5DE5\u5177\u68C0\u6D4B"));
|
|
1485
|
+
printToolTable(tools);
|
|
1486
|
+
if (detected.length === 0) {
|
|
1487
|
+
printWarn("\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55 AI \u5DE5\u5177");
|
|
1488
|
+
console.log();
|
|
1489
|
+
printInfo("\u53EF\u4EE5\u624B\u52A8\u6307\u5B9A\u76EE\u6807\u5DE5\u5177\uFF1A");
|
|
1490
|
+
console.log(` ${chalk3.cyan("web-probe skill install --target cursor")}`);
|
|
1491
|
+
console.log(` ${chalk3.cyan("web-probe skill install --target claude-code")}`);
|
|
1492
|
+
console.log();
|
|
1493
|
+
console.log(" \u53EF\u7528\u76EE\u6807: " + tools.map((t) => chalk3.cyan(t.name)).join(", "));
|
|
1494
|
+
console.log();
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
if (notInstalled.length === 0 && !options.yes) {
|
|
1498
|
+
printSuccess("\u6240\u6709\u68C0\u6D4B\u5230\u7684 AI \u5DE5\u5177\u5DF2\u6CE8\u5165 Skill");
|
|
1499
|
+
const answer = await askUser(" \u662F\u5426\u91CD\u65B0\u6CE8\u5165\u4EE5\u66F4\u65B0\uFF1F(y/N) ");
|
|
1500
|
+
if (answer.toLowerCase() !== "y") return;
|
|
1501
|
+
}
|
|
1502
|
+
const toInstall = options.yes ? detected : detected;
|
|
1503
|
+
if (!options.yes && notInstalled.length > 0) {
|
|
1504
|
+
const names = notInstalled.map((t) => chalk3.cyan(t.displayName)).join(", ");
|
|
1505
|
+
console.log(` \u68C0\u6D4B\u5230 ${notInstalled.length} \u4E2A\u5DE5\u5177\u672A\u6CE8\u5165: ${names}`);
|
|
1506
|
+
const answer = await askUser(" \u662F\u5426\u5168\u90E8\u6CE8\u5165\uFF1F(Y/n) ");
|
|
1507
|
+
if (answer.toLowerCase() === "n") return;
|
|
1508
|
+
}
|
|
1509
|
+
console.log();
|
|
1510
|
+
for (const tool of toInstall) {
|
|
1511
|
+
installSkill(tool, skillSource);
|
|
1512
|
+
printSuccess(`${tool.displayName} \u2192 ${join4(tool.skillDir, tool.skillFile)}`);
|
|
1513
|
+
}
|
|
1514
|
+
console.log();
|
|
1515
|
+
printSuccess(`\u5DF2\u5B8C\u6210 ${toInstall.length} \u4E2A\u5DE5\u5177\u7684 Skill \u6CE8\u5165`);
|
|
1516
|
+
console.log();
|
|
1517
|
+
} catch (err) {
|
|
1518
|
+
if (options.json) {
|
|
1519
|
+
printJSON({ error: err.message });
|
|
1520
|
+
process.exitCode = 1;
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
printError(err.message);
|
|
1524
|
+
process.exitCode = 1;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
var init_skill = __esm({
|
|
1528
|
+
"src/commands/skill.ts"() {
|
|
1529
|
+
"use strict";
|
|
1530
|
+
init_detect_tools();
|
|
1531
|
+
init_output();
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
// src/index.ts
|
|
1536
|
+
import { Command } from "commander";
|
|
1537
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1538
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
1539
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1540
|
+
var __dirname = dirname2(fileURLToPath2(import.meta.url));
|
|
1541
|
+
function getVersion() {
|
|
1542
|
+
try {
|
|
1543
|
+
const pkgPath = join5(__dirname, "..", "package.json");
|
|
1544
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
1545
|
+
return pkg.version ?? "0.0.0";
|
|
1546
|
+
} catch {
|
|
1547
|
+
return "0.0.0";
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
async function autoTimeoutCheck() {
|
|
1551
|
+
const { checkTimeout: checkTimeout2, loadState: loadState2, clearState: clearState2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
1552
|
+
const { verifyPort: verifyPort2 } = await Promise.resolve().then(() => (init_port_manager(), port_manager_exports));
|
|
1553
|
+
const { expired, minutesIdle } = checkTimeout2();
|
|
1554
|
+
if (!expired) return;
|
|
1555
|
+
const state = loadState2();
|
|
1556
|
+
if (!state) return;
|
|
1557
|
+
const stillAlive = await verifyPort2(state.port);
|
|
1558
|
+
if (stillAlive && state.pid > 0) {
|
|
1559
|
+
try {
|
|
1560
|
+
process.kill(state.pid, "SIGTERM");
|
|
1561
|
+
} catch {
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
clearState2();
|
|
1565
|
+
const chalk4 = (await import("chalk")).default;
|
|
1566
|
+
console.error(
|
|
1567
|
+
chalk4.yellow(`\u23F1\uFE0F \u8C03\u8BD5\u7AEF\u53E3\u5DF2\u7A7A\u95F2 ${minutesIdle} \u5206\u949F\uFF0C\u5DF2\u81EA\u52A8\u5173\u95ED (\u7AEF\u53E3 ${state.port})`)
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
var program = new Command();
|
|
1571
|
+
program.name("web-probe").description("AI \u6D4F\u89C8\u5668\u81EA\u52A8\u5316 CLI \u5DE5\u5177").version(getVersion(), "-v, --version").hook("preAction", async () => {
|
|
1572
|
+
await autoTimeoutCheck();
|
|
1573
|
+
});
|
|
1574
|
+
var chrome = program.command("chrome").description("Chrome \u8C03\u8BD5\u7AEF\u53E3\u7BA1\u7406");
|
|
1575
|
+
chrome.command("start").description("\u542F\u52A8 Chrome \u8C03\u8BD5\u7AEF\u53E3").option("--port <port>", "\u6307\u5B9A\u7AEF\u53E3\uFF08\u9ED8\u8BA4\u968F\u673A 20000-29999\uFF09").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1576
|
+
const { chromeStart: chromeStart2 } = await Promise.resolve().then(() => (init_chrome(), chrome_exports));
|
|
1577
|
+
await chromeStart2(opts);
|
|
1578
|
+
});
|
|
1579
|
+
chrome.command("stop").description("\u5173\u95ED\u8C03\u8BD5\u7AEF\u53E3\uFF08\u4E0D\u5173 Chrome \u672C\u4F53\uFF09").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1580
|
+
const { chromeStop: chromeStop2 } = await Promise.resolve().then(() => (init_chrome(), chrome_exports));
|
|
1581
|
+
await chromeStop2(opts);
|
|
1582
|
+
});
|
|
1583
|
+
chrome.command("status").description("\u663E\u793A\u5F53\u524D\u8C03\u8BD5\u7AEF\u53E3\u72B6\u6001").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1584
|
+
const { chromeStatus: chromeStatus2 } = await Promise.resolve().then(() => (init_chrome(), chrome_exports));
|
|
1585
|
+
await chromeStatus2(opts);
|
|
1586
|
+
});
|
|
1587
|
+
chrome.command("restart").description("\u91CD\u542F\u8C03\u8BD5\uFF08\u65B0\u968F\u673A\u7AEF\u53E3\uFF09").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1588
|
+
const { chromeRestart: chromeRestart2 } = await Promise.resolve().then(() => (init_chrome(), chrome_exports));
|
|
1589
|
+
await chromeRestart2(opts);
|
|
1590
|
+
});
|
|
1591
|
+
program.command("tabs").description("\u5217\u51FA\u6240\u6709\u6253\u5F00\u7684\u6807\u7B7E\u9875").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1592
|
+
const { tabsCommand: tabsCommand2 } = await Promise.resolve().then(() => (init_tabs(), tabs_exports));
|
|
1593
|
+
await tabsCommand2(opts);
|
|
1594
|
+
});
|
|
1595
|
+
program.command("inspect <target>").description("\u63D0\u53D6\u9875\u9762\u5B8C\u6574\u7ED3\u6784\uFF08JSON \u8F93\u51FA\uFF09").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", true).action(async (target, opts) => {
|
|
1596
|
+
const { inspectCommand: inspectCommand2 } = await Promise.resolve().then(() => (init_inspect(), inspect_exports));
|
|
1597
|
+
await inspectCommand2(target, opts);
|
|
1598
|
+
});
|
|
1599
|
+
program.command("shot <target>").description("\u622A\u56FE\uFF08target \u53EF\u4EE5\u662F tabId \u6216 URL\uFF09").option("-o, --output <path>", "\u6307\u5B9A\u8F93\u51FA\u8DEF\u5F84").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (target, opts) => {
|
|
1600
|
+
const { shotCommand: shotCommand2 } = await Promise.resolve().then(() => (init_shot(), shot_exports));
|
|
1601
|
+
await shotCommand2(target, opts);
|
|
1602
|
+
});
|
|
1603
|
+
program.command("eval <target> <javascript>").description("\u5728\u6807\u7B7E\u9875\u4E2D\u6267\u884C JavaScript").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (target, javascript, opts) => {
|
|
1604
|
+
const { evalCommand: evalCommand2 } = await Promise.resolve().then(() => (init_eval(), eval_exports));
|
|
1605
|
+
await evalCommand2(target, javascript, opts);
|
|
1606
|
+
});
|
|
1607
|
+
program.command("click <target> <selector>").description("\u70B9\u51FB\u5143\u7D20").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (target, selector, opts) => {
|
|
1608
|
+
const { clickCommand: clickCommand2 } = await Promise.resolve().then(() => (init_click(), click_exports));
|
|
1609
|
+
await clickCommand2(target, selector, opts);
|
|
1610
|
+
});
|
|
1611
|
+
program.command("type <target> <selector> <text>").description("\u5728\u5143\u7D20\u4E2D\u8F93\u5165\u6587\u5B57").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (target, selector, text, opts) => {
|
|
1612
|
+
const { typeCommand: typeCommand2 } = await Promise.resolve().then(() => (init_type(), type_exports));
|
|
1613
|
+
await typeCommand2(target, selector, text, opts);
|
|
1614
|
+
});
|
|
1615
|
+
program.command("nav <target> <url>").description("\u5BFC\u822A\u6807\u7B7E\u9875\u5230\u65B0 URL").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (target, url, opts) => {
|
|
1616
|
+
const { navCommand: navCommand2 } = await Promise.resolve().then(() => (init_nav(), nav_exports));
|
|
1617
|
+
await navCommand2(target, url, opts);
|
|
1618
|
+
});
|
|
1619
|
+
program.command("html <target> [selector]").description("\u63D0\u53D6 HTML \u5185\u5BB9").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (target, selector, opts) => {
|
|
1620
|
+
const { htmlCommand: htmlCommand2 } = await Promise.resolve().then(() => (init_html(), html_exports));
|
|
1621
|
+
await htmlCommand2(target, selector, opts);
|
|
1622
|
+
});
|
|
1623
|
+
program.command("doctor").description("\u73AF\u5883\u8BCA\u65AD\uFF08\u68C0\u67E5\u6240\u6709\u4F9D\u8D56\uFF0C\u8F93\u51FA\u72B6\u6001\u8868\uFF09").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1624
|
+
const { doctorCommand: doctorCommand2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
|
|
1625
|
+
await doctorCommand2(opts);
|
|
1626
|
+
});
|
|
1627
|
+
program.command("setup").description("\u4E00\u952E\u5B89\u88C5\u6240\u6709\u4F9D\u8D56 + \u914D\u7F6E").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1628
|
+
const { setupCommand: setupCommand2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
1629
|
+
await setupCommand2(opts);
|
|
1630
|
+
});
|
|
1631
|
+
program.command("explore <url> [goal]").description("AI \u81EA\u4E3B\u63A2\u7D22\u7F51\u7AD9\uFF08\u9700\u5B89\u88C5 @browserbasehq/stagehand\uFF09").option("--model <model>", "LLM \u6A21\u578B\uFF08\u9ED8\u8BA4\u6839\u636E API Key \u81EA\u52A8\u9009\u62E9\uFF09").option("--no-headless", "\u663E\u793A\u6D4F\u89C8\u5668\u7A97\u53E3").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (url, goal, opts) => {
|
|
1632
|
+
const { exploreCommand: exploreCommand2 } = await Promise.resolve().then(() => (init_explore(), explore_exports));
|
|
1633
|
+
await exploreCommand2(url, goal, opts);
|
|
1634
|
+
});
|
|
1635
|
+
var skill = program.command("skill").description("Skill \u7BA1\u7406");
|
|
1636
|
+
skill.command("install").description("\u68C0\u6D4B AI \u5DE5\u5177\u5E76\u6CE8\u5165 Skill").option("--target <tool>", "\u6307\u5B9A\u76EE\u6807\u5DE5\u5177 (cursor|claude-code|codex|agents|opencode|windsurf|copilot|trae)").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4\uFF0C\u81EA\u52A8\u6CE8\u5165\u6240\u6709\u68C0\u6D4B\u5230\u7684\u5DE5\u5177").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA").action(async (opts) => {
|
|
1637
|
+
const { skillInstallCommand: skillInstallCommand2 } = await Promise.resolve().then(() => (init_skill(), skill_exports));
|
|
1638
|
+
await skillInstallCommand2(opts);
|
|
1639
|
+
});
|
|
1640
|
+
program.parse();
|
|
1641
|
+
//# sourceMappingURL=index.js.map
|