bb-browser 0.4.4 → 0.5.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 +116 -102
- package/README.zh-CN.md +114 -100
- package/dist/chunk-AHGAQEFO.js +110 -0
- package/dist/chunk-AHGAQEFO.js.map +1 -0
- package/dist/{chunk-H7M4J4CW.js → chunk-D4HDZEJT.js} +2 -18
- package/dist/chunk-D4HDZEJT.js.map +1 -0
- package/dist/chunk-DBJBHYC7.js +21 -0
- package/dist/chunk-DBJBHYC7.js.map +1 -0
- package/dist/cli.js +1751 -1499
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +2 -1
- package/dist/daemon.js.map +1 -1
- package/dist/jq-HHMLHEPA.js +9 -0
- package/dist/jq-HHMLHEPA.js.map +1 -0
- package/dist/mcp.js +5 -3
- package/dist/mcp.js.map +1 -1
- package/extension/background.js +80 -0
- package/extension/background.js.map +1 -1
- package/extension/dist/background.js +80 -0
- package/extension/dist/background.js.map +1 -1
- package/extension/dist/manifest.json +2 -2
- package/extension/manifest.json +2 -2
- package/package.json +1 -1
- package/dist/chunk-H7M4J4CW.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -3,9 +3,30 @@ import {
|
|
|
3
3
|
COMMAND_TIMEOUT,
|
|
4
4
|
DAEMON_BASE_URL,
|
|
5
5
|
generateId
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DBJBHYC7.js";
|
|
7
|
+
import {
|
|
8
|
+
applyJq
|
|
9
|
+
} from "./chunk-AHGAQEFO.js";
|
|
10
|
+
import "./chunk-D4HDZEJT.js";
|
|
7
11
|
|
|
8
12
|
// packages/cli/src/client.ts
|
|
13
|
+
var jqExpression;
|
|
14
|
+
function setJqExpression(expression) {
|
|
15
|
+
jqExpression = expression;
|
|
16
|
+
}
|
|
17
|
+
function printJqResults(response) {
|
|
18
|
+
const target = response.data ?? response;
|
|
19
|
+
const results = applyJq(target, jqExpression || ".");
|
|
20
|
+
for (const result of results) {
|
|
21
|
+
console.log(typeof result === "string" ? result : JSON.stringify(result));
|
|
22
|
+
}
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
function handleJqResponse(response) {
|
|
26
|
+
if (jqExpression) {
|
|
27
|
+
printJqResults(response);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
9
30
|
async function sendCommand(request) {
|
|
10
31
|
const controller = new AbortController();
|
|
11
32
|
const timeoutId = setTimeout(() => controller.abort(), COMMAND_TIMEOUT);
|
|
@@ -47,7 +68,8 @@ async function sendCommand(request) {
|
|
|
47
68
|
error: `HTTP \u9519\u8BEF: ${res.status} ${res.statusText}`
|
|
48
69
|
};
|
|
49
70
|
}
|
|
50
|
-
|
|
71
|
+
const response = await res.json();
|
|
72
|
+
return response;
|
|
51
73
|
} catch (error) {
|
|
52
74
|
clearTimeout(timeoutId);
|
|
53
75
|
if (error instanceof Error) {
|
|
@@ -150,377 +172,595 @@ async function stopDaemon() {
|
|
|
150
172
|
}
|
|
151
173
|
}
|
|
152
174
|
|
|
153
|
-
// packages/cli/src/commands/
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
// packages/cli/src/commands/site.ts
|
|
176
|
+
import { readFileSync, readdirSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
177
|
+
import { join, relative } from "path";
|
|
178
|
+
import { homedir } from "os";
|
|
179
|
+
import { execSync } from "child_process";
|
|
180
|
+
var BB_DIR = join(homedir(), ".bb-browser");
|
|
181
|
+
var LOCAL_SITES_DIR = join(BB_DIR, "sites");
|
|
182
|
+
var COMMUNITY_SITES_DIR = join(BB_DIR, "bb-sites");
|
|
183
|
+
var COMMUNITY_REPO = "https://github.com/epiral/bb-sites.git";
|
|
184
|
+
function parseSiteMeta(filePath, source) {
|
|
185
|
+
let content;
|
|
186
|
+
try {
|
|
187
|
+
content = readFileSync(filePath, "utf-8");
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
157
190
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
191
|
+
const sitesDir = source === "local" ? LOCAL_SITES_DIR : COMMUNITY_SITES_DIR;
|
|
192
|
+
const relPath = relative(sitesDir, filePath);
|
|
193
|
+
const defaultName = relPath.replace(/\.js$/, "").replace(/\\/g, "/");
|
|
194
|
+
const metaMatch = content.match(/\/\*\s*@meta\s*\n([\s\S]*?)\*\//);
|
|
195
|
+
if (metaMatch) {
|
|
196
|
+
try {
|
|
197
|
+
const metaJson = JSON.parse(metaMatch[1]);
|
|
198
|
+
return {
|
|
199
|
+
name: metaJson.name || defaultName,
|
|
200
|
+
description: metaJson.description || "",
|
|
201
|
+
domain: metaJson.domain || "",
|
|
202
|
+
args: metaJson.args || {},
|
|
203
|
+
capabilities: metaJson.capabilities,
|
|
204
|
+
readOnly: metaJson.readOnly,
|
|
205
|
+
example: metaJson.example,
|
|
206
|
+
filePath,
|
|
207
|
+
source
|
|
208
|
+
};
|
|
209
|
+
} catch {
|
|
210
|
+
}
|
|
162
211
|
}
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
212
|
+
const meta = {
|
|
213
|
+
name: defaultName,
|
|
214
|
+
description: "",
|
|
215
|
+
domain: "",
|
|
216
|
+
args: {},
|
|
217
|
+
filePath,
|
|
218
|
+
source
|
|
167
219
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
220
|
+
const tagPattern = /\/\/\s*@(\w+)[ \t]+(.*)/g;
|
|
221
|
+
let match;
|
|
222
|
+
while ((match = tagPattern.exec(content)) !== null) {
|
|
223
|
+
const [, key, value] = match;
|
|
224
|
+
switch (key) {
|
|
225
|
+
case "name":
|
|
226
|
+
meta.name = value.trim();
|
|
227
|
+
break;
|
|
228
|
+
case "description":
|
|
229
|
+
meta.description = value.trim();
|
|
230
|
+
break;
|
|
231
|
+
case "domain":
|
|
232
|
+
meta.domain = value.trim();
|
|
233
|
+
break;
|
|
234
|
+
case "args":
|
|
235
|
+
for (const arg of value.trim().split(/[,\s]+/).filter(Boolean)) {
|
|
236
|
+
meta.args[arg] = { required: true };
|
|
237
|
+
}
|
|
238
|
+
break;
|
|
239
|
+
case "example":
|
|
240
|
+
meta.example = value.trim();
|
|
241
|
+
break;
|
|
177
242
|
}
|
|
178
243
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
244
|
+
return meta;
|
|
245
|
+
}
|
|
246
|
+
function scanSites(dir, source) {
|
|
247
|
+
if (!existsSync2(dir)) return [];
|
|
248
|
+
const sites = [];
|
|
249
|
+
function walk(currentDir) {
|
|
250
|
+
let entries;
|
|
251
|
+
try {
|
|
252
|
+
entries = readdirSync(currentDir, { withFileTypes: true });
|
|
253
|
+
} catch {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
for (const entry of entries) {
|
|
257
|
+
const fullPath = join(currentDir, entry.name);
|
|
258
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
259
|
+
walk(fullPath);
|
|
260
|
+
} else if (entry.isFile() && entry.name.endsWith(".js")) {
|
|
261
|
+
const meta = parseSiteMeta(fullPath, source);
|
|
262
|
+
if (meta) sites.push(meta);
|
|
190
263
|
}
|
|
191
|
-
} else {
|
|
192
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
193
|
-
process.exit(1);
|
|
194
264
|
}
|
|
195
265
|
}
|
|
266
|
+
walk(dir);
|
|
267
|
+
return sites;
|
|
196
268
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
tabId: options.tabId
|
|
209
|
-
};
|
|
210
|
-
const response = await sendCommand(request);
|
|
211
|
-
if (options.json) {
|
|
212
|
-
console.log(JSON.stringify(response, null, 2));
|
|
213
|
-
} else {
|
|
214
|
-
if (response.success) {
|
|
215
|
-
console.log(`\u6807\u9898: ${response.data?.title ?? "(\u65E0\u6807\u9898)"}`);
|
|
216
|
-
console.log(`URL: ${response.data?.url ?? "(\u672A\u77E5)"}`);
|
|
217
|
-
if (response.data?.snapshotData?.snapshot) {
|
|
218
|
-
console.log("");
|
|
219
|
-
console.log(response.data.snapshotData.snapshot);
|
|
220
|
-
}
|
|
221
|
-
} else {
|
|
222
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
223
|
-
process.exit(1);
|
|
224
|
-
}
|
|
269
|
+
function getSiteHintForDomain(url) {
|
|
270
|
+
try {
|
|
271
|
+
const hostname = new URL(url).hostname;
|
|
272
|
+
const sites = getAllSites();
|
|
273
|
+
const matched = sites.filter((s) => s.domain && (hostname === s.domain || hostname.endsWith("." + s.domain)));
|
|
274
|
+
if (matched.length === 0) return null;
|
|
275
|
+
const names = matched.map((s) => s.name);
|
|
276
|
+
const example = matched[0].example || `bb-browser site ${names[0]}`;
|
|
277
|
+
return `\u8BE5\u7F51\u7AD9\u6709 ${names.length} \u4E2A site adapter \u53EF\u76F4\u63A5\u83B7\u53D6\u6570\u636E\uFF0C\u65E0\u9700\u624B\u52A8\u64CD\u4F5C\u6D4F\u89C8\u5668\u3002\u8BD5\u8BD5: ${example}`;
|
|
278
|
+
} catch {
|
|
279
|
+
return null;
|
|
225
280
|
}
|
|
226
281
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
282
|
+
function getAllSites() {
|
|
283
|
+
const community = scanSites(COMMUNITY_SITES_DIR, "community");
|
|
284
|
+
const local = scanSites(LOCAL_SITES_DIR, "local");
|
|
285
|
+
const byName = /* @__PURE__ */ new Map();
|
|
286
|
+
for (const s of community) byName.set(s.name, s);
|
|
287
|
+
for (const s of local) byName.set(s.name, s);
|
|
288
|
+
return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
231
289
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
290
|
+
function matchTabOrigin(tabUrl, domain) {
|
|
291
|
+
try {
|
|
292
|
+
const tabOrigin = new URL(tabUrl).hostname;
|
|
293
|
+
return tabOrigin === domain || tabOrigin.endsWith("." + domain);
|
|
294
|
+
} catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function siteList(options) {
|
|
299
|
+
const sites = getAllSites();
|
|
300
|
+
if (sites.length === 0) {
|
|
301
|
+
console.log("\u672A\u627E\u5230\u4EFB\u4F55 site adapter\u3002");
|
|
302
|
+
console.log(" \u5B89\u88C5\u793E\u533A adapter: bb-browser site update");
|
|
303
|
+
console.log(` \u79C1\u6709 adapter \u76EE\u5F55: ${LOCAL_SITES_DIR}`);
|
|
304
|
+
return;
|
|
235
305
|
}
|
|
236
|
-
await ensureDaemonRunning();
|
|
237
|
-
const parsedRef = parseRef(ref);
|
|
238
|
-
const request = {
|
|
239
|
-
id: generateId(),
|
|
240
|
-
action: "click",
|
|
241
|
-
ref: parsedRef,
|
|
242
|
-
tabId: options.tabId
|
|
243
|
-
};
|
|
244
|
-
const response = await sendCommand(request);
|
|
245
306
|
if (options.json) {
|
|
246
|
-
console.log(JSON.stringify(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
307
|
+
console.log(JSON.stringify(sites.map((s) => ({
|
|
308
|
+
name: s.name,
|
|
309
|
+
description: s.description,
|
|
310
|
+
domain: s.domain,
|
|
311
|
+
args: s.args,
|
|
312
|
+
source: s.source
|
|
313
|
+
})), null, 2));
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const groups = /* @__PURE__ */ new Map();
|
|
317
|
+
for (const s of sites) {
|
|
318
|
+
const platform = s.name.split("/")[0];
|
|
319
|
+
if (!groups.has(platform)) groups.set(platform, []);
|
|
320
|
+
groups.get(platform).push(s);
|
|
321
|
+
}
|
|
322
|
+
for (const [platform, items] of groups) {
|
|
323
|
+
console.log(`
|
|
324
|
+
${platform}/`);
|
|
325
|
+
for (const s of items) {
|
|
326
|
+
const cmd = s.name.split("/").slice(1).join("/");
|
|
327
|
+
const src = s.source === "local" ? " (local)" : "";
|
|
328
|
+
const desc = s.description ? ` - ${s.description}` : "";
|
|
329
|
+
console.log(` ${cmd.padEnd(20)}${desc}${src}`);
|
|
259
330
|
}
|
|
260
331
|
}
|
|
332
|
+
console.log();
|
|
261
333
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
334
|
+
function siteSearch(query, options) {
|
|
335
|
+
const sites = getAllSites();
|
|
336
|
+
const q = query.toLowerCase();
|
|
337
|
+
const matches = sites.filter(
|
|
338
|
+
(s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.domain.toLowerCase().includes(q)
|
|
339
|
+
);
|
|
340
|
+
if (matches.length === 0) {
|
|
341
|
+
console.log(`\u672A\u627E\u5230\u5339\u914D "${query}" \u7684 adapter\u3002`);
|
|
342
|
+
console.log(" \u67E5\u770B\u6240\u6709: bb-browser site list");
|
|
343
|
+
return;
|
|
270
344
|
}
|
|
271
|
-
await ensureDaemonRunning();
|
|
272
|
-
const parsedRef = parseRef2(ref);
|
|
273
|
-
const request = {
|
|
274
|
-
id: generateId(),
|
|
275
|
-
action: "hover",
|
|
276
|
-
ref: parsedRef,
|
|
277
|
-
tabId: options.tabId
|
|
278
|
-
};
|
|
279
|
-
const response = await sendCommand(request);
|
|
280
345
|
if (options.json) {
|
|
281
|
-
console.log(JSON.stringify(
|
|
346
|
+
console.log(JSON.stringify(matches.map((s) => ({
|
|
347
|
+
name: s.name,
|
|
348
|
+
description: s.description,
|
|
349
|
+
domain: s.domain,
|
|
350
|
+
source: s.source
|
|
351
|
+
})), null, 2));
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
for (const s of matches) {
|
|
355
|
+
const src = s.source === "local" ? " (local)" : "";
|
|
356
|
+
console.log(`${s.name.padEnd(24)} ${s.description}${src}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function siteUpdate() {
|
|
360
|
+
mkdirSync(BB_DIR, { recursive: true });
|
|
361
|
+
if (existsSync2(join(COMMUNITY_SITES_DIR, ".git"))) {
|
|
362
|
+
console.log("\u66F4\u65B0\u793E\u533A site adapter \u5E93...");
|
|
363
|
+
try {
|
|
364
|
+
execSync("git pull --ff-only", { cwd: COMMUNITY_SITES_DIR, stdio: "pipe" });
|
|
365
|
+
console.log("\u66F4\u65B0\u5B8C\u6210\u3002");
|
|
366
|
+
console.log("");
|
|
367
|
+
console.log("\u{1F4A1} \u8FD0\u884C bb-browser site recommend \u770B\u770B\u54EA\u4E9B\u548C\u4F60\u7684\u6D4F\u89C8\u4E60\u60EF\u5339\u914D");
|
|
368
|
+
} catch (e) {
|
|
369
|
+
console.error(`\u66F4\u65B0\u5931\u8D25: ${e instanceof Error ? e.message : e}`);
|
|
370
|
+
console.error(" \u624B\u52A8\u4FEE\u590D: cd ~/.bb-browser/bb-sites && git pull");
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
282
373
|
} else {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
374
|
+
console.log(`\u514B\u9686\u793E\u533A adapter \u5E93: ${COMMUNITY_REPO}`);
|
|
375
|
+
try {
|
|
376
|
+
execSync(`git clone ${COMMUNITY_REPO} ${COMMUNITY_SITES_DIR}`, { stdio: "pipe" });
|
|
377
|
+
console.log("\u514B\u9686\u5B8C\u6210\u3002");
|
|
378
|
+
console.log("");
|
|
379
|
+
console.log("\u{1F4A1} \u8FD0\u884C bb-browser site recommend \u770B\u770B\u54EA\u4E9B\u548C\u4F60\u7684\u6D4F\u89C8\u4E60\u60EF\u5339\u914D");
|
|
380
|
+
} catch (e) {
|
|
381
|
+
console.error(`\u514B\u9686\u5931\u8D25: ${e instanceof Error ? e.message : e}`);
|
|
382
|
+
console.error(` \u624B\u52A8\u4FEE\u590D: git clone ${COMMUNITY_REPO} ~/.bb-browser/bb-sites`);
|
|
293
383
|
process.exit(1);
|
|
294
384
|
}
|
|
295
385
|
}
|
|
386
|
+
const sites = scanSites(COMMUNITY_SITES_DIR, "community");
|
|
387
|
+
console.log(`\u5DF2\u5B89\u88C5 ${sites.length} \u4E2A\u793E\u533A adapter\u3002`);
|
|
296
388
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
function parseRef3(ref) {
|
|
300
|
-
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
389
|
+
function findSiteByName(name) {
|
|
390
|
+
return getAllSites().find((site) => site.name === name);
|
|
301
391
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
392
|
+
function siteInfo(name, options) {
|
|
393
|
+
const site = findSiteByName(name);
|
|
394
|
+
if (!site) {
|
|
395
|
+
console.error(`[error] site info: adapter "${name}" not found.`);
|
|
396
|
+
console.error(" Try: bb-browser site list");
|
|
397
|
+
process.exit(1);
|
|
308
398
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
tabId: options.tabId
|
|
399
|
+
const meta = {
|
|
400
|
+
name: site.name,
|
|
401
|
+
description: site.description,
|
|
402
|
+
domain: site.domain,
|
|
403
|
+
args: site.args,
|
|
404
|
+
example: site.example,
|
|
405
|
+
readOnly: site.readOnly
|
|
317
406
|
};
|
|
318
|
-
const response = await sendCommand(request);
|
|
319
407
|
if (options.json) {
|
|
320
|
-
console.log(JSON.stringify(
|
|
408
|
+
console.log(JSON.stringify(meta, null, 2));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
console.log(`${site.name} \u2014 ${site.description}`);
|
|
412
|
+
console.log();
|
|
413
|
+
console.log("\u53C2\u6570\uFF1A");
|
|
414
|
+
const argEntries = Object.entries(site.args);
|
|
415
|
+
if (argEntries.length === 0) {
|
|
416
|
+
console.log(" \uFF08\u65E0\uFF09");
|
|
321
417
|
} else {
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
console.log(`\u5DF2\u586B\u5145: ${role} "${name}"`);
|
|
327
|
-
} else {
|
|
328
|
-
console.log(`\u5DF2\u586B\u5145: ${role}`);
|
|
329
|
-
}
|
|
330
|
-
console.log(`\u5185\u5BB9: "${text}"`);
|
|
331
|
-
} else {
|
|
332
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
333
|
-
process.exit(1);
|
|
418
|
+
for (const [argName, argDef] of argEntries) {
|
|
419
|
+
const requiredText = argDef.required ? "\u5FC5\u586B" : "\u53EF\u9009";
|
|
420
|
+
const description = argDef.description || "";
|
|
421
|
+
console.log(` ${argName} (${requiredText}) ${description}`.trimEnd());
|
|
334
422
|
}
|
|
335
423
|
}
|
|
424
|
+
console.log();
|
|
425
|
+
console.log("\u793A\u4F8B\uFF1A");
|
|
426
|
+
console.log(` ${site.example || `bb-browser site ${site.name}`}`);
|
|
427
|
+
console.log();
|
|
428
|
+
console.log(`\u57DF\u540D\uFF1A${site.domain || "\uFF08\u672A\u58F0\u660E\uFF09"}`);
|
|
429
|
+
console.log(`\u53EA\u8BFB\uFF1A${site.readOnly ? "\u662F" : "\u5426"}`);
|
|
336
430
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
431
|
+
async function siteRecommend(options) {
|
|
432
|
+
const days = options.days ?? 30;
|
|
433
|
+
const response = await sendCommand({
|
|
434
|
+
id: generateId(),
|
|
435
|
+
action: "history",
|
|
436
|
+
historyCommand: "domains",
|
|
437
|
+
ms: days
|
|
438
|
+
});
|
|
439
|
+
if (!response.success) {
|
|
440
|
+
throw new Error(response.error || "History command failed");
|
|
345
441
|
}
|
|
346
|
-
|
|
347
|
-
|
|
442
|
+
const historyDomains = response.data?.historyDomains || [];
|
|
443
|
+
const sites = getAllSites();
|
|
444
|
+
const sitesByDomain = /* @__PURE__ */ new Map();
|
|
445
|
+
for (const site of sites) {
|
|
446
|
+
if (!site.domain) continue;
|
|
447
|
+
const domain = site.domain.toLowerCase();
|
|
448
|
+
const existing = sitesByDomain.get(domain) || [];
|
|
449
|
+
existing.push(site);
|
|
450
|
+
sitesByDomain.set(domain, existing);
|
|
451
|
+
}
|
|
452
|
+
const available = [];
|
|
453
|
+
const notAvailable = [];
|
|
454
|
+
for (const item of historyDomains) {
|
|
455
|
+
const adapters = sitesByDomain.get(item.domain.toLowerCase());
|
|
456
|
+
if (adapters && adapters.length > 0) {
|
|
457
|
+
const sortedAdapters = [...adapters].sort((a, b) => a.name.localeCompare(b.name));
|
|
458
|
+
available.push({
|
|
459
|
+
domain: item.domain,
|
|
460
|
+
visits: item.visits,
|
|
461
|
+
adapterCount: sortedAdapters.length,
|
|
462
|
+
adapters: sortedAdapters.map((site) => ({
|
|
463
|
+
name: site.name,
|
|
464
|
+
description: site.description,
|
|
465
|
+
example: site.example || `bb-browser site ${site.name}`
|
|
466
|
+
}))
|
|
467
|
+
});
|
|
468
|
+
} else if (item.visits >= 5 && item.domain && !item.domain.includes("localhost") && item.domain.includes(".")) {
|
|
469
|
+
notAvailable.push(item);
|
|
470
|
+
}
|
|
348
471
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
action: "type",
|
|
354
|
-
ref: parsedRef,
|
|
355
|
-
text,
|
|
356
|
-
tabId: options.tabId
|
|
472
|
+
const jsonData = {
|
|
473
|
+
days,
|
|
474
|
+
available,
|
|
475
|
+
not_available: notAvailable
|
|
357
476
|
};
|
|
358
|
-
|
|
477
|
+
if (options.jq) {
|
|
478
|
+
handleJqResponse({ id: generateId(), success: true, data: jsonData });
|
|
479
|
+
}
|
|
359
480
|
if (options.json) {
|
|
360
|
-
console.log(JSON.stringify(
|
|
481
|
+
console.log(JSON.stringify(jsonData, null, 2));
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
console.log(`\u57FA\u4E8E\u4F60\u6700\u8FD1 ${days} \u5929\u7684\u6D4F\u89C8\u8BB0\u5F55\uFF1A`);
|
|
485
|
+
console.log();
|
|
486
|
+
console.log("\u{1F3AF} \u4F60\u5E38\u7528\u8FD9\u4E9B\u7F51\u7AD9\uFF0C\u53EF\u4EE5\u76F4\u63A5\u7528\uFF1A");
|
|
487
|
+
console.log();
|
|
488
|
+
if (available.length === 0) {
|
|
489
|
+
console.log(" \uFF08\u6682\u65E0\u5339\u914D\u7684 adapter\uFF09");
|
|
361
490
|
} else {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
491
|
+
for (const item of available) {
|
|
492
|
+
console.log(` ${item.domain.padEnd(20)} ${item.visits} \u6B21\u8BBF\u95EE ${item.adapterCount} \u4E2A\u547D\u4EE4`);
|
|
493
|
+
console.log(` \u8BD5\u8BD5: ${item.adapters[0]?.example || `bb-browser site ${item.adapters[0]?.name || ""}`}`);
|
|
494
|
+
console.log();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
console.log("\u{1F4CB} \u4F60\u5E38\u7528\u4F46\u8FD8\u6CA1\u6709 adapter\uFF1A");
|
|
498
|
+
console.log();
|
|
499
|
+
if (notAvailable.length === 0) {
|
|
500
|
+
console.log(" \uFF08\u6682\u65E0\uFF09");
|
|
501
|
+
} else {
|
|
502
|
+
for (const item of notAvailable) {
|
|
503
|
+
console.log(` ${item.domain.padEnd(20)} ${item.visits} \u6B21\u8BBF\u95EE`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
console.log();
|
|
507
|
+
console.log('\u{1F4A1} \u8DDF\u4F60\u7684 AI Agent \u8BF4 "\u628A notion.so CLI \u5316"\uFF0C\u5B83\u5C31\u80FD\u81EA\u52A8\u5B8C\u6210\u3002');
|
|
508
|
+
console.log();
|
|
509
|
+
console.log(`\u6240\u6709\u5206\u6790\u7EAF\u672C\u5730\u5B8C\u6210\u3002\u7528 --days 7 \u53EA\u770B\u6700\u8FD1\u4E00\u5468\u3002`);
|
|
510
|
+
}
|
|
511
|
+
async function siteRun(name, args, options) {
|
|
512
|
+
const sites = getAllSites();
|
|
513
|
+
const site = sites.find((s) => s.name === name);
|
|
514
|
+
if (!site) {
|
|
515
|
+
const fuzzy = sites.filter((s) => s.name.includes(name));
|
|
516
|
+
console.error(`[error] site: "${name}" not found.`);
|
|
517
|
+
if (fuzzy.length > 0) {
|
|
518
|
+
console.error(" Did you mean:");
|
|
519
|
+
for (const s of fuzzy.slice(0, 5)) {
|
|
520
|
+
console.error(` bb-browser site ${s.name}`);
|
|
369
521
|
}
|
|
370
|
-
console.log(`\u5185\u5BB9: "${text}"`);
|
|
371
522
|
} else {
|
|
372
|
-
console.error(
|
|
373
|
-
|
|
523
|
+
console.error(" Try: bb-browser site list");
|
|
524
|
+
console.error(" Or: bb-browser site update");
|
|
374
525
|
}
|
|
526
|
+
process.exit(1);
|
|
375
527
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
};
|
|
386
|
-
const response = await sendCommand(request);
|
|
387
|
-
if (options.json) {
|
|
388
|
-
console.log(JSON.stringify(response, null, 2));
|
|
389
|
-
} else {
|
|
390
|
-
if (response.success) {
|
|
391
|
-
const title = response.data?.title ?? "";
|
|
392
|
-
if (title) {
|
|
393
|
-
console.log(`\u5DF2\u5173\u95ED: "${title}"`);
|
|
394
|
-
} else {
|
|
395
|
-
console.log("\u5DF2\u5173\u95ED\u5F53\u524D\u6807\u7B7E\u9875");
|
|
528
|
+
const argNames = Object.keys(site.args);
|
|
529
|
+
const argMap = {};
|
|
530
|
+
const positionalArgs = [];
|
|
531
|
+
for (let i = 0; i < args.length; i++) {
|
|
532
|
+
if (args[i].startsWith("--")) {
|
|
533
|
+
const flagName = args[i].slice(2);
|
|
534
|
+
if (flagName in site.args && args[i + 1]) {
|
|
535
|
+
argMap[flagName] = args[i + 1];
|
|
536
|
+
i++;
|
|
396
537
|
}
|
|
397
538
|
} else {
|
|
398
|
-
|
|
399
|
-
process.exit(1);
|
|
539
|
+
positionalArgs.push(args[i]);
|
|
400
540
|
}
|
|
401
541
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
async function getCommand(attribute, ref, options = {}) {
|
|
409
|
-
if (attribute === "text" && !ref) {
|
|
410
|
-
throw new Error("get text \u9700\u8981 ref \u53C2\u6570\uFF0C\u5982: get text @5");
|
|
542
|
+
let posIdx = 0;
|
|
543
|
+
for (const argName of argNames) {
|
|
544
|
+
if (!argMap[argName] && posIdx < positionalArgs.length) {
|
|
545
|
+
argMap[argName] = positionalArgs[posIdx++];
|
|
546
|
+
}
|
|
411
547
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
if (options.json) {
|
|
422
|
-
console.log(JSON.stringify(response, null, 2));
|
|
423
|
-
} else {
|
|
424
|
-
if (response.success) {
|
|
425
|
-
const value = response.data?.value ?? "";
|
|
426
|
-
console.log(value);
|
|
427
|
-
} else {
|
|
428
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
548
|
+
for (const [argName, argDef] of Object.entries(site.args)) {
|
|
549
|
+
if (argDef.required && !argMap[argName]) {
|
|
550
|
+
console.error(`[error] site ${name}: missing required argument "${argName}".`);
|
|
551
|
+
const usage = argNames.map((a) => {
|
|
552
|
+
const def = site.args[a];
|
|
553
|
+
return def.required ? `<${a}>` : `[${a}]`;
|
|
554
|
+
}).join(" ");
|
|
555
|
+
console.error(` Usage: bb-browser site ${name} ${usage}`);
|
|
556
|
+
if (site.example) console.error(` Example: ${site.example}`);
|
|
429
557
|
process.exit(1);
|
|
430
558
|
}
|
|
431
559
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
import path from "path";
|
|
437
|
-
import os from "os";
|
|
438
|
-
function getDefaultPath() {
|
|
439
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
440
|
-
const filename = `bb-screenshot-${timestamp}.png`;
|
|
441
|
-
return path.join(os.tmpdir(), filename);
|
|
442
|
-
}
|
|
443
|
-
function saveBase64Image(dataUrl, filePath) {
|
|
444
|
-
const base64Data = dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
445
|
-
const buffer = Buffer.from(base64Data, "base64");
|
|
446
|
-
const dir = path.dirname(filePath);
|
|
447
|
-
if (!fs.existsSync(dir)) {
|
|
448
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
449
|
-
}
|
|
450
|
-
fs.writeFileSync(filePath, buffer);
|
|
451
|
-
}
|
|
452
|
-
async function screenshotCommand(outputPath, options = {}) {
|
|
560
|
+
const jsContent = readFileSync(site.filePath, "utf-8");
|
|
561
|
+
const jsBody = jsContent.replace(/\/\*\s*@meta[\s\S]*?\*\//, "").trim();
|
|
562
|
+
const argsJson = JSON.stringify(argMap);
|
|
563
|
+
const script = `(${jsBody})(${argsJson})`;
|
|
453
564
|
await ensureDaemonRunning();
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
id: generateId(),
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
565
|
+
let targetTabId = options.tabId;
|
|
566
|
+
if (!targetTabId && site.domain) {
|
|
567
|
+
const listReq = { id: generateId(), action: "tab_list" };
|
|
568
|
+
const listResp = await sendCommand(listReq);
|
|
569
|
+
if (listResp.success && listResp.data?.tabs) {
|
|
570
|
+
const matchingTab = listResp.data.tabs.find(
|
|
571
|
+
(tab) => matchTabOrigin(tab.url, site.domain)
|
|
572
|
+
);
|
|
573
|
+
if (matchingTab) {
|
|
574
|
+
targetTabId = matchingTab.tabId;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (!targetTabId) {
|
|
578
|
+
const newResp = await sendCommand({
|
|
579
|
+
id: generateId(),
|
|
580
|
+
action: "tab_new",
|
|
581
|
+
url: `https://${site.domain}`
|
|
582
|
+
});
|
|
583
|
+
targetTabId = newResp.data?.tabId;
|
|
584
|
+
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const evalReq = { id: generateId(), action: "eval", script, tabId: targetTabId };
|
|
588
|
+
const evalResp = await sendCommand(evalReq);
|
|
589
|
+
if (!evalResp.success) {
|
|
590
|
+
const hint = site.domain ? `Open https://${site.domain} in your browser, make sure you are logged in, then retry.` : void 0;
|
|
464
591
|
if (options.json) {
|
|
465
|
-
console.log(JSON.stringify({
|
|
466
|
-
success: true,
|
|
467
|
-
path: filePath,
|
|
468
|
-
base64: dataUrl
|
|
469
|
-
}, null, 2));
|
|
592
|
+
console.log(JSON.stringify({ id: evalReq.id, success: false, error: evalResp.error || "eval failed", hint }));
|
|
470
593
|
} else {
|
|
471
|
-
console.
|
|
594
|
+
console.error(`[error] site ${name}: ${evalResp.error || "eval failed"}`);
|
|
595
|
+
if (hint) console.error(` Hint: ${hint}`);
|
|
472
596
|
}
|
|
473
|
-
|
|
597
|
+
process.exit(1);
|
|
598
|
+
}
|
|
599
|
+
const result = evalResp.data?.result;
|
|
600
|
+
if (result === void 0 || result === null) {
|
|
474
601
|
if (options.json) {
|
|
475
|
-
console.log(JSON.stringify(
|
|
602
|
+
console.log(JSON.stringify({ id: evalReq.id, success: true, data: null }));
|
|
476
603
|
} else {
|
|
477
|
-
console.
|
|
604
|
+
console.log("(no output)");
|
|
605
|
+
}
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
let parsed;
|
|
609
|
+
try {
|
|
610
|
+
parsed = typeof result === "string" ? JSON.parse(result) : result;
|
|
611
|
+
} catch {
|
|
612
|
+
parsed = result;
|
|
613
|
+
}
|
|
614
|
+
if (typeof parsed === "object" && parsed !== null && "error" in parsed) {
|
|
615
|
+
const errObj = parsed;
|
|
616
|
+
const checkText = `${errObj.error} ${errObj.hint || ""}`;
|
|
617
|
+
const isAuthError = /401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth/i.test(checkText);
|
|
618
|
+
const loginHint = isAuthError && site.domain ? `Please log in to https://${site.domain} in your browser first, then retry.` : void 0;
|
|
619
|
+
const hint = loginHint || errObj.hint;
|
|
620
|
+
const reportHint = `If this is an adapter bug, report via: gh issue create --repo epiral/bb-sites --title "[${name}] <description>" OR: bb-browser site github/issue-create epiral/bb-sites --title "[${name}] <description>"`;
|
|
621
|
+
if (options.json) {
|
|
622
|
+
console.log(JSON.stringify({ id: evalReq.id, success: false, error: errObj.error, hint, reportHint }));
|
|
623
|
+
} else {
|
|
624
|
+
console.error(`[error] site ${name}: ${errObj.error}`);
|
|
625
|
+
if (hint) console.error(` Hint: ${hint}`);
|
|
626
|
+
console.error(` Report: gh issue create --repo epiral/bb-sites --title "[${name}] ..."`);
|
|
627
|
+
console.error(` or: bb-browser site github/issue-create epiral/bb-sites --title "[${name}] ..."`);
|
|
478
628
|
}
|
|
479
629
|
process.exit(1);
|
|
480
630
|
}
|
|
631
|
+
if (options.jq) {
|
|
632
|
+
const { applyJq: applyJq2 } = await import("./jq-HHMLHEPA.js");
|
|
633
|
+
const expr = options.jq.replace(/^\.data\./, ".");
|
|
634
|
+
const results = applyJq2(parsed, expr);
|
|
635
|
+
for (const r of results) {
|
|
636
|
+
console.log(typeof r === "string" ? r : JSON.stringify(r));
|
|
637
|
+
}
|
|
638
|
+
} else if (options.json) {
|
|
639
|
+
console.log(JSON.stringify({ id: evalReq.id, success: true, data: parsed }));
|
|
640
|
+
} else {
|
|
641
|
+
console.log(JSON.stringify(parsed, null, 2));
|
|
642
|
+
}
|
|
481
643
|
}
|
|
644
|
+
async function siteCommand(args, options = {}) {
|
|
645
|
+
const subCommand = args[0];
|
|
646
|
+
if (!subCommand || subCommand === "--help" || subCommand === "-h") {
|
|
647
|
+
console.log(`bb-browser site - \u7F51\u7AD9 CLI \u5316\uFF08\u7BA1\u7406\u548C\u8FD0\u884C site adapter\uFF09
|
|
482
648
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
649
|
+
\u7528\u6CD5:
|
|
650
|
+
bb-browser site list \u5217\u51FA\u6240\u6709\u53EF\u7528 adapter
|
|
651
|
+
bb-browser site info <name> \u67E5\u770B adapter \u5143\u4FE1\u606F
|
|
652
|
+
bb-browser site recommend \u57FA\u4E8E\u5386\u53F2\u8BB0\u5F55\u63A8\u8350 adapter
|
|
653
|
+
bb-browser site search <query> \u641C\u7D22 adapter
|
|
654
|
+
bb-browser site <name> [args...] \u8FD0\u884C adapter\uFF08\u7B80\u5199\uFF09
|
|
655
|
+
bb-browser site run <name> [args...] \u8FD0\u884C adapter
|
|
656
|
+
bb-browser site update \u66F4\u65B0\u793E\u533A adapter \u5E93 (git clone/pull)
|
|
657
|
+
|
|
658
|
+
\u76EE\u5F55:
|
|
659
|
+
${LOCAL_SITES_DIR} \u79C1\u6709 adapter\uFF08\u4F18\u5148\uFF09
|
|
660
|
+
${COMMUNITY_SITES_DIR} \u793E\u533A adapter
|
|
661
|
+
|
|
662
|
+
\u793A\u4F8B:
|
|
663
|
+
bb-browser site update
|
|
664
|
+
bb-browser site list
|
|
665
|
+
bb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...
|
|
666
|
+
bb-browser site twitter/user yan5xu
|
|
667
|
+
bb-browser site search reddit
|
|
668
|
+
|
|
669
|
+
\u521B\u5EFA\u65B0 adapter: bb-browser guide
|
|
670
|
+
\u62A5\u544A\u95EE\u9898: gh issue create --repo epiral/bb-sites --title "[adapter-name] \u63CF\u8FF0"
|
|
671
|
+
\u8D21\u732E\u793E\u533A: https://github.com/epiral/bb-sites`);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
switch (subCommand) {
|
|
675
|
+
case "list":
|
|
676
|
+
siteList(options);
|
|
677
|
+
break;
|
|
678
|
+
case "search":
|
|
679
|
+
if (!args[1]) {
|
|
680
|
+
console.error("[error] site search: <query> is required.");
|
|
681
|
+
console.error(" Usage: bb-browser site search <query>");
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
siteSearch(args[1], options);
|
|
685
|
+
break;
|
|
686
|
+
case "info":
|
|
687
|
+
if (!args[1]) {
|
|
688
|
+
console.error("[error] site info: <name> is required.");
|
|
689
|
+
console.error(" Usage: bb-browser site info <name>");
|
|
690
|
+
process.exit(1);
|
|
691
|
+
}
|
|
692
|
+
siteInfo(args[1], options);
|
|
693
|
+
break;
|
|
694
|
+
case "recommend":
|
|
695
|
+
await siteRecommend(options);
|
|
696
|
+
break;
|
|
697
|
+
case "update":
|
|
698
|
+
siteUpdate();
|
|
699
|
+
break;
|
|
700
|
+
case "run":
|
|
701
|
+
if (!args[1]) {
|
|
702
|
+
console.error("[error] site run: <name> is required.");
|
|
703
|
+
console.error(" Usage: bb-browser site run <name> [args...]");
|
|
704
|
+
console.error(" Try: bb-browser site list");
|
|
705
|
+
process.exit(1);
|
|
706
|
+
}
|
|
707
|
+
await siteRun(args[1], args.slice(2), options);
|
|
708
|
+
break;
|
|
709
|
+
default:
|
|
710
|
+
if (subCommand.includes("/")) {
|
|
711
|
+
await siteRun(subCommand, args.slice(1), options);
|
|
712
|
+
} else {
|
|
713
|
+
console.error(`[error] site: unknown subcommand "${subCommand}".`);
|
|
714
|
+
console.error(" Available: list, info, recommend, search, run, update");
|
|
715
|
+
console.error(" Try: bb-browser site --help");
|
|
716
|
+
process.exit(1);
|
|
717
|
+
}
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
489
720
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
721
|
+
|
|
722
|
+
// packages/cli/src/commands/open.ts
|
|
723
|
+
async function openCommand(url, options = {}) {
|
|
724
|
+
if (!url) {
|
|
725
|
+
throw new Error("\u7F3A\u5C11 URL \u53C2\u6570");
|
|
493
726
|
}
|
|
494
727
|
await ensureDaemonRunning();
|
|
495
|
-
let
|
|
496
|
-
if (
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
tabId
|
|
513
|
-
}
|
|
728
|
+
let normalizedUrl = url;
|
|
729
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
730
|
+
normalizedUrl = "https://" + url;
|
|
731
|
+
}
|
|
732
|
+
const request = {
|
|
733
|
+
id: generateId(),
|
|
734
|
+
action: "open",
|
|
735
|
+
url: normalizedUrl
|
|
736
|
+
};
|
|
737
|
+
if (options.tab !== void 0) {
|
|
738
|
+
if (options.tab === "current") {
|
|
739
|
+
request.tabId = "current";
|
|
740
|
+
} else {
|
|
741
|
+
const tabId = parseInt(options.tab, 10);
|
|
742
|
+
if (isNaN(tabId)) {
|
|
743
|
+
throw new Error(`\u65E0\u6548\u7684 tabId: ${options.tab}`);
|
|
744
|
+
}
|
|
745
|
+
request.tabId = tabId;
|
|
746
|
+
}
|
|
514
747
|
}
|
|
515
748
|
const response = await sendCommand(request);
|
|
516
749
|
if (options.json) {
|
|
517
750
|
console.log(JSON.stringify(response, null, 2));
|
|
518
751
|
} else {
|
|
519
752
|
if (response.success) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
753
|
+
console.log(`\u5DF2\u6253\u5F00: ${response.data?.url ?? normalizedUrl}`);
|
|
754
|
+
if (response.data?.title) {
|
|
755
|
+
console.log(`\u6807\u9898: ${response.data.title}`);
|
|
756
|
+
}
|
|
757
|
+
if (response.data?.tabId) {
|
|
758
|
+
console.log(`Tab ID: ${response.data.tabId}`);
|
|
759
|
+
}
|
|
760
|
+
const siteHint = getSiteHintForDomain(normalizedUrl);
|
|
761
|
+
if (siteHint) {
|
|
762
|
+
console.log(`
|
|
763
|
+
\u{1F4A1} ${siteHint}`);
|
|
524
764
|
}
|
|
525
765
|
} else {
|
|
526
766
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -529,35 +769,16 @@ async function waitCommand(target, options = {}) {
|
|
|
529
769
|
}
|
|
530
770
|
}
|
|
531
771
|
|
|
532
|
-
// packages/cli/src/commands/
|
|
533
|
-
function
|
|
534
|
-
const parts = keyString.split("+");
|
|
535
|
-
const modifierNames = ["Control", "Alt", "Shift", "Meta"];
|
|
536
|
-
const modifiers = [];
|
|
537
|
-
let key = "";
|
|
538
|
-
for (const part of parts) {
|
|
539
|
-
if (modifierNames.includes(part)) {
|
|
540
|
-
modifiers.push(part);
|
|
541
|
-
} else {
|
|
542
|
-
key = part;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
return { key, modifiers };
|
|
546
|
-
}
|
|
547
|
-
async function pressCommand(keyString, options = {}) {
|
|
548
|
-
if (!keyString) {
|
|
549
|
-
throw new Error("\u7F3A\u5C11 key \u53C2\u6570");
|
|
550
|
-
}
|
|
772
|
+
// packages/cli/src/commands/snapshot.ts
|
|
773
|
+
async function snapshotCommand(options = {}) {
|
|
551
774
|
await ensureDaemonRunning();
|
|
552
|
-
const { key, modifiers } = parseKey(keyString);
|
|
553
|
-
if (!key) {
|
|
554
|
-
throw new Error("\u65E0\u6548\u7684\u6309\u952E\u683C\u5F0F");
|
|
555
|
-
}
|
|
556
775
|
const request = {
|
|
557
776
|
id: generateId(),
|
|
558
|
-
action: "
|
|
559
|
-
|
|
560
|
-
|
|
777
|
+
action: "snapshot",
|
|
778
|
+
interactive: options.interactive,
|
|
779
|
+
compact: options.compact,
|
|
780
|
+
maxDepth: options.maxDepth,
|
|
781
|
+
selector: options.selector,
|
|
561
782
|
tabId: options.tabId
|
|
562
783
|
};
|
|
563
784
|
const response = await sendCommand(request);
|
|
@@ -565,8 +786,12 @@ async function pressCommand(keyString, options = {}) {
|
|
|
565
786
|
console.log(JSON.stringify(response, null, 2));
|
|
566
787
|
} else {
|
|
567
788
|
if (response.success) {
|
|
568
|
-
|
|
569
|
-
console.log(
|
|
789
|
+
console.log(`\u6807\u9898: ${response.data?.title ?? "(\u65E0\u6807\u9898)"}`);
|
|
790
|
+
console.log(`URL: ${response.data?.url ?? "(\u672A\u77E5)"}`);
|
|
791
|
+
if (response.data?.snapshotData?.snapshot) {
|
|
792
|
+
console.log("");
|
|
793
|
+
console.log(response.data.snapshotData.snapshot);
|
|
794
|
+
}
|
|
570
795
|
} else {
|
|
571
796
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
572
797
|
process.exit(1);
|
|
@@ -574,31 +799,20 @@ async function pressCommand(keyString, options = {}) {
|
|
|
574
799
|
}
|
|
575
800
|
}
|
|
576
801
|
|
|
577
|
-
// packages/cli/src/commands/
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (!VALID_DIRECTIONS.includes(direction)) {
|
|
585
|
-
throw new Error(
|
|
586
|
-
`\u65E0\u6548\u7684\u6EDA\u52A8\u65B9\u5411: ${direction}\uFF0C\u652F\u6301: ${VALID_DIRECTIONS.join(", ")}`
|
|
587
|
-
);
|
|
588
|
-
}
|
|
589
|
-
let pixelValue = DEFAULT_PIXELS;
|
|
590
|
-
if (pixels !== void 0) {
|
|
591
|
-
pixelValue = parseInt(pixels, 10);
|
|
592
|
-
if (isNaN(pixelValue) || pixelValue <= 0) {
|
|
593
|
-
throw new Error(`\u65E0\u6548\u7684\u50CF\u7D20\u503C: ${pixels}`);
|
|
594
|
-
}
|
|
802
|
+
// packages/cli/src/commands/click.ts
|
|
803
|
+
function parseRef(ref) {
|
|
804
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
805
|
+
}
|
|
806
|
+
async function clickCommand(ref, options = {}) {
|
|
807
|
+
if (!ref) {
|
|
808
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
595
809
|
}
|
|
596
810
|
await ensureDaemonRunning();
|
|
811
|
+
const parsedRef = parseRef(ref);
|
|
597
812
|
const request = {
|
|
598
813
|
id: generateId(),
|
|
599
|
-
action: "
|
|
600
|
-
|
|
601
|
-
pixels: pixelValue,
|
|
814
|
+
action: "click",
|
|
815
|
+
ref: parsedRef,
|
|
602
816
|
tabId: options.tabId
|
|
603
817
|
};
|
|
604
818
|
const response = await sendCommand(request);
|
|
@@ -606,7 +820,13 @@ async function scrollCommand(direction, pixels, options = {}) {
|
|
|
606
820
|
console.log(JSON.stringify(response, null, 2));
|
|
607
821
|
} else {
|
|
608
822
|
if (response.success) {
|
|
609
|
-
|
|
823
|
+
const role = response.data?.role ?? "element";
|
|
824
|
+
const name = response.data?.name;
|
|
825
|
+
if (name) {
|
|
826
|
+
console.log(`\u5DF2\u70B9\u51FB: ${role} "${name}"`);
|
|
827
|
+
} else {
|
|
828
|
+
console.log(`\u5DF2\u70B9\u51FB: ${role}`);
|
|
829
|
+
}
|
|
610
830
|
} else {
|
|
611
831
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
612
832
|
process.exit(1);
|
|
@@ -614,188 +834,20 @@ async function scrollCommand(direction, pixels, options = {}) {
|
|
|
614
834
|
}
|
|
615
835
|
}
|
|
616
836
|
|
|
617
|
-
// packages/cli/src/commands/
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if (await isDaemonRunning()) {
|
|
621
|
-
if (options.json) {
|
|
622
|
-
console.log(JSON.stringify({ success: false, error: "Daemon \u5DF2\u5728\u8FD0\u884C" }));
|
|
623
|
-
} else {
|
|
624
|
-
console.log("Daemon \u5DF2\u5728\u8FD0\u884C");
|
|
625
|
-
}
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
const daemonPath = getDaemonPath();
|
|
629
|
-
const args = [daemonPath];
|
|
630
|
-
if (options.host) {
|
|
631
|
-
args.push("--host", options.host);
|
|
632
|
-
}
|
|
633
|
-
if (options.json) {
|
|
634
|
-
console.log(JSON.stringify({ success: true, message: "Daemon \u542F\u52A8\u4E2D..." }));
|
|
635
|
-
} else {
|
|
636
|
-
console.log("Daemon \u542F\u52A8\u4E2D...");
|
|
637
|
-
}
|
|
638
|
-
await new Promise((resolve2, reject) => {
|
|
639
|
-
const child = spawn2(process.execPath, args, {
|
|
640
|
-
stdio: "inherit"
|
|
641
|
-
});
|
|
642
|
-
child.on("exit", (code) => {
|
|
643
|
-
if (code && code !== 0) {
|
|
644
|
-
reject(new Error(`Daemon exited with code ${code}`));
|
|
645
|
-
} else {
|
|
646
|
-
resolve2();
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
child.on("error", reject);
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
async function stopCommand(options = {}) {
|
|
653
|
-
if (!await isDaemonRunning()) {
|
|
654
|
-
if (options.json) {
|
|
655
|
-
console.log(JSON.stringify({ success: false, error: "Daemon \u672A\u8FD0\u884C" }));
|
|
656
|
-
} else {
|
|
657
|
-
console.log("Daemon \u672A\u8FD0\u884C");
|
|
658
|
-
}
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
const stopped = await stopDaemon();
|
|
662
|
-
if (stopped) {
|
|
663
|
-
if (options.json) {
|
|
664
|
-
console.log(JSON.stringify({ success: true, message: "Daemon \u5DF2\u505C\u6B62" }));
|
|
665
|
-
} else {
|
|
666
|
-
console.log("Daemon \u5DF2\u505C\u6B62");
|
|
667
|
-
}
|
|
668
|
-
} else {
|
|
669
|
-
if (options.json) {
|
|
670
|
-
console.log(JSON.stringify({ success: false, error: "\u65E0\u6CD5\u505C\u6B62 Daemon" }));
|
|
671
|
-
} else {
|
|
672
|
-
console.error("\u65E0\u6CD5\u505C\u6B62 Daemon");
|
|
673
|
-
}
|
|
674
|
-
process.exit(1);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
async function statusCommand(options = {}) {
|
|
678
|
-
const running = await isDaemonRunning();
|
|
679
|
-
if (options.json) {
|
|
680
|
-
console.log(JSON.stringify({ running }));
|
|
681
|
-
} else {
|
|
682
|
-
console.log(running ? "Daemon \u8FD0\u884C\u4E2D" : "Daemon \u672A\u8FD0\u884C");
|
|
683
|
-
}
|
|
837
|
+
// packages/cli/src/commands/hover.ts
|
|
838
|
+
function parseRef2(ref) {
|
|
839
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
684
840
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
var EXTENSION_NAME = "bb-browser";
|
|
689
|
-
async function reloadCommand(options = {}) {
|
|
690
|
-
const port = options.port || 9222;
|
|
691
|
-
try {
|
|
692
|
-
const listRes = await fetch(`http://127.0.0.1:${port}/json/list`);
|
|
693
|
-
if (!listRes.ok) {
|
|
694
|
-
throw new Error(`CDP \u672A\u542F\u7528\u3002\u8BF7\u7528 --remote-debugging-port=${port} \u542F\u52A8 Chrome`);
|
|
695
|
-
}
|
|
696
|
-
const list = await listRes.json();
|
|
697
|
-
const extPage = list.find(
|
|
698
|
-
(t) => t.type === "page" && t.url.includes("chrome://extensions")
|
|
699
|
-
);
|
|
700
|
-
if (!extPage) {
|
|
701
|
-
throw new Error("\u8BF7\u5148\u6253\u5F00 chrome://extensions \u9875\u9762");
|
|
702
|
-
}
|
|
703
|
-
const result = await new Promise((resolve2, reject) => {
|
|
704
|
-
const ws = new WebSocket(extPage.webSocketDebuggerUrl);
|
|
705
|
-
let resolved = false;
|
|
706
|
-
const timeout = setTimeout(() => {
|
|
707
|
-
if (!resolved) {
|
|
708
|
-
resolved = true;
|
|
709
|
-
ws.close();
|
|
710
|
-
reject(new Error("CDP \u8FDE\u63A5\u8D85\u65F6"));
|
|
711
|
-
}
|
|
712
|
-
}, 1e4);
|
|
713
|
-
ws.on("open", () => {
|
|
714
|
-
const script = `
|
|
715
|
-
(async function() {
|
|
716
|
-
if (!chrome || !chrome.developerPrivate) {
|
|
717
|
-
return { error: 'developerPrivate API not available' };
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
try {
|
|
721
|
-
const exts = await chrome.developerPrivate.getExtensionsInfo();
|
|
722
|
-
const bbExt = exts.find(e => e.name === '${EXTENSION_NAME}');
|
|
723
|
-
|
|
724
|
-
if (!bbExt) {
|
|
725
|
-
return { error: '${EXTENSION_NAME} \u6269\u5C55\u672A\u5B89\u88C5' };
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
if (bbExt.state !== 'ENABLED') {
|
|
729
|
-
return { error: '${EXTENSION_NAME} \u6269\u5C55\u5DF2\u7981\u7528' };
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
await chrome.developerPrivate.reload(bbExt.id, {failQuietly: true});
|
|
733
|
-
return { success: true, extensionId: bbExt.id };
|
|
734
|
-
} catch (e) {
|
|
735
|
-
return { error: e.message };
|
|
736
|
-
}
|
|
737
|
-
})()
|
|
738
|
-
`;
|
|
739
|
-
ws.send(JSON.stringify({
|
|
740
|
-
id: 1,
|
|
741
|
-
method: "Runtime.evaluate",
|
|
742
|
-
params: {
|
|
743
|
-
expression: script,
|
|
744
|
-
awaitPromise: true,
|
|
745
|
-
returnByValue: true
|
|
746
|
-
}
|
|
747
|
-
}));
|
|
748
|
-
});
|
|
749
|
-
ws.on("message", (data) => {
|
|
750
|
-
const msg = JSON.parse(data.toString());
|
|
751
|
-
if (msg.id === 1) {
|
|
752
|
-
clearTimeout(timeout);
|
|
753
|
-
resolved = true;
|
|
754
|
-
ws.close();
|
|
755
|
-
const value = msg.result?.result?.value;
|
|
756
|
-
if (value?.success) {
|
|
757
|
-
resolve2({
|
|
758
|
-
success: true,
|
|
759
|
-
message: "\u6269\u5C55\u5DF2\u91CD\u8F7D",
|
|
760
|
-
extensionId: value.extensionId
|
|
761
|
-
});
|
|
762
|
-
} else if (value?.error) {
|
|
763
|
-
reject(new Error(value.error));
|
|
764
|
-
} else {
|
|
765
|
-
reject(new Error(`\u91CD\u8F7D\u5931\u8D25: ${JSON.stringify(value)}`));
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
});
|
|
769
|
-
ws.on("error", (err) => {
|
|
770
|
-
clearTimeout(timeout);
|
|
771
|
-
if (!resolved) {
|
|
772
|
-
resolved = true;
|
|
773
|
-
reject(new Error(`CDP \u8FDE\u63A5\u5931\u8D25: ${err.message}`));
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
});
|
|
777
|
-
if (options.json) {
|
|
778
|
-
console.log(JSON.stringify(result));
|
|
779
|
-
} else {
|
|
780
|
-
console.log(`${result.message} (${result.extensionId})`);
|
|
781
|
-
}
|
|
782
|
-
} catch (error) {
|
|
783
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
784
|
-
if (options.json) {
|
|
785
|
-
console.log(JSON.stringify({ success: false, error: message }));
|
|
786
|
-
} else {
|
|
787
|
-
console.error(`\u9519\u8BEF: ${message}`);
|
|
788
|
-
}
|
|
789
|
-
process.exit(1);
|
|
841
|
+
async function hoverCommand(ref, options = {}) {
|
|
842
|
+
if (!ref) {
|
|
843
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
790
844
|
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// packages/cli/src/commands/nav.ts
|
|
794
|
-
async function backCommand(options = {}) {
|
|
795
845
|
await ensureDaemonRunning();
|
|
846
|
+
const parsedRef = parseRef2(ref);
|
|
796
847
|
const request = {
|
|
797
848
|
id: generateId(),
|
|
798
|
-
action: "
|
|
849
|
+
action: "hover",
|
|
850
|
+
ref: parsedRef,
|
|
799
851
|
tabId: options.tabId
|
|
800
852
|
};
|
|
801
853
|
const response = await sendCommand(request);
|
|
@@ -803,11 +855,12 @@ async function backCommand(options = {}) {
|
|
|
803
855
|
console.log(JSON.stringify(response, null, 2));
|
|
804
856
|
} else {
|
|
805
857
|
if (response.success) {
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
858
|
+
const role = response.data?.role ?? "element";
|
|
859
|
+
const name = response.data?.name;
|
|
860
|
+
if (name) {
|
|
861
|
+
console.log(`\u5DF2\u60AC\u505C: ${role} "${name}"`);
|
|
809
862
|
} else {
|
|
810
|
-
console.log(
|
|
863
|
+
console.log(`\u5DF2\u60AC\u505C: ${role}`);
|
|
811
864
|
}
|
|
812
865
|
} else {
|
|
813
866
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -815,11 +868,25 @@ async function backCommand(options = {}) {
|
|
|
815
868
|
}
|
|
816
869
|
}
|
|
817
870
|
}
|
|
818
|
-
|
|
871
|
+
|
|
872
|
+
// packages/cli/src/commands/fill.ts
|
|
873
|
+
function parseRef3(ref) {
|
|
874
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
875
|
+
}
|
|
876
|
+
async function fillCommand(ref, text, options = {}) {
|
|
877
|
+
if (!ref) {
|
|
878
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
879
|
+
}
|
|
880
|
+
if (text === void 0 || text === null) {
|
|
881
|
+
throw new Error("\u7F3A\u5C11 text \u53C2\u6570");
|
|
882
|
+
}
|
|
819
883
|
await ensureDaemonRunning();
|
|
884
|
+
const parsedRef = parseRef3(ref);
|
|
820
885
|
const request = {
|
|
821
886
|
id: generateId(),
|
|
822
|
-
action: "
|
|
887
|
+
action: "fill",
|
|
888
|
+
ref: parsedRef,
|
|
889
|
+
text,
|
|
823
890
|
tabId: options.tabId
|
|
824
891
|
};
|
|
825
892
|
const response = await sendCommand(request);
|
|
@@ -827,36 +894,14 @@ async function forwardCommand(options = {}) {
|
|
|
827
894
|
console.log(JSON.stringify(response, null, 2));
|
|
828
895
|
} else {
|
|
829
896
|
if (response.success) {
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
897
|
+
const role = response.data?.role ?? "element";
|
|
898
|
+
const name = response.data?.name;
|
|
899
|
+
if (name) {
|
|
900
|
+
console.log(`\u5DF2\u586B\u5145: ${role} "${name}"`);
|
|
833
901
|
} else {
|
|
834
|
-
console.log(
|
|
835
|
-
}
|
|
836
|
-
} else {
|
|
837
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
838
|
-
process.exit(1);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
async function refreshCommand(options = {}) {
|
|
843
|
-
await ensureDaemonRunning();
|
|
844
|
-
const request = {
|
|
845
|
-
id: generateId(),
|
|
846
|
-
action: "refresh",
|
|
847
|
-
tabId: options.tabId
|
|
848
|
-
};
|
|
849
|
-
const response = await sendCommand(request);
|
|
850
|
-
if (options.json) {
|
|
851
|
-
console.log(JSON.stringify(response, null, 2));
|
|
852
|
-
} else {
|
|
853
|
-
if (response.success) {
|
|
854
|
-
const title = response.data?.title ?? "";
|
|
855
|
-
if (title) {
|
|
856
|
-
console.log(`\u5DF2\u5237\u65B0: "${title}"`);
|
|
857
|
-
} else {
|
|
858
|
-
console.log("\u5DF2\u5237\u65B0\u9875\u9762");
|
|
902
|
+
console.log(`\u5DF2\u586B\u5145: ${role}`);
|
|
859
903
|
}
|
|
904
|
+
console.log(`\u5185\u5BB9: "${text}"`);
|
|
860
905
|
} else {
|
|
861
906
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
862
907
|
process.exit(1);
|
|
@@ -864,20 +909,24 @@ async function refreshCommand(options = {}) {
|
|
|
864
909
|
}
|
|
865
910
|
}
|
|
866
911
|
|
|
867
|
-
// packages/cli/src/commands/
|
|
868
|
-
function
|
|
912
|
+
// packages/cli/src/commands/type.ts
|
|
913
|
+
function parseRef4(ref) {
|
|
869
914
|
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
870
915
|
}
|
|
871
|
-
async function
|
|
916
|
+
async function typeCommand(ref, text, options = {}) {
|
|
872
917
|
if (!ref) {
|
|
873
918
|
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
874
919
|
}
|
|
920
|
+
if (text === void 0 || text === null) {
|
|
921
|
+
throw new Error("\u7F3A\u5C11 text \u53C2\u6570");
|
|
922
|
+
}
|
|
875
923
|
await ensureDaemonRunning();
|
|
876
|
-
const parsedRef =
|
|
924
|
+
const parsedRef = parseRef4(ref);
|
|
877
925
|
const request = {
|
|
878
926
|
id: generateId(),
|
|
879
|
-
action: "
|
|
927
|
+
action: "type",
|
|
880
928
|
ref: parsedRef,
|
|
929
|
+
text,
|
|
881
930
|
tabId: options.tabId
|
|
882
931
|
};
|
|
883
932
|
const response = await sendCommand(request);
|
|
@@ -885,38 +934,27 @@ async function checkCommand(ref, options = {}) {
|
|
|
885
934
|
console.log(JSON.stringify(response, null, 2));
|
|
886
935
|
} else {
|
|
887
936
|
if (response.success) {
|
|
888
|
-
const role = response.data?.role ?? "
|
|
937
|
+
const role = response.data?.role ?? "element";
|
|
889
938
|
const name = response.data?.name;
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if (name) {
|
|
893
|
-
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
894
|
-
} else {
|
|
895
|
-
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role}`);
|
|
896
|
-
}
|
|
939
|
+
if (name) {
|
|
940
|
+
console.log(`\u5DF2\u8F93\u5165: ${role} "${name}"`);
|
|
897
941
|
} else {
|
|
898
|
-
|
|
899
|
-
console.log(`\u5DF2\u52FE\u9009: ${role} "${name}"`);
|
|
900
|
-
} else {
|
|
901
|
-
console.log(`\u5DF2\u52FE\u9009: ${role}`);
|
|
902
|
-
}
|
|
942
|
+
console.log(`\u5DF2\u8F93\u5165: ${role}`);
|
|
903
943
|
}
|
|
944
|
+
console.log(`\u5185\u5BB9: "${text}"`);
|
|
904
945
|
} else {
|
|
905
946
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
906
947
|
process.exit(1);
|
|
907
948
|
}
|
|
908
949
|
}
|
|
909
950
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
}
|
|
951
|
+
|
|
952
|
+
// packages/cli/src/commands/close.ts
|
|
953
|
+
async function closeCommand(options = {}) {
|
|
914
954
|
await ensureDaemonRunning();
|
|
915
|
-
const parsedRef = parseRef7(ref);
|
|
916
955
|
const request = {
|
|
917
956
|
id: generateId(),
|
|
918
|
-
action: "
|
|
919
|
-
ref: parsedRef,
|
|
957
|
+
action: "close",
|
|
920
958
|
tabId: options.tabId
|
|
921
959
|
};
|
|
922
960
|
const response = await sendCommand(request);
|
|
@@ -924,21 +962,11 @@ async function uncheckCommand(ref, options = {}) {
|
|
|
924
962
|
console.log(JSON.stringify(response, null, 2));
|
|
925
963
|
} else {
|
|
926
964
|
if (response.success) {
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
if (wasAlreadyUnchecked) {
|
|
931
|
-
if (name) {
|
|
932
|
-
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
933
|
-
} else {
|
|
934
|
-
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role}`);
|
|
935
|
-
}
|
|
965
|
+
const title = response.data?.title ?? "";
|
|
966
|
+
if (title) {
|
|
967
|
+
console.log(`\u5DF2\u5173\u95ED: "${title}"`);
|
|
936
968
|
} else {
|
|
937
|
-
|
|
938
|
-
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role} "${name}"`);
|
|
939
|
-
} else {
|
|
940
|
-
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role}`);
|
|
941
|
-
}
|
|
969
|
+
console.log("\u5DF2\u5173\u95ED\u5F53\u524D\u6807\u7B7E\u9875");
|
|
942
970
|
}
|
|
943
971
|
} else {
|
|
944
972
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -947,24 +975,20 @@ async function uncheckCommand(ref, options = {}) {
|
|
|
947
975
|
}
|
|
948
976
|
}
|
|
949
977
|
|
|
950
|
-
// packages/cli/src/commands/
|
|
951
|
-
function
|
|
978
|
+
// packages/cli/src/commands/get.ts
|
|
979
|
+
function parseRef5(ref) {
|
|
952
980
|
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
953
981
|
}
|
|
954
|
-
async function
|
|
955
|
-
if (!ref) {
|
|
956
|
-
throw new Error("\
|
|
957
|
-
}
|
|
958
|
-
if (value === void 0 || value === null) {
|
|
959
|
-
throw new Error("\u7F3A\u5C11 value \u53C2\u6570");
|
|
982
|
+
async function getCommand(attribute, ref, options = {}) {
|
|
983
|
+
if (attribute === "text" && !ref) {
|
|
984
|
+
throw new Error("get text \u9700\u8981 ref \u53C2\u6570\uFF0C\u5982: get text @5");
|
|
960
985
|
}
|
|
961
986
|
await ensureDaemonRunning();
|
|
962
|
-
const parsedRef = parseRef8(ref);
|
|
963
987
|
const request = {
|
|
964
988
|
id: generateId(),
|
|
965
|
-
action: "
|
|
966
|
-
|
|
967
|
-
|
|
989
|
+
action: "get",
|
|
990
|
+
attribute,
|
|
991
|
+
ref: ref ? parseRef5(ref) : void 0,
|
|
968
992
|
tabId: options.tabId
|
|
969
993
|
};
|
|
970
994
|
const response = await sendCommand(request);
|
|
@@ -972,20 +996,8 @@ async function selectCommand(ref, value, options = {}) {
|
|
|
972
996
|
console.log(JSON.stringify(response, null, 2));
|
|
973
997
|
} else {
|
|
974
998
|
if (response.success) {
|
|
975
|
-
const
|
|
976
|
-
|
|
977
|
-
const selectedValue = response.data?.selectedValue;
|
|
978
|
-
const selectedLabel = response.data?.selectedLabel;
|
|
979
|
-
if (name) {
|
|
980
|
-
console.log(`\u5DF2\u9009\u62E9: ${role} "${name}"`);
|
|
981
|
-
} else {
|
|
982
|
-
console.log(`\u5DF2\u9009\u62E9: ${role}`);
|
|
983
|
-
}
|
|
984
|
-
if (selectedLabel && selectedLabel !== selectedValue) {
|
|
985
|
-
console.log(`\u9009\u9879: "${selectedLabel}" (value="${selectedValue}")`);
|
|
986
|
-
} else {
|
|
987
|
-
console.log(`\u9009\u9879: "${selectedValue}"`);
|
|
988
|
-
}
|
|
999
|
+
const value = response.data?.value ?? "";
|
|
1000
|
+
console.log(value);
|
|
989
1001
|
} else {
|
|
990
1002
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
991
1003
|
process.exit(1);
|
|
@@ -993,32 +1005,96 @@ async function selectCommand(ref, value, options = {}) {
|
|
|
993
1005
|
}
|
|
994
1006
|
}
|
|
995
1007
|
|
|
996
|
-
// packages/cli/src/commands/
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1008
|
+
// packages/cli/src/commands/screenshot.ts
|
|
1009
|
+
import fs from "fs";
|
|
1010
|
+
import path from "path";
|
|
1011
|
+
import os from "os";
|
|
1012
|
+
function getDefaultPath() {
|
|
1013
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1014
|
+
const filename = `bb-screenshot-${timestamp}.png`;
|
|
1015
|
+
return path.join(os.tmpdir(), filename);
|
|
1016
|
+
}
|
|
1017
|
+
function saveBase64Image(dataUrl, filePath) {
|
|
1018
|
+
const base64Data = dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
1019
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
1020
|
+
const dir = path.dirname(filePath);
|
|
1021
|
+
if (!fs.existsSync(dir)) {
|
|
1022
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1000
1023
|
}
|
|
1024
|
+
fs.writeFileSync(filePath, buffer);
|
|
1025
|
+
}
|
|
1026
|
+
async function screenshotCommand(outputPath, options = {}) {
|
|
1001
1027
|
await ensureDaemonRunning();
|
|
1028
|
+
const filePath = outputPath ? path.resolve(outputPath) : getDefaultPath();
|
|
1002
1029
|
const request = {
|
|
1003
1030
|
id: generateId(),
|
|
1004
|
-
action: "
|
|
1005
|
-
script,
|
|
1031
|
+
action: "screenshot",
|
|
1006
1032
|
tabId: options.tabId
|
|
1007
1033
|
};
|
|
1008
1034
|
const response = await sendCommand(request);
|
|
1035
|
+
if (response.success && response.data?.dataUrl) {
|
|
1036
|
+
const dataUrl = response.data.dataUrl;
|
|
1037
|
+
saveBase64Image(dataUrl, filePath);
|
|
1038
|
+
if (options.json) {
|
|
1039
|
+
console.log(JSON.stringify({
|
|
1040
|
+
success: true,
|
|
1041
|
+
path: filePath,
|
|
1042
|
+
base64: dataUrl
|
|
1043
|
+
}, null, 2));
|
|
1044
|
+
} else {
|
|
1045
|
+
console.log(`\u622A\u56FE\u5DF2\u4FDD\u5B58: ${filePath}`);
|
|
1046
|
+
}
|
|
1047
|
+
} else {
|
|
1048
|
+
if (options.json) {
|
|
1049
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1050
|
+
} else {
|
|
1051
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1052
|
+
}
|
|
1053
|
+
process.exit(1);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// packages/cli/src/commands/wait.ts
|
|
1058
|
+
function isTimeWait(target) {
|
|
1059
|
+
return /^\d+$/.test(target);
|
|
1060
|
+
}
|
|
1061
|
+
function parseRef6(ref) {
|
|
1062
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
1063
|
+
}
|
|
1064
|
+
async function waitCommand(target, options = {}) {
|
|
1065
|
+
if (!target) {
|
|
1066
|
+
throw new Error("\u7F3A\u5C11\u7B49\u5F85\u76EE\u6807\u53C2\u6570");
|
|
1067
|
+
}
|
|
1068
|
+
await ensureDaemonRunning();
|
|
1069
|
+
let request;
|
|
1070
|
+
if (isTimeWait(target)) {
|
|
1071
|
+
const ms = parseInt(target, 10);
|
|
1072
|
+
request = {
|
|
1073
|
+
id: generateId(),
|
|
1074
|
+
action: "wait",
|
|
1075
|
+
waitType: "time",
|
|
1076
|
+
ms,
|
|
1077
|
+
tabId: options.tabId
|
|
1078
|
+
};
|
|
1079
|
+
} else {
|
|
1080
|
+
const ref = parseRef6(target);
|
|
1081
|
+
request = {
|
|
1082
|
+
id: generateId(),
|
|
1083
|
+
action: "wait",
|
|
1084
|
+
waitType: "element",
|
|
1085
|
+
ref,
|
|
1086
|
+
tabId: options.tabId
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
const response = await sendCommand(request);
|
|
1009
1090
|
if (options.json) {
|
|
1010
1091
|
console.log(JSON.stringify(response, null, 2));
|
|
1011
1092
|
} else {
|
|
1012
1093
|
if (response.success) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
if (typeof result === "object" && result !== null) {
|
|
1016
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1017
|
-
} else {
|
|
1018
|
-
console.log(result);
|
|
1019
|
-
}
|
|
1094
|
+
if (isTimeWait(target)) {
|
|
1095
|
+
console.log(`\u5DF2\u7B49\u5F85 ${target}ms`);
|
|
1020
1096
|
} else {
|
|
1021
|
-
console.log(
|
|
1097
|
+
console.log(`\u5143\u7D20 @${parseRef6(target)} \u5DF2\u51FA\u73B0`);
|
|
1022
1098
|
}
|
|
1023
1099
|
} else {
|
|
1024
1100
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -1027,101 +1103,44 @@ async function evalCommand(script, options = {}) {
|
|
|
1027
1103
|
}
|
|
1028
1104
|
}
|
|
1029
1105
|
|
|
1030
|
-
// packages/cli/src/commands/
|
|
1031
|
-
function
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1106
|
+
// packages/cli/src/commands/press.ts
|
|
1107
|
+
function parseKey(keyString) {
|
|
1108
|
+
const parts = keyString.split("+");
|
|
1109
|
+
const modifierNames = ["Control", "Alt", "Shift", "Meta"];
|
|
1110
|
+
const modifiers = [];
|
|
1111
|
+
let key = "";
|
|
1112
|
+
for (const part of parts) {
|
|
1113
|
+
if (modifierNames.includes(part)) {
|
|
1114
|
+
modifiers.push(part);
|
|
1115
|
+
} else {
|
|
1116
|
+
key = part;
|
|
1040
1117
|
}
|
|
1041
1118
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
return { action: "tab_new", url: args[1] };
|
|
1119
|
+
return { key, modifiers };
|
|
1120
|
+
}
|
|
1121
|
+
async function pressCommand(keyString, options = {}) {
|
|
1122
|
+
if (!keyString) {
|
|
1123
|
+
throw new Error("\u7F3A\u5C11 key \u53C2\u6570");
|
|
1048
1124
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
throw new Error("tab select \u9700\u8981 --id \u53C2\u6570\uFF0C\u7528\u6CD5\uFF1Abb-browser tab select --id <tabId>");
|
|
1125
|
+
await ensureDaemonRunning();
|
|
1126
|
+
const { key, modifiers } = parseKey(keyString);
|
|
1127
|
+
if (!key) {
|
|
1128
|
+
throw new Error("\u65E0\u6548\u7684\u6309\u952E\u683C\u5F0F");
|
|
1054
1129
|
}
|
|
1055
|
-
if (first === "close") {
|
|
1056
|
-
if (tabId !== void 0) {
|
|
1057
|
-
return { action: "tab_close", tabId };
|
|
1058
|
-
}
|
|
1059
|
-
const indexArg = args[1];
|
|
1060
|
-
if (indexArg !== void 0) {
|
|
1061
|
-
const index2 = parseInt(indexArg, 10);
|
|
1062
|
-
if (isNaN(index2) || index2 < 0) {
|
|
1063
|
-
throw new Error(`\u65E0\u6548\u7684\u6807\u7B7E\u9875\u7D22\u5F15: ${indexArg}`);
|
|
1064
|
-
}
|
|
1065
|
-
return { action: "tab_close", index: index2 };
|
|
1066
|
-
}
|
|
1067
|
-
return { action: "tab_close" };
|
|
1068
|
-
}
|
|
1069
|
-
const index = parseInt(first, 10);
|
|
1070
|
-
if (!isNaN(index) && index >= 0) {
|
|
1071
|
-
return { action: "tab_select", index };
|
|
1072
|
-
}
|
|
1073
|
-
throw new Error(`\u672A\u77E5\u7684 tab \u5B50\u547D\u4EE4: ${first}`);
|
|
1074
|
-
}
|
|
1075
|
-
function formatTabList(tabs, activeIndex) {
|
|
1076
|
-
const lines = [];
|
|
1077
|
-
lines.push(`\u6807\u7B7E\u9875\u5217\u8868\uFF08\u5171 ${tabs.length} \u4E2A\uFF0C\u5F53\u524D #${activeIndex}\uFF09\uFF1A`);
|
|
1078
|
-
for (const tab of tabs) {
|
|
1079
|
-
const prefix = tab.active ? "*" : " ";
|
|
1080
|
-
const title = tab.title || "(\u65E0\u6807\u9898)";
|
|
1081
|
-
lines.push(`${prefix} [${tab.index}] ${tab.url} - ${title}`);
|
|
1082
|
-
}
|
|
1083
|
-
return lines.join("\n");
|
|
1084
|
-
}
|
|
1085
|
-
async function tabCommand(args, options = {}) {
|
|
1086
|
-
await ensureDaemonRunning();
|
|
1087
|
-
const parsed = parseTabSubcommand(args, process.argv);
|
|
1088
1130
|
const request = {
|
|
1089
1131
|
id: generateId(),
|
|
1090
|
-
action:
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
tabId:
|
|
1132
|
+
action: "press",
|
|
1133
|
+
key,
|
|
1134
|
+
modifiers,
|
|
1135
|
+
tabId: options.tabId
|
|
1094
1136
|
};
|
|
1095
1137
|
const response = await sendCommand(request);
|
|
1096
1138
|
if (options.json) {
|
|
1097
1139
|
console.log(JSON.stringify(response, null, 2));
|
|
1098
1140
|
} else {
|
|
1099
1141
|
if (response.success) {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
const tabs = response.data?.tabs ?? [];
|
|
1103
|
-
const activeIndex = response.data?.activeIndex ?? 0;
|
|
1104
|
-
console.log(formatTabList(tabs, activeIndex));
|
|
1105
|
-
break;
|
|
1106
|
-
}
|
|
1107
|
-
case "tab_new": {
|
|
1108
|
-
const url = response.data?.url ?? "about:blank";
|
|
1109
|
-
console.log(`\u5DF2\u521B\u5EFA\u65B0\u6807\u7B7E\u9875: ${url}`);
|
|
1110
|
-
break;
|
|
1111
|
-
}
|
|
1112
|
-
case "tab_select": {
|
|
1113
|
-
const title = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1114
|
-
const url = response.data?.url ?? "";
|
|
1115
|
-
console.log(`\u5DF2\u5207\u6362\u5230\u6807\u7B7E\u9875 #${parsed.index}: ${title}`);
|
|
1116
|
-
console.log(` URL: ${url}`);
|
|
1117
|
-
break;
|
|
1118
|
-
}
|
|
1119
|
-
case "tab_close": {
|
|
1120
|
-
const closedTitle = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1121
|
-
console.log(`\u5DF2\u5173\u95ED\u6807\u7B7E\u9875: ${closedTitle}`);
|
|
1122
|
-
break;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1142
|
+
const displayKey = modifiers.length > 0 ? `${modifiers.join("+")}+${key}` : key;
|
|
1143
|
+
console.log(`\u5DF2\u6309\u4E0B: ${displayKey}`);
|
|
1125
1144
|
} else {
|
|
1126
1145
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1127
1146
|
process.exit(1);
|
|
@@ -1129,40 +1148,31 @@ async function tabCommand(args, options = {}) {
|
|
|
1129
1148
|
}
|
|
1130
1149
|
}
|
|
1131
1150
|
|
|
1132
|
-
// packages/cli/src/commands/
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1151
|
+
// packages/cli/src/commands/scroll.ts
|
|
1152
|
+
var VALID_DIRECTIONS = ["up", "down", "left", "right"];
|
|
1153
|
+
var DEFAULT_PIXELS = 300;
|
|
1154
|
+
async function scrollCommand(direction, pixels, options = {}) {
|
|
1155
|
+
if (!direction) {
|
|
1156
|
+
throw new Error("\u7F3A\u5C11 direction \u53C2\u6570");
|
|
1136
1157
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
} else {
|
|
1148
|
-
if (response.success) {
|
|
1149
|
-
const frameInfo = response.data?.frameInfo;
|
|
1150
|
-
if (frameInfo?.url) {
|
|
1151
|
-
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector} (${frameInfo.url})`);
|
|
1152
|
-
} else {
|
|
1153
|
-
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector}`);
|
|
1154
|
-
}
|
|
1155
|
-
} else {
|
|
1156
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1157
|
-
process.exit(1);
|
|
1158
|
+
if (!VALID_DIRECTIONS.includes(direction)) {
|
|
1159
|
+
throw new Error(
|
|
1160
|
+
`\u65E0\u6548\u7684\u6EDA\u52A8\u65B9\u5411: ${direction}\uFF0C\u652F\u6301: ${VALID_DIRECTIONS.join(", ")}`
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
let pixelValue = DEFAULT_PIXELS;
|
|
1164
|
+
if (pixels !== void 0) {
|
|
1165
|
+
pixelValue = parseInt(pixels, 10);
|
|
1166
|
+
if (isNaN(pixelValue) || pixelValue <= 0) {
|
|
1167
|
+
throw new Error(`\u65E0\u6548\u7684\u50CF\u7D20\u503C: ${pixels}`);
|
|
1158
1168
|
}
|
|
1159
1169
|
}
|
|
1160
|
-
}
|
|
1161
|
-
async function frameMainCommand(options = {}) {
|
|
1162
1170
|
await ensureDaemonRunning();
|
|
1163
1171
|
const request = {
|
|
1164
1172
|
id: generateId(),
|
|
1165
|
-
action: "
|
|
1173
|
+
action: "scroll",
|
|
1174
|
+
direction,
|
|
1175
|
+
pixels: pixelValue,
|
|
1166
1176
|
tabId: options.tabId
|
|
1167
1177
|
};
|
|
1168
1178
|
const response = await sendCommand(request);
|
|
@@ -1170,7 +1180,7 @@ async function frameMainCommand(options = {}) {
|
|
|
1170
1180
|
console.log(JSON.stringify(response, null, 2));
|
|
1171
1181
|
} else {
|
|
1172
1182
|
if (response.success) {
|
|
1173
|
-
console.log(
|
|
1183
|
+
console.log(`\u5DF2\u6EDA\u52A8: ${direction} ${pixelValue}px`);
|
|
1174
1184
|
} else {
|
|
1175
1185
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1176
1186
|
process.exit(1);
|
|
@@ -1178,797 +1188,1027 @@ async function frameMainCommand(options = {}) {
|
|
|
1178
1188
|
}
|
|
1179
1189
|
}
|
|
1180
1190
|
|
|
1181
|
-
// packages/cli/src/commands/
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1191
|
+
// packages/cli/src/commands/daemon.ts
|
|
1192
|
+
import { spawn as spawn2 } from "child_process";
|
|
1193
|
+
async function daemonCommand(options = {}) {
|
|
1194
|
+
if (await isDaemonRunning()) {
|
|
1195
|
+
if (options.json) {
|
|
1196
|
+
console.log(JSON.stringify({ success: false, error: "Daemon \u5DF2\u5728\u8FD0\u884C" }));
|
|
1197
|
+
} else {
|
|
1198
|
+
console.log("Daemon \u5DF2\u5728\u8FD0\u884C");
|
|
1199
|
+
}
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
const daemonPath = getDaemonPath();
|
|
1203
|
+
const args = [daemonPath];
|
|
1204
|
+
if (options.host) {
|
|
1205
|
+
args.push("--host", options.host);
|
|
1185
1206
|
}
|
|
1186
|
-
await ensureDaemonRunning();
|
|
1187
|
-
const request = {
|
|
1188
|
-
id: generateId(),
|
|
1189
|
-
action: "dialog",
|
|
1190
|
-
dialogResponse: subCommand,
|
|
1191
|
-
promptText: subCommand === "accept" ? promptText : void 0,
|
|
1192
|
-
tabId: options.tabId
|
|
1193
|
-
};
|
|
1194
|
-
const response = await sendCommand(request);
|
|
1195
1207
|
if (options.json) {
|
|
1196
|
-
console.log(JSON.stringify(
|
|
1208
|
+
console.log(JSON.stringify({ success: true, message: "Daemon \u542F\u52A8\u4E2D..." }));
|
|
1197
1209
|
} else {
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1210
|
+
console.log("Daemon \u542F\u52A8\u4E2D...");
|
|
1211
|
+
}
|
|
1212
|
+
await new Promise((resolve2, reject) => {
|
|
1213
|
+
const child = spawn2(process.execPath, args, {
|
|
1214
|
+
stdio: "inherit"
|
|
1215
|
+
});
|
|
1216
|
+
child.on("exit", (code) => {
|
|
1217
|
+
if (code && code !== 0) {
|
|
1218
|
+
reject(new Error(`Daemon exited with code ${code}`));
|
|
1203
1219
|
} else {
|
|
1204
|
-
|
|
1220
|
+
resolve2();
|
|
1205
1221
|
}
|
|
1222
|
+
});
|
|
1223
|
+
child.on("error", reject);
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
async function stopCommand(options = {}) {
|
|
1227
|
+
if (!await isDaemonRunning()) {
|
|
1228
|
+
if (options.json) {
|
|
1229
|
+
console.log(JSON.stringify({ success: false, error: "Daemon \u672A\u8FD0\u884C" }));
|
|
1206
1230
|
} else {
|
|
1207
|
-
console.
|
|
1208
|
-
|
|
1231
|
+
console.log("Daemon \u672A\u8FD0\u884C");
|
|
1232
|
+
}
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
const stopped = await stopDaemon();
|
|
1236
|
+
if (stopped) {
|
|
1237
|
+
if (options.json) {
|
|
1238
|
+
console.log(JSON.stringify({ success: true, message: "Daemon \u5DF2\u505C\u6B62" }));
|
|
1239
|
+
} else {
|
|
1240
|
+
console.log("Daemon \u5DF2\u505C\u6B62");
|
|
1241
|
+
}
|
|
1242
|
+
} else {
|
|
1243
|
+
if (options.json) {
|
|
1244
|
+
console.log(JSON.stringify({ success: false, error: "\u65E0\u6CD5\u505C\u6B62 Daemon" }));
|
|
1245
|
+
} else {
|
|
1246
|
+
console.error("\u65E0\u6CD5\u505C\u6B62 Daemon");
|
|
1209
1247
|
}
|
|
1248
|
+
process.exit(1);
|
|
1210
1249
|
}
|
|
1211
1250
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
async function networkCommand(subCommand, urlOrFilter, options = {}) {
|
|
1215
|
-
const response = await sendCommand({
|
|
1216
|
-
id: crypto.randomUUID(),
|
|
1217
|
-
action: "network",
|
|
1218
|
-
networkCommand: subCommand,
|
|
1219
|
-
url: subCommand === "route" || subCommand === "unroute" ? urlOrFilter : void 0,
|
|
1220
|
-
filter: subCommand === "requests" ? urlOrFilter : void 0,
|
|
1221
|
-
routeOptions: subCommand === "route" ? {
|
|
1222
|
-
abort: options.abort,
|
|
1223
|
-
body: options.body
|
|
1224
|
-
} : void 0,
|
|
1225
|
-
withBody: subCommand === "requests" ? options.withBody : void 0,
|
|
1226
|
-
tabId: options.tabId
|
|
1227
|
-
});
|
|
1251
|
+
async function statusCommand(options = {}) {
|
|
1252
|
+
const running = await isDaemonRunning();
|
|
1228
1253
|
if (options.json) {
|
|
1229
|
-
console.log(JSON.stringify(
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
if (!response.success) {
|
|
1233
|
-
throw new Error(response.error || "Network command failed");
|
|
1254
|
+
console.log(JSON.stringify({ running }));
|
|
1255
|
+
} else {
|
|
1256
|
+
console.log(running ? "Daemon \u8FD0\u884C\u4E2D" : "Daemon \u672A\u8FD0\u884C");
|
|
1234
1257
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
const status = req.failed ? `FAILED (${req.failureReason})` : req.status ? `${req.status} ${req.statusText || ""}` : "pending";
|
|
1247
|
-
console.log(`${req.method} ${req.url}`);
|
|
1248
|
-
console.log(` \u7C7B\u578B: ${req.type}, \u72B6\u6001: ${status}`);
|
|
1249
|
-
if (options.withBody) {
|
|
1250
|
-
const requestHeaderCount = req.requestHeaders ? Object.keys(req.requestHeaders).length : 0;
|
|
1251
|
-
const responseHeaderCount = req.responseHeaders ? Object.keys(req.responseHeaders).length : 0;
|
|
1252
|
-
console.log(` \u8BF7\u6C42\u5934: ${requestHeaderCount}, \u54CD\u5E94\u5934: ${responseHeaderCount}`);
|
|
1253
|
-
if (req.requestBody !== void 0) {
|
|
1254
|
-
const preview = req.requestBody.length > 200 ? `${req.requestBody.slice(0, 200)}...` : req.requestBody;
|
|
1255
|
-
console.log(` \u8BF7\u6C42\u4F53: ${preview}`);
|
|
1256
|
-
}
|
|
1257
|
-
if (req.responseBody !== void 0) {
|
|
1258
|
-
const preview = req.responseBody.length > 200 ? `${req.responseBody.slice(0, 200)}...` : req.responseBody;
|
|
1259
|
-
console.log(` \u54CD\u5E94\u4F53: ${preview}`);
|
|
1260
|
-
}
|
|
1261
|
-
if (req.bodyError) {
|
|
1262
|
-
console.log(` Body\u9519\u8BEF: ${req.bodyError}`);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
console.log("");
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
break;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// packages/cli/src/commands/reload.ts
|
|
1261
|
+
import WebSocket from "ws";
|
|
1262
|
+
var EXTENSION_NAME = "bb-browser";
|
|
1263
|
+
async function reloadCommand(options = {}) {
|
|
1264
|
+
const port = options.port || 9222;
|
|
1265
|
+
try {
|
|
1266
|
+
const listRes = await fetch(`http://127.0.0.1:${port}/json/list`);
|
|
1267
|
+
if (!listRes.ok) {
|
|
1268
|
+
throw new Error(`CDP \u672A\u542F\u7528\u3002\u8BF7\u7528 --remote-debugging-port=${port} \u542F\u52A8 Chrome`);
|
|
1269
1269
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
} else {
|
|
1277
|
-
console.log(" \u884C\u4E3A: \u7EE7\u7EED\u8BF7\u6C42");
|
|
1278
|
-
}
|
|
1279
|
-
console.log(`\u5F53\u524D\u89C4\u5219\u6570: ${data?.routeCount || 0}`);
|
|
1280
|
-
break;
|
|
1270
|
+
const list = await listRes.json();
|
|
1271
|
+
const extPage = list.find(
|
|
1272
|
+
(t) => t.type === "page" && t.url.includes("chrome://extensions")
|
|
1273
|
+
);
|
|
1274
|
+
if (!extPage) {
|
|
1275
|
+
throw new Error("\u8BF7\u5148\u6253\u5F00 chrome://extensions \u9875\u9762");
|
|
1281
1276
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1277
|
+
const result = await new Promise((resolve2, reject) => {
|
|
1278
|
+
const ws = new WebSocket(extPage.webSocketDebuggerUrl);
|
|
1279
|
+
let resolved = false;
|
|
1280
|
+
const timeout = setTimeout(() => {
|
|
1281
|
+
if (!resolved) {
|
|
1282
|
+
resolved = true;
|
|
1283
|
+
ws.close();
|
|
1284
|
+
reject(new Error("CDP \u8FDE\u63A5\u8D85\u65F6"));
|
|
1285
|
+
}
|
|
1286
|
+
}, 1e4);
|
|
1287
|
+
ws.on("open", () => {
|
|
1288
|
+
const script = `
|
|
1289
|
+
(async function() {
|
|
1290
|
+
if (!chrome || !chrome.developerPrivate) {
|
|
1291
|
+
return { error: 'developerPrivate API not available' };
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
try {
|
|
1295
|
+
const exts = await chrome.developerPrivate.getExtensionsInfo();
|
|
1296
|
+
const bbExt = exts.find(e => e.name === '${EXTENSION_NAME}');
|
|
1297
|
+
|
|
1298
|
+
if (!bbExt) {
|
|
1299
|
+
return { error: '${EXTENSION_NAME} \u6269\u5C55\u672A\u5B89\u88C5' };
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if (bbExt.state !== 'ENABLED') {
|
|
1303
|
+
return { error: '${EXTENSION_NAME} \u6269\u5C55\u5DF2\u7981\u7528' };
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
await chrome.developerPrivate.reload(bbExt.id, {failQuietly: true});
|
|
1307
|
+
return { success: true, extensionId: bbExt.id };
|
|
1308
|
+
} catch (e) {
|
|
1309
|
+
return { error: e.message };
|
|
1310
|
+
}
|
|
1311
|
+
})()
|
|
1312
|
+
`;
|
|
1313
|
+
ws.send(JSON.stringify({
|
|
1314
|
+
id: 1,
|
|
1315
|
+
method: "Runtime.evaluate",
|
|
1316
|
+
params: {
|
|
1317
|
+
expression: script,
|
|
1318
|
+
awaitPromise: true,
|
|
1319
|
+
returnByValue: true
|
|
1320
|
+
}
|
|
1321
|
+
}));
|
|
1322
|
+
});
|
|
1323
|
+
ws.on("message", (data) => {
|
|
1324
|
+
const msg = JSON.parse(data.toString());
|
|
1325
|
+
if (msg.id === 1) {
|
|
1326
|
+
clearTimeout(timeout);
|
|
1327
|
+
resolved = true;
|
|
1328
|
+
ws.close();
|
|
1329
|
+
const value = msg.result?.result?.value;
|
|
1330
|
+
if (value?.success) {
|
|
1331
|
+
resolve2({
|
|
1332
|
+
success: true,
|
|
1333
|
+
message: "\u6269\u5C55\u5DF2\u91CD\u8F7D",
|
|
1334
|
+
extensionId: value.extensionId
|
|
1335
|
+
});
|
|
1336
|
+
} else if (value?.error) {
|
|
1337
|
+
reject(new Error(value.error));
|
|
1338
|
+
} else {
|
|
1339
|
+
reject(new Error(`\u91CD\u8F7D\u5931\u8D25: ${JSON.stringify(value)}`));
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
ws.on("error", (err) => {
|
|
1344
|
+
clearTimeout(timeout);
|
|
1345
|
+
if (!resolved) {
|
|
1346
|
+
resolved = true;
|
|
1347
|
+
reject(new Error(`CDP \u8FDE\u63A5\u5931\u8D25: ${err.message}`));
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
});
|
|
1351
|
+
if (options.json) {
|
|
1352
|
+
console.log(JSON.stringify(result));
|
|
1353
|
+
} else {
|
|
1354
|
+
console.log(`${result.message} (${result.extensionId})`);
|
|
1290
1355
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1356
|
+
} catch (error) {
|
|
1357
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1358
|
+
if (options.json) {
|
|
1359
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
1360
|
+
} else {
|
|
1361
|
+
console.error(`\u9519\u8BEF: ${message}`);
|
|
1294
1362
|
}
|
|
1295
|
-
|
|
1296
|
-
throw new Error(`\u672A\u77E5\u7684 network \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1363
|
+
process.exit(1);
|
|
1297
1364
|
}
|
|
1298
1365
|
}
|
|
1299
1366
|
|
|
1300
|
-
// packages/cli/src/commands/
|
|
1301
|
-
async function
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1367
|
+
// packages/cli/src/commands/nav.ts
|
|
1368
|
+
async function backCommand(options = {}) {
|
|
1369
|
+
await ensureDaemonRunning();
|
|
1370
|
+
const request = {
|
|
1371
|
+
id: generateId(),
|
|
1372
|
+
action: "back",
|
|
1306
1373
|
tabId: options.tabId
|
|
1307
|
-
});
|
|
1308
|
-
if (options.json) {
|
|
1309
|
-
console.log(JSON.stringify(response));
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1312
|
-
if (!response.success) {
|
|
1313
|
-
throw new Error(response.error || "Console command failed");
|
|
1314
|
-
}
|
|
1315
|
-
if (options.clear) {
|
|
1316
|
-
console.log("\u5DF2\u6E05\u7A7A\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1317
|
-
return;
|
|
1318
|
-
}
|
|
1319
|
-
const messages = response.data?.consoleMessages || [];
|
|
1320
|
-
if (messages.length === 0) {
|
|
1321
|
-
console.log("\u6CA1\u6709\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1322
|
-
console.log("\u63D0\u793A: console \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1323
|
-
return;
|
|
1324
|
-
}
|
|
1325
|
-
console.log(`\u63A7\u5236\u53F0\u6D88\u606F (${messages.length} \u6761):
|
|
1326
|
-
`);
|
|
1327
|
-
const typeColors = {
|
|
1328
|
-
log: "",
|
|
1329
|
-
info: "[INFO]",
|
|
1330
|
-
warn: "[WARN]",
|
|
1331
|
-
error: "[ERROR]",
|
|
1332
|
-
debug: "[DEBUG]"
|
|
1333
1374
|
};
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1375
|
+
const response = await sendCommand(request);
|
|
1376
|
+
if (options.json) {
|
|
1377
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1378
|
+
} else {
|
|
1379
|
+
if (response.success) {
|
|
1380
|
+
const url = response.data?.url ?? "";
|
|
1381
|
+
if (url) {
|
|
1382
|
+
console.log(`\u540E\u9000\u81F3: ${url}`);
|
|
1383
|
+
} else {
|
|
1384
|
+
console.log("\u5DF2\u540E\u9000");
|
|
1385
|
+
}
|
|
1339
1386
|
} else {
|
|
1340
|
-
console.
|
|
1387
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1388
|
+
process.exit(1);
|
|
1341
1389
|
}
|
|
1342
1390
|
}
|
|
1343
1391
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
action: "errors",
|
|
1350
|
-
errorsCommand: options.clear ? "clear" : "get",
|
|
1392
|
+
async function forwardCommand(options = {}) {
|
|
1393
|
+
await ensureDaemonRunning();
|
|
1394
|
+
const request = {
|
|
1395
|
+
id: generateId(),
|
|
1396
|
+
action: "forward",
|
|
1351
1397
|
tabId: options.tabId
|
|
1352
|
-
}
|
|
1398
|
+
};
|
|
1399
|
+
const response = await sendCommand(request);
|
|
1353
1400
|
if (options.json) {
|
|
1354
|
-
console.log(JSON.stringify(response));
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
console.log("\u6CA1\u6709 JS \u9519\u8BEF");
|
|
1367
|
-
console.log("\u63D0\u793A: errors \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1368
|
-
return;
|
|
1369
|
-
}
|
|
1370
|
-
console.log(`JS \u9519\u8BEF (${errors.length} \u6761):
|
|
1371
|
-
`);
|
|
1372
|
-
for (const err of errors) {
|
|
1373
|
-
console.log(`[ERROR] ${err.message}`);
|
|
1374
|
-
if (err.url) {
|
|
1375
|
-
console.log(` \u4F4D\u7F6E: ${err.url}:${err.lineNumber || 0}:${err.columnNumber || 0}`);
|
|
1376
|
-
}
|
|
1377
|
-
if (err.stackTrace) {
|
|
1378
|
-
console.log(` \u5806\u6808:`);
|
|
1379
|
-
console.log(err.stackTrace.split("\n").map((line) => ` ${line}`).join("\n"));
|
|
1401
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1402
|
+
} else {
|
|
1403
|
+
if (response.success) {
|
|
1404
|
+
const url = response.data?.url ?? "";
|
|
1405
|
+
if (url) {
|
|
1406
|
+
console.log(`\u524D\u8FDB\u81F3: ${url}`);
|
|
1407
|
+
} else {
|
|
1408
|
+
console.log("\u5DF2\u524D\u8FDB");
|
|
1409
|
+
}
|
|
1410
|
+
} else {
|
|
1411
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1412
|
+
process.exit(1);
|
|
1380
1413
|
}
|
|
1381
|
-
console.log("");
|
|
1382
1414
|
}
|
|
1383
1415
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
action: "trace",
|
|
1390
|
-
traceCommand: subCommand,
|
|
1416
|
+
async function refreshCommand(options = {}) {
|
|
1417
|
+
await ensureDaemonRunning();
|
|
1418
|
+
const request = {
|
|
1419
|
+
id: generateId(),
|
|
1420
|
+
action: "refresh",
|
|
1391
1421
|
tabId: options.tabId
|
|
1392
|
-
}
|
|
1422
|
+
};
|
|
1423
|
+
const response = await sendCommand(request);
|
|
1393
1424
|
if (options.json) {
|
|
1394
|
-
console.log(JSON.stringify(response));
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1425
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1426
|
+
} else {
|
|
1427
|
+
if (response.success) {
|
|
1428
|
+
const title = response.data?.title ?? "";
|
|
1429
|
+
if (title) {
|
|
1430
|
+
console.log(`\u5DF2\u5237\u65B0: "${title}"`);
|
|
1431
|
+
} else {
|
|
1432
|
+
console.log("\u5DF2\u5237\u65B0\u9875\u9762");
|
|
1433
|
+
}
|
|
1434
|
+
} else {
|
|
1435
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1436
|
+
process.exit(1);
|
|
1437
|
+
}
|
|
1399
1438
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
case "press":
|
|
1438
|
-
console.log(`${i + 1}. \u6309\u952E ${event.key}`);
|
|
1439
|
-
break;
|
|
1440
|
-
case "scroll":
|
|
1441
|
-
console.log(`${i + 1}. \u6EDA\u52A8 ${event.direction} ${event.pixels}px`);
|
|
1442
|
-
break;
|
|
1443
|
-
default:
|
|
1444
|
-
console.log(`${i + 1}. ${event.type}`);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// packages/cli/src/commands/check.ts
|
|
1442
|
+
function parseRef7(ref) {
|
|
1443
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
1444
|
+
}
|
|
1445
|
+
async function checkCommand(ref, options = {}) {
|
|
1446
|
+
if (!ref) {
|
|
1447
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1448
|
+
}
|
|
1449
|
+
await ensureDaemonRunning();
|
|
1450
|
+
const parsedRef = parseRef7(ref);
|
|
1451
|
+
const request = {
|
|
1452
|
+
id: generateId(),
|
|
1453
|
+
action: "check",
|
|
1454
|
+
ref: parsedRef,
|
|
1455
|
+
tabId: options.tabId
|
|
1456
|
+
};
|
|
1457
|
+
const response = await sendCommand(request);
|
|
1458
|
+
if (options.json) {
|
|
1459
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1460
|
+
} else {
|
|
1461
|
+
if (response.success) {
|
|
1462
|
+
const role = response.data?.role ?? "checkbox";
|
|
1463
|
+
const name = response.data?.name;
|
|
1464
|
+
const wasAlreadyChecked = response.data?.wasAlreadyChecked;
|
|
1465
|
+
if (wasAlreadyChecked) {
|
|
1466
|
+
if (name) {
|
|
1467
|
+
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
1468
|
+
} else {
|
|
1469
|
+
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role}`);
|
|
1470
|
+
}
|
|
1471
|
+
} else {
|
|
1472
|
+
if (name) {
|
|
1473
|
+
console.log(`\u5DF2\u52FE\u9009: ${role} "${name}"`);
|
|
1474
|
+
} else {
|
|
1475
|
+
console.log(`\u5DF2\u52FE\u9009: ${role}`);
|
|
1445
1476
|
}
|
|
1446
1477
|
}
|
|
1447
|
-
|
|
1448
|
-
\
|
|
1449
|
-
|
|
1478
|
+
} else {
|
|
1479
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1480
|
+
process.exit(1);
|
|
1450
1481
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
async function uncheckCommand(ref, options = {}) {
|
|
1485
|
+
if (!ref) {
|
|
1486
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1487
|
+
}
|
|
1488
|
+
await ensureDaemonRunning();
|
|
1489
|
+
const parsedRef = parseRef7(ref);
|
|
1490
|
+
const request = {
|
|
1491
|
+
id: generateId(),
|
|
1492
|
+
action: "uncheck",
|
|
1493
|
+
ref: parsedRef,
|
|
1494
|
+
tabId: options.tabId
|
|
1495
|
+
};
|
|
1496
|
+
const response = await sendCommand(request);
|
|
1497
|
+
if (options.json) {
|
|
1498
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1499
|
+
} else {
|
|
1500
|
+
if (response.success) {
|
|
1501
|
+
const role = response.data?.role ?? "checkbox";
|
|
1502
|
+
const name = response.data?.name;
|
|
1503
|
+
const wasAlreadyUnchecked = response.data?.wasAlreadyUnchecked;
|
|
1504
|
+
if (wasAlreadyUnchecked) {
|
|
1505
|
+
if (name) {
|
|
1506
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
1507
|
+
} else {
|
|
1508
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role}`);
|
|
1509
|
+
}
|
|
1456
1510
|
} else {
|
|
1457
|
-
|
|
1511
|
+
if (name) {
|
|
1512
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role} "${name}"`);
|
|
1513
|
+
} else {
|
|
1514
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role}`);
|
|
1515
|
+
}
|
|
1458
1516
|
}
|
|
1459
|
-
|
|
1517
|
+
} else {
|
|
1518
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1519
|
+
process.exit(1);
|
|
1460
1520
|
}
|
|
1461
|
-
default:
|
|
1462
|
-
throw new Error(`\u672A\u77E5\u7684 trace \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1463
1521
|
}
|
|
1464
1522
|
}
|
|
1465
1523
|
|
|
1466
|
-
// packages/cli/src/commands/
|
|
1467
|
-
function
|
|
1468
|
-
|
|
1469
|
-
const tabHostname = new URL(tabUrl).hostname;
|
|
1470
|
-
return tabHostname === targetHostname || tabHostname.endsWith("." + targetHostname);
|
|
1471
|
-
} catch {
|
|
1472
|
-
return false;
|
|
1473
|
-
}
|
|
1524
|
+
// packages/cli/src/commands/select.ts
|
|
1525
|
+
function parseRef8(ref) {
|
|
1526
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
1474
1527
|
}
|
|
1475
|
-
async function
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
if (listResp.success && listResp.data?.tabs) {
|
|
1479
|
-
const matchingTab = listResp.data.tabs.find(
|
|
1480
|
-
(tab) => matchTabOrigin(tab.url, hostname)
|
|
1481
|
-
);
|
|
1482
|
-
if (matchingTab) {
|
|
1483
|
-
return matchingTab.tabId;
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
const newResp = await sendCommand({ id: generateId(), action: "tab_new", url: origin });
|
|
1487
|
-
if (!newResp.success) {
|
|
1488
|
-
throw new Error(`\u65E0\u6CD5\u6253\u5F00 ${origin}: ${newResp.error}`);
|
|
1528
|
+
async function selectCommand(ref, value, options = {}) {
|
|
1529
|
+
if (!ref) {
|
|
1530
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1489
1531
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
function buildFetchScript(url, options) {
|
|
1494
|
-
const method = (options.method || "GET").toUpperCase();
|
|
1495
|
-
const hasBody = options.body && method !== "GET" && method !== "HEAD";
|
|
1496
|
-
let headersExpr = "{}";
|
|
1497
|
-
if (options.headers) {
|
|
1498
|
-
try {
|
|
1499
|
-
JSON.parse(options.headers);
|
|
1500
|
-
headersExpr = options.headers;
|
|
1501
|
-
} catch {
|
|
1502
|
-
throw new Error(`--headers must be valid JSON. Got: ${options.headers}`);
|
|
1503
|
-
}
|
|
1532
|
+
if (value === void 0 || value === null) {
|
|
1533
|
+
throw new Error("\u7F3A\u5C11 value \u53C2\u6570");
|
|
1504
1534
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1535
|
+
await ensureDaemonRunning();
|
|
1536
|
+
const parsedRef = parseRef8(ref);
|
|
1537
|
+
const request = {
|
|
1538
|
+
id: generateId(),
|
|
1539
|
+
action: "select",
|
|
1540
|
+
ref: parsedRef,
|
|
1541
|
+
value,
|
|
1542
|
+
tabId: options.tabId
|
|
1543
|
+
};
|
|
1544
|
+
const response = await sendCommand(request);
|
|
1545
|
+
if (options.json) {
|
|
1546
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1547
|
+
} else {
|
|
1548
|
+
if (response.success) {
|
|
1549
|
+
const role = response.data?.role ?? "combobox";
|
|
1550
|
+
const name = response.data?.name;
|
|
1551
|
+
const selectedValue = response.data?.selectedValue;
|
|
1552
|
+
const selectedLabel = response.data?.selectedLabel;
|
|
1553
|
+
if (name) {
|
|
1554
|
+
console.log(`\u5DF2\u9009\u62E9: ${role} "${name}"`);
|
|
1517
1555
|
} else {
|
|
1518
|
-
|
|
1556
|
+
console.log(`\u5DF2\u9009\u62E9: ${role}`);
|
|
1519
1557
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1558
|
+
if (selectedLabel && selectedLabel !== selectedValue) {
|
|
1559
|
+
console.log(`\u9009\u9879: "${selectedLabel}" (value="${selectedValue}")`);
|
|
1560
|
+
} else {
|
|
1561
|
+
console.log(`\u9009\u9879: "${selectedValue}"`);
|
|
1562
|
+
}
|
|
1563
|
+
} else {
|
|
1564
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1565
|
+
process.exit(1);
|
|
1527
1566
|
}
|
|
1528
|
-
}
|
|
1567
|
+
}
|
|
1529
1568
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
);
|
|
1569
|
+
|
|
1570
|
+
// packages/cli/src/commands/eval.ts
|
|
1571
|
+
async function evalCommand(script, options = {}) {
|
|
1572
|
+
if (!script) {
|
|
1573
|
+
throw new Error("\u7F3A\u5C11 script \u53C2\u6570");
|
|
1535
1574
|
}
|
|
1536
1575
|
await ensureDaemonRunning();
|
|
1537
|
-
const
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1576
|
+
const request = {
|
|
1577
|
+
id: generateId(),
|
|
1578
|
+
action: "eval",
|
|
1579
|
+
script,
|
|
1580
|
+
tabId: options.tabId
|
|
1581
|
+
};
|
|
1582
|
+
const response = await sendCommand(request);
|
|
1583
|
+
if (options.json) {
|
|
1584
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1585
|
+
} else {
|
|
1586
|
+
if (response.success) {
|
|
1587
|
+
const result = response.data?.result;
|
|
1588
|
+
if (result !== void 0) {
|
|
1589
|
+
if (typeof result === "object" && result !== null) {
|
|
1590
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1591
|
+
} else {
|
|
1592
|
+
console.log(result);
|
|
1593
|
+
}
|
|
1594
|
+
} else {
|
|
1595
|
+
console.log("undefined");
|
|
1596
|
+
}
|
|
1597
|
+
} else {
|
|
1598
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1599
|
+
process.exit(1);
|
|
1600
|
+
}
|
|
1558
1601
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// packages/cli/src/commands/tab.ts
|
|
1605
|
+
function parseTabSubcommand(args, rawArgv) {
|
|
1606
|
+
let tabId;
|
|
1607
|
+
if (rawArgv) {
|
|
1608
|
+
const idIdx = rawArgv.indexOf("--id");
|
|
1609
|
+
if (idIdx >= 0 && rawArgv[idIdx + 1]) {
|
|
1610
|
+
tabId = parseInt(rawArgv[idIdx + 1], 10);
|
|
1611
|
+
if (isNaN(tabId)) {
|
|
1612
|
+
throw new Error(`\u65E0\u6548\u7684 tabId: ${rawArgv[idIdx + 1]}`);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1562
1615
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
result = typeof rawResult === "string" ? JSON.parse(rawResult) : rawResult;
|
|
1566
|
-
} catch {
|
|
1567
|
-
console.log(rawResult);
|
|
1568
|
-
return;
|
|
1616
|
+
if (args.length === 0) {
|
|
1617
|
+
return { action: "tab_list" };
|
|
1569
1618
|
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1619
|
+
const first = args[0];
|
|
1620
|
+
if (first === "new") {
|
|
1621
|
+
return { action: "tab_new", url: args[1] };
|
|
1572
1622
|
}
|
|
1573
|
-
if (
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
return;
|
|
1623
|
+
if (first === "select") {
|
|
1624
|
+
if (tabId !== void 0) {
|
|
1625
|
+
return { action: "tab_select", tabId };
|
|
1626
|
+
}
|
|
1627
|
+
throw new Error("tab select \u9700\u8981 --id \u53C2\u6570\uFF0C\u7528\u6CD5\uFF1Abb-browser tab select --id <tabId>");
|
|
1579
1628
|
}
|
|
1580
|
-
if (
|
|
1581
|
-
|
|
1629
|
+
if (first === "close") {
|
|
1630
|
+
if (tabId !== void 0) {
|
|
1631
|
+
return { action: "tab_close", tabId };
|
|
1632
|
+
}
|
|
1633
|
+
const indexArg = args[1];
|
|
1634
|
+
if (indexArg !== void 0) {
|
|
1635
|
+
const index2 = parseInt(indexArg, 10);
|
|
1636
|
+
if (isNaN(index2) || index2 < 0) {
|
|
1637
|
+
throw new Error(`\u65E0\u6548\u7684\u6807\u7B7E\u9875\u7D22\u5F15: ${indexArg}`);
|
|
1638
|
+
}
|
|
1639
|
+
return { action: "tab_close", index: index2 };
|
|
1640
|
+
}
|
|
1641
|
+
return { action: "tab_close" };
|
|
1642
|
+
}
|
|
1643
|
+
const index = parseInt(first, 10);
|
|
1644
|
+
if (!isNaN(index) && index >= 0) {
|
|
1645
|
+
return { action: "tab_select", index };
|
|
1646
|
+
}
|
|
1647
|
+
throw new Error(`\u672A\u77E5\u7684 tab \u5B50\u547D\u4EE4: ${first}`);
|
|
1648
|
+
}
|
|
1649
|
+
function formatTabList(tabs, activeIndex) {
|
|
1650
|
+
const lines = [];
|
|
1651
|
+
lines.push(`\u6807\u7B7E\u9875\u5217\u8868\uFF08\u5171 ${tabs.length} \u4E2A\uFF0C\u5F53\u524D #${activeIndex}\uFF09\uFF1A`);
|
|
1652
|
+
for (const tab of tabs) {
|
|
1653
|
+
const prefix = tab.active ? "*" : " ";
|
|
1654
|
+
const title = tab.title || "(\u65E0\u6807\u9898)";
|
|
1655
|
+
lines.push(`${prefix} [${tab.index}] ${tab.url} - ${title}`);
|
|
1656
|
+
}
|
|
1657
|
+
return lines.join("\n");
|
|
1658
|
+
}
|
|
1659
|
+
async function tabCommand(args, options = {}) {
|
|
1660
|
+
await ensureDaemonRunning();
|
|
1661
|
+
const parsed = parseTabSubcommand(args, process.argv);
|
|
1662
|
+
const request = {
|
|
1663
|
+
id: generateId(),
|
|
1664
|
+
action: parsed.action,
|
|
1665
|
+
url: parsed.url,
|
|
1666
|
+
index: parsed.index,
|
|
1667
|
+
tabId: parsed.tabId
|
|
1668
|
+
};
|
|
1669
|
+
const response = await sendCommand(request);
|
|
1670
|
+
if (options.json) {
|
|
1671
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1582
1672
|
} else {
|
|
1583
|
-
|
|
1673
|
+
if (response.success) {
|
|
1674
|
+
switch (parsed.action) {
|
|
1675
|
+
case "tab_list": {
|
|
1676
|
+
const tabs = response.data?.tabs ?? [];
|
|
1677
|
+
const activeIndex = response.data?.activeIndex ?? 0;
|
|
1678
|
+
console.log(formatTabList(tabs, activeIndex));
|
|
1679
|
+
break;
|
|
1680
|
+
}
|
|
1681
|
+
case "tab_new": {
|
|
1682
|
+
const url = response.data?.url ?? "about:blank";
|
|
1683
|
+
console.log(`\u5DF2\u521B\u5EFA\u65B0\u6807\u7B7E\u9875: ${url}`);
|
|
1684
|
+
break;
|
|
1685
|
+
}
|
|
1686
|
+
case "tab_select": {
|
|
1687
|
+
const title = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1688
|
+
const url = response.data?.url ?? "";
|
|
1689
|
+
console.log(`\u5DF2\u5207\u6362\u5230\u6807\u7B7E\u9875 #${parsed.index}: ${title}`);
|
|
1690
|
+
console.log(` URL: ${url}`);
|
|
1691
|
+
break;
|
|
1692
|
+
}
|
|
1693
|
+
case "tab_close": {
|
|
1694
|
+
const closedTitle = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1695
|
+
console.log(`\u5DF2\u5173\u95ED\u6807\u7B7E\u9875: ${closedTitle}`);
|
|
1696
|
+
break;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
} else {
|
|
1700
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1701
|
+
process.exit(1);
|
|
1702
|
+
}
|
|
1584
1703
|
}
|
|
1585
1704
|
}
|
|
1586
1705
|
|
|
1587
|
-
// packages/cli/src/commands/
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
import { execSync } from "child_process";
|
|
1592
|
-
var BB_DIR = join(homedir(), ".bb-browser");
|
|
1593
|
-
var LOCAL_SITES_DIR = join(BB_DIR, "sites");
|
|
1594
|
-
var COMMUNITY_SITES_DIR = join(BB_DIR, "bb-sites");
|
|
1595
|
-
var COMMUNITY_REPO = "https://github.com/epiral/bb-sites.git";
|
|
1596
|
-
function parseSiteMeta(filePath, source) {
|
|
1597
|
-
let content;
|
|
1598
|
-
try {
|
|
1599
|
-
content = readFileSync(filePath, "utf-8");
|
|
1600
|
-
} catch {
|
|
1601
|
-
return null;
|
|
1706
|
+
// packages/cli/src/commands/frame.ts
|
|
1707
|
+
async function frameCommand(selector, options = {}) {
|
|
1708
|
+
if (!selector) {
|
|
1709
|
+
throw new Error("\u7F3A\u5C11 selector \u53C2\u6570");
|
|
1602
1710
|
}
|
|
1603
|
-
|
|
1604
|
-
const
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1711
|
+
await ensureDaemonRunning();
|
|
1712
|
+
const request = {
|
|
1713
|
+
id: generateId(),
|
|
1714
|
+
action: "frame",
|
|
1715
|
+
selector,
|
|
1716
|
+
tabId: options.tabId
|
|
1717
|
+
};
|
|
1718
|
+
const response = await sendCommand(request);
|
|
1719
|
+
if (options.json) {
|
|
1720
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1721
|
+
} else {
|
|
1722
|
+
if (response.success) {
|
|
1723
|
+
const frameInfo = response.data?.frameInfo;
|
|
1724
|
+
if (frameInfo?.url) {
|
|
1725
|
+
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector} (${frameInfo.url})`);
|
|
1726
|
+
} else {
|
|
1727
|
+
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector}`);
|
|
1728
|
+
}
|
|
1729
|
+
} else {
|
|
1730
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1731
|
+
process.exit(1);
|
|
1622
1732
|
}
|
|
1623
1733
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1734
|
+
}
|
|
1735
|
+
async function frameMainCommand(options = {}) {
|
|
1736
|
+
await ensureDaemonRunning();
|
|
1737
|
+
const request = {
|
|
1738
|
+
id: generateId(),
|
|
1739
|
+
action: "frame_main",
|
|
1740
|
+
tabId: options.tabId
|
|
1631
1741
|
};
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
meta.description = value.trim();
|
|
1642
|
-
break;
|
|
1643
|
-
case "domain":
|
|
1644
|
-
meta.domain = value.trim();
|
|
1645
|
-
break;
|
|
1646
|
-
case "args":
|
|
1647
|
-
for (const arg of value.trim().split(/[,\s]+/).filter(Boolean)) {
|
|
1648
|
-
meta.args[arg] = { required: true };
|
|
1649
|
-
}
|
|
1650
|
-
break;
|
|
1651
|
-
case "example":
|
|
1652
|
-
meta.example = value.trim();
|
|
1653
|
-
break;
|
|
1742
|
+
const response = await sendCommand(request);
|
|
1743
|
+
if (options.json) {
|
|
1744
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1745
|
+
} else {
|
|
1746
|
+
if (response.success) {
|
|
1747
|
+
console.log("\u5DF2\u8FD4\u56DE\u4E3B frame");
|
|
1748
|
+
} else {
|
|
1749
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1750
|
+
process.exit(1);
|
|
1654
1751
|
}
|
|
1655
1752
|
}
|
|
1656
|
-
return meta;
|
|
1657
1753
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1754
|
+
|
|
1755
|
+
// packages/cli/src/commands/dialog.ts
|
|
1756
|
+
async function dialogCommand(subCommand, promptText, options = {}) {
|
|
1757
|
+
if (!subCommand || !["accept", "dismiss"].includes(subCommand)) {
|
|
1758
|
+
throw new Error("\u8BF7\u4F7F\u7528 'dialog accept [text]' \u6216 'dialog dismiss'");
|
|
1759
|
+
}
|
|
1760
|
+
await ensureDaemonRunning();
|
|
1761
|
+
const request = {
|
|
1762
|
+
id: generateId(),
|
|
1763
|
+
action: "dialog",
|
|
1764
|
+
dialogResponse: subCommand,
|
|
1765
|
+
promptText: subCommand === "accept" ? promptText : void 0,
|
|
1766
|
+
tabId: options.tabId
|
|
1767
|
+
};
|
|
1768
|
+
const response = await sendCommand(request);
|
|
1769
|
+
if (options.json) {
|
|
1770
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1771
|
+
} else {
|
|
1772
|
+
if (response.success) {
|
|
1773
|
+
const dialogInfo = response.data?.dialogInfo;
|
|
1774
|
+
if (dialogInfo) {
|
|
1775
|
+
const action = subCommand === "accept" ? "\u5DF2\u63A5\u53D7" : "\u5DF2\u62D2\u7EDD";
|
|
1776
|
+
console.log(`${action}\u5BF9\u8BDD\u6846\uFF08${dialogInfo.type}\uFF09: "${dialogInfo.message}"`);
|
|
1777
|
+
} else {
|
|
1778
|
+
console.log("\u5BF9\u8BDD\u6846\u5DF2\u5904\u7406");
|
|
1675
1779
|
}
|
|
1780
|
+
} else {
|
|
1781
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1782
|
+
process.exit(1);
|
|
1676
1783
|
}
|
|
1677
1784
|
}
|
|
1678
|
-
walk(dir);
|
|
1679
|
-
return sites;
|
|
1680
|
-
}
|
|
1681
|
-
function getAllSites() {
|
|
1682
|
-
const community = scanSites(COMMUNITY_SITES_DIR, "community");
|
|
1683
|
-
const local = scanSites(LOCAL_SITES_DIR, "local");
|
|
1684
|
-
const byName = /* @__PURE__ */ new Map();
|
|
1685
|
-
for (const s of community) byName.set(s.name, s);
|
|
1686
|
-
for (const s of local) byName.set(s.name, s);
|
|
1687
|
-
return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
1688
1785
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1786
|
+
|
|
1787
|
+
// packages/cli/src/commands/network.ts
|
|
1788
|
+
async function networkCommand(subCommand, urlOrFilter, options = {}) {
|
|
1789
|
+
const response = await sendCommand({
|
|
1790
|
+
id: crypto.randomUUID(),
|
|
1791
|
+
action: "network",
|
|
1792
|
+
networkCommand: subCommand,
|
|
1793
|
+
url: subCommand === "route" || subCommand === "unroute" ? urlOrFilter : void 0,
|
|
1794
|
+
filter: subCommand === "requests" ? urlOrFilter : void 0,
|
|
1795
|
+
routeOptions: subCommand === "route" ? {
|
|
1796
|
+
abort: options.abort,
|
|
1797
|
+
body: options.body
|
|
1798
|
+
} : void 0,
|
|
1799
|
+
withBody: subCommand === "requests" ? options.withBody : void 0,
|
|
1800
|
+
tabId: options.tabId
|
|
1801
|
+
});
|
|
1802
|
+
if (options.json) {
|
|
1803
|
+
console.log(JSON.stringify(response));
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
if (!response.success) {
|
|
1807
|
+
throw new Error(response.error || "Network command failed");
|
|
1808
|
+
}
|
|
1809
|
+
const data = response.data;
|
|
1810
|
+
switch (subCommand) {
|
|
1811
|
+
case "requests": {
|
|
1812
|
+
const requests = data?.networkRequests || [];
|
|
1813
|
+
if (requests.length === 0) {
|
|
1814
|
+
console.log("\u6CA1\u6709\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55");
|
|
1815
|
+
console.log("\u63D0\u793A: \u4F7F\u7528 network requests \u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1816
|
+
} else {
|
|
1817
|
+
console.log(`\u7F51\u7EDC\u8BF7\u6C42 (${requests.length} \u6761):
|
|
1818
|
+
`);
|
|
1819
|
+
for (const req of requests) {
|
|
1820
|
+
const status = req.failed ? `FAILED (${req.failureReason})` : req.status ? `${req.status} ${req.statusText || ""}` : "pending";
|
|
1821
|
+
console.log(`${req.method} ${req.url}`);
|
|
1822
|
+
console.log(` \u7C7B\u578B: ${req.type}, \u72B6\u6001: ${status}`);
|
|
1823
|
+
if (options.withBody) {
|
|
1824
|
+
const requestHeaderCount = req.requestHeaders ? Object.keys(req.requestHeaders).length : 0;
|
|
1825
|
+
const responseHeaderCount = req.responseHeaders ? Object.keys(req.responseHeaders).length : 0;
|
|
1826
|
+
console.log(` \u8BF7\u6C42\u5934: ${requestHeaderCount}, \u54CD\u5E94\u5934: ${responseHeaderCount}`);
|
|
1827
|
+
if (req.requestBody !== void 0) {
|
|
1828
|
+
const preview = req.requestBody.length > 200 ? `${req.requestBody.slice(0, 200)}...` : req.requestBody;
|
|
1829
|
+
console.log(` \u8BF7\u6C42\u4F53: ${preview}`);
|
|
1830
|
+
}
|
|
1831
|
+
if (req.responseBody !== void 0) {
|
|
1832
|
+
const preview = req.responseBody.length > 200 ? `${req.responseBody.slice(0, 200)}...` : req.responseBody;
|
|
1833
|
+
console.log(` \u54CD\u5E94\u4F53: ${preview}`);
|
|
1834
|
+
}
|
|
1835
|
+
if (req.bodyError) {
|
|
1836
|
+
console.log(` Body\u9519\u8BEF: ${req.bodyError}`);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
console.log("");
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
break;
|
|
1843
|
+
}
|
|
1844
|
+
case "route": {
|
|
1845
|
+
console.log(`\u5DF2\u6DFB\u52A0\u62E6\u622A\u89C4\u5219: ${urlOrFilter}`);
|
|
1846
|
+
if (options.abort) {
|
|
1847
|
+
console.log(" \u884C\u4E3A: \u963B\u6B62\u8BF7\u6C42");
|
|
1848
|
+
} else if (options.body) {
|
|
1849
|
+
console.log(" \u884C\u4E3A: \u8FD4\u56DE mock \u6570\u636E");
|
|
1850
|
+
} else {
|
|
1851
|
+
console.log(" \u884C\u4E3A: \u7EE7\u7EED\u8BF7\u6C42");
|
|
1852
|
+
}
|
|
1853
|
+
console.log(`\u5F53\u524D\u89C4\u5219\u6570: ${data?.routeCount || 0}`);
|
|
1854
|
+
break;
|
|
1855
|
+
}
|
|
1856
|
+
case "unroute": {
|
|
1857
|
+
if (urlOrFilter) {
|
|
1858
|
+
console.log(`\u5DF2\u79FB\u9664\u62E6\u622A\u89C4\u5219: ${urlOrFilter}`);
|
|
1859
|
+
} else {
|
|
1860
|
+
console.log("\u5DF2\u79FB\u9664\u6240\u6709\u62E6\u622A\u89C4\u5219");
|
|
1861
|
+
}
|
|
1862
|
+
console.log(`\u5269\u4F59\u89C4\u5219\u6570: ${data?.routeCount || 0}`);
|
|
1863
|
+
break;
|
|
1864
|
+
}
|
|
1865
|
+
case "clear": {
|
|
1866
|
+
console.log("\u5DF2\u6E05\u7A7A\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55");
|
|
1867
|
+
break;
|
|
1868
|
+
}
|
|
1869
|
+
default:
|
|
1870
|
+
throw new Error(`\u672A\u77E5\u7684 network \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1695
1871
|
}
|
|
1696
1872
|
}
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1873
|
+
|
|
1874
|
+
// packages/cli/src/commands/console.ts
|
|
1875
|
+
async function consoleCommand(options = {}) {
|
|
1876
|
+
const response = await sendCommand({
|
|
1877
|
+
id: crypto.randomUUID(),
|
|
1878
|
+
action: "console",
|
|
1879
|
+
consoleCommand: options.clear ? "clear" : "get",
|
|
1880
|
+
tabId: options.tabId
|
|
1881
|
+
});
|
|
1882
|
+
if (options.json) {
|
|
1883
|
+
console.log(JSON.stringify(response));
|
|
1703
1884
|
return;
|
|
1704
1885
|
}
|
|
1705
|
-
if (
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
args: s.args,
|
|
1711
|
-
source: s.source
|
|
1712
|
-
})), null, 2));
|
|
1886
|
+
if (!response.success) {
|
|
1887
|
+
throw new Error(response.error || "Console command failed");
|
|
1888
|
+
}
|
|
1889
|
+
if (options.clear) {
|
|
1890
|
+
console.log("\u5DF2\u6E05\u7A7A\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1713
1891
|
return;
|
|
1714
1892
|
}
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1893
|
+
const messages = response.data?.consoleMessages || [];
|
|
1894
|
+
if (messages.length === 0) {
|
|
1895
|
+
console.log("\u6CA1\u6709\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1896
|
+
console.log("\u63D0\u793A: console \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1897
|
+
return;
|
|
1720
1898
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1899
|
+
console.log(`\u63A7\u5236\u53F0\u6D88\u606F (${messages.length} \u6761):
|
|
1900
|
+
`);
|
|
1901
|
+
const typeColors = {
|
|
1902
|
+
log: "",
|
|
1903
|
+
info: "[INFO]",
|
|
1904
|
+
warn: "[WARN]",
|
|
1905
|
+
error: "[ERROR]",
|
|
1906
|
+
debug: "[DEBUG]"
|
|
1907
|
+
};
|
|
1908
|
+
for (const msg of messages) {
|
|
1909
|
+
const prefix = typeColors[msg.type] || `[${msg.type.toUpperCase()}]`;
|
|
1910
|
+
const location = msg.url ? ` (${msg.url}${msg.lineNumber ? `:${msg.lineNumber}` : ""})` : "";
|
|
1911
|
+
if (prefix) {
|
|
1912
|
+
console.log(`${prefix} ${msg.text}${location}`);
|
|
1913
|
+
} else {
|
|
1914
|
+
console.log(`${msg.text}${location}`);
|
|
1729
1915
|
}
|
|
1730
1916
|
}
|
|
1731
|
-
console.log();
|
|
1732
1917
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1918
|
+
|
|
1919
|
+
// packages/cli/src/commands/errors.ts
|
|
1920
|
+
async function errorsCommand(options = {}) {
|
|
1921
|
+
const response = await sendCommand({
|
|
1922
|
+
id: crypto.randomUUID(),
|
|
1923
|
+
action: "errors",
|
|
1924
|
+
errorsCommand: options.clear ? "clear" : "get",
|
|
1925
|
+
tabId: options.tabId
|
|
1926
|
+
});
|
|
1927
|
+
if (options.json) {
|
|
1928
|
+
console.log(JSON.stringify(response));
|
|
1742
1929
|
return;
|
|
1743
1930
|
}
|
|
1744
|
-
if (
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
source: s.source
|
|
1750
|
-
})), null, 2));
|
|
1931
|
+
if (!response.success) {
|
|
1932
|
+
throw new Error(response.error || "Errors command failed");
|
|
1933
|
+
}
|
|
1934
|
+
if (options.clear) {
|
|
1935
|
+
console.log("\u5DF2\u6E05\u7A7A JS \u9519\u8BEF\u8BB0\u5F55");
|
|
1751
1936
|
return;
|
|
1752
1937
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
console.log(
|
|
1938
|
+
const errors = response.data?.jsErrors || [];
|
|
1939
|
+
if (errors.length === 0) {
|
|
1940
|
+
console.log("\u6CA1\u6709 JS \u9519\u8BEF");
|
|
1941
|
+
console.log("\u63D0\u793A: errors \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1942
|
+
return;
|
|
1756
1943
|
}
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
execSync("git pull --ff-only", { cwd: COMMUNITY_SITES_DIR, stdio: "pipe" });
|
|
1764
|
-
console.log("\u66F4\u65B0\u5B8C\u6210\u3002");
|
|
1765
|
-
} catch (e) {
|
|
1766
|
-
console.error(`\u66F4\u65B0\u5931\u8D25: ${e instanceof Error ? e.message : e}`);
|
|
1767
|
-
console.error(" \u624B\u52A8\u4FEE\u590D: cd ~/.bb-browser/bb-sites && git pull");
|
|
1768
|
-
process.exit(1);
|
|
1944
|
+
console.log(`JS \u9519\u8BEF (${errors.length} \u6761):
|
|
1945
|
+
`);
|
|
1946
|
+
for (const err of errors) {
|
|
1947
|
+
console.log(`[ERROR] ${err.message}`);
|
|
1948
|
+
if (err.url) {
|
|
1949
|
+
console.log(` \u4F4D\u7F6E: ${err.url}:${err.lineNumber || 0}:${err.columnNumber || 0}`);
|
|
1769
1950
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
execSync(`git clone ${COMMUNITY_REPO} ${COMMUNITY_SITES_DIR}`, { stdio: "pipe" });
|
|
1774
|
-
console.log("\u514B\u9686\u5B8C\u6210\u3002");
|
|
1775
|
-
} catch (e) {
|
|
1776
|
-
console.error(`\u514B\u9686\u5931\u8D25: ${e instanceof Error ? e.message : e}`);
|
|
1777
|
-
console.error(` \u624B\u52A8\u4FEE\u590D: git clone ${COMMUNITY_REPO} ~/.bb-browser/bb-sites`);
|
|
1778
|
-
process.exit(1);
|
|
1951
|
+
if (err.stackTrace) {
|
|
1952
|
+
console.log(` \u5806\u6808:`);
|
|
1953
|
+
console.log(err.stackTrace.split("\n").map((line) => ` ${line}`).join("\n"));
|
|
1779
1954
|
}
|
|
1955
|
+
console.log("");
|
|
1780
1956
|
}
|
|
1781
|
-
const sites = scanSites(COMMUNITY_SITES_DIR, "community");
|
|
1782
|
-
console.log(`\u5DF2\u5B89\u88C5 ${sites.length} \u4E2A\u793E\u533A adapter\u3002`);
|
|
1783
1957
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1958
|
+
|
|
1959
|
+
// packages/cli/src/commands/trace.ts
|
|
1960
|
+
async function traceCommand(subCommand, options = {}) {
|
|
1961
|
+
const response = await sendCommand({
|
|
1962
|
+
id: crypto.randomUUID(),
|
|
1963
|
+
action: "trace",
|
|
1964
|
+
traceCommand: subCommand,
|
|
1965
|
+
tabId: options.tabId
|
|
1966
|
+
});
|
|
1967
|
+
if (options.json) {
|
|
1968
|
+
console.log(JSON.stringify(response));
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
if (!response.success) {
|
|
1972
|
+
throw new Error(response.error || "Trace command failed");
|
|
1973
|
+
}
|
|
1974
|
+
const data = response.data;
|
|
1975
|
+
switch (subCommand) {
|
|
1976
|
+
case "start": {
|
|
1977
|
+
const status = data?.traceStatus;
|
|
1978
|
+
console.log("\u5F00\u59CB\u5F55\u5236\u7528\u6237\u64CD\u4F5C");
|
|
1979
|
+
console.log(`\u6807\u7B7E\u9875 ID: ${status?.tabId || "N/A"}`);
|
|
1980
|
+
console.log("\n\u5728\u6D4F\u89C8\u5668\u4E2D\u8FDB\u884C\u64CD\u4F5C\uFF0C\u5B8C\u6210\u540E\u8FD0\u884C 'bb-browser trace stop' \u505C\u6B62\u5F55\u5236");
|
|
1981
|
+
break;
|
|
1982
|
+
}
|
|
1983
|
+
case "stop": {
|
|
1984
|
+
const events = data?.traceEvents || [];
|
|
1985
|
+
const status = data?.traceStatus;
|
|
1986
|
+
console.log(`\u5F55\u5236\u5B8C\u6210\uFF0C\u5171 ${events.length} \u4E2A\u4E8B\u4EF6
|
|
1987
|
+
`);
|
|
1988
|
+
if (events.length === 0) {
|
|
1989
|
+
console.log("\u6CA1\u6709\u5F55\u5236\u5230\u4EFB\u4F55\u64CD\u4F5C");
|
|
1990
|
+
break;
|
|
1794
1991
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1992
|
+
for (let i = 0; i < events.length; i++) {
|
|
1993
|
+
const event = events[i];
|
|
1994
|
+
const refStr = event.ref !== void 0 ? `@${event.ref}` : "";
|
|
1995
|
+
switch (event.type) {
|
|
1996
|
+
case "navigation":
|
|
1997
|
+
console.log(`${i + 1}. \u5BFC\u822A\u5230: ${event.url}`);
|
|
1998
|
+
break;
|
|
1999
|
+
case "click":
|
|
2000
|
+
console.log(`${i + 1}. \u70B9\u51FB ${refStr} [${event.elementRole}] "${event.elementName || ""}"`);
|
|
2001
|
+
break;
|
|
2002
|
+
case "fill":
|
|
2003
|
+
console.log(`${i + 1}. \u586B\u5145 ${refStr} [${event.elementRole}] "${event.elementName || ""}" <- "${event.value}"`);
|
|
2004
|
+
break;
|
|
2005
|
+
case "select":
|
|
2006
|
+
console.log(`${i + 1}. \u9009\u62E9 ${refStr} [${event.elementRole}] "${event.elementName || ""}" <- "${event.value}"`);
|
|
2007
|
+
break;
|
|
2008
|
+
case "check":
|
|
2009
|
+
console.log(`${i + 1}. ${event.checked ? "\u52FE\u9009" : "\u53D6\u6D88\u52FE\u9009"} ${refStr} [${event.elementRole}] "${event.elementName || ""}"`);
|
|
2010
|
+
break;
|
|
2011
|
+
case "press":
|
|
2012
|
+
console.log(`${i + 1}. \u6309\u952E ${event.key}`);
|
|
2013
|
+
break;
|
|
2014
|
+
case "scroll":
|
|
2015
|
+
console.log(`${i + 1}. \u6EDA\u52A8 ${event.direction} ${event.pixels}px`);
|
|
2016
|
+
break;
|
|
2017
|
+
default:
|
|
2018
|
+
console.log(`${i + 1}. ${event.type}`);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
console.log(`
|
|
2022
|
+
\u72B6\u6001: ${status?.recording ? "\u5F55\u5236\u4E2D" : "\u5DF2\u505C\u6B62"}`);
|
|
2023
|
+
break;
|
|
1798
2024
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
const flagName = args[i].slice(2);
|
|
1807
|
-
if (flagName in site.args && args[i + 1]) {
|
|
1808
|
-
argMap[flagName] = args[i + 1];
|
|
1809
|
-
i++;
|
|
2025
|
+
case "status": {
|
|
2026
|
+
const status = data?.traceStatus;
|
|
2027
|
+
if (status?.recording) {
|
|
2028
|
+
console.log(`\u5F55\u5236\u4E2D (\u6807\u7B7E\u9875 ${status.tabId})`);
|
|
2029
|
+
console.log(`\u5DF2\u5F55\u5236 ${status.eventCount} \u4E2A\u4E8B\u4EF6`);
|
|
2030
|
+
} else {
|
|
2031
|
+
console.log("\u672A\u5728\u5F55\u5236");
|
|
1810
2032
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
default:
|
|
2036
|
+
throw new Error(`\u672A\u77E5\u7684 trace \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
// packages/cli/src/commands/fetch.ts
|
|
2041
|
+
function matchTabOrigin2(tabUrl, targetHostname) {
|
|
2042
|
+
try {
|
|
2043
|
+
const tabHostname = new URL(tabUrl).hostname;
|
|
2044
|
+
return tabHostname === targetHostname || tabHostname.endsWith("." + targetHostname);
|
|
2045
|
+
} catch {
|
|
2046
|
+
return false;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
async function ensureTabForOrigin(origin, hostname) {
|
|
2050
|
+
const listReq = { id: generateId(), action: "tab_list" };
|
|
2051
|
+
const listResp = await sendCommand(listReq);
|
|
2052
|
+
if (listResp.success && listResp.data?.tabs) {
|
|
2053
|
+
const matchingTab = listResp.data.tabs.find(
|
|
2054
|
+
(tab) => matchTabOrigin2(tab.url, hostname)
|
|
2055
|
+
);
|
|
2056
|
+
if (matchingTab) {
|
|
2057
|
+
return matchingTab.tabId;
|
|
1813
2058
|
}
|
|
1814
2059
|
}
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2060
|
+
const newResp = await sendCommand({ id: generateId(), action: "tab_new", url: origin });
|
|
2061
|
+
if (!newResp.success) {
|
|
2062
|
+
throw new Error(`\u65E0\u6CD5\u6253\u5F00 ${origin}: ${newResp.error}`);
|
|
2063
|
+
}
|
|
2064
|
+
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
2065
|
+
return newResp.data?.tabId;
|
|
2066
|
+
}
|
|
2067
|
+
function buildFetchScript(url, options) {
|
|
2068
|
+
const method = (options.method || "GET").toUpperCase();
|
|
2069
|
+
const hasBody = options.body && method !== "GET" && method !== "HEAD";
|
|
2070
|
+
let headersExpr = "{}";
|
|
2071
|
+
if (options.headers) {
|
|
2072
|
+
try {
|
|
2073
|
+
JSON.parse(options.headers);
|
|
2074
|
+
headersExpr = options.headers;
|
|
2075
|
+
} catch {
|
|
2076
|
+
throw new Error(`--headers must be valid JSON. Got: ${options.headers}`);
|
|
1819
2077
|
}
|
|
1820
2078
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
2079
|
+
return `(async () => {
|
|
2080
|
+
try {
|
|
2081
|
+
const resp = await fetch(${JSON.stringify(url)}, {
|
|
2082
|
+
method: ${JSON.stringify(method)},
|
|
2083
|
+
credentials: 'include',
|
|
2084
|
+
headers: ${headersExpr}${hasBody ? `,
|
|
2085
|
+
body: ${JSON.stringify(options.body)}` : ""}
|
|
2086
|
+
});
|
|
2087
|
+
const contentType = resp.headers.get('content-type') || '';
|
|
2088
|
+
let body;
|
|
2089
|
+
if (contentType.includes('application/json') && resp.status !== 204) {
|
|
2090
|
+
try { body = await resp.json(); } catch { body = await resp.text(); }
|
|
2091
|
+
} else {
|
|
2092
|
+
body = await resp.text();
|
|
2093
|
+
}
|
|
2094
|
+
return JSON.stringify({
|
|
2095
|
+
status: resp.status,
|
|
2096
|
+
contentType,
|
|
2097
|
+
body
|
|
2098
|
+
});
|
|
2099
|
+
} catch (e) {
|
|
2100
|
+
return JSON.stringify({ error: e.message });
|
|
1831
2101
|
}
|
|
2102
|
+
})()`;
|
|
2103
|
+
}
|
|
2104
|
+
async function fetchCommand(url, options = {}) {
|
|
2105
|
+
if (!url) {
|
|
2106
|
+
throw new Error(
|
|
2107
|
+
"\u7F3A\u5C11 URL \u53C2\u6570\n \u7528\u6CD5: bb-browser fetch <url> [--json] [--method POST] [--body '{...}']\n \u793A\u4F8B: bb-browser fetch https://www.reddit.com/api/me.json --json"
|
|
2108
|
+
);
|
|
1832
2109
|
}
|
|
1833
|
-
const jsContent = readFileSync(site.filePath, "utf-8");
|
|
1834
|
-
const jsBody = jsContent.replace(/\/\*\s*@meta[\s\S]*?\*\//, "").trim();
|
|
1835
|
-
const argsJson = JSON.stringify(argMap);
|
|
1836
|
-
const script = `(${jsBody})(${argsJson})`;
|
|
1837
2110
|
await ensureDaemonRunning();
|
|
2111
|
+
const isAbsolute = url.startsWith("http://") || url.startsWith("https://");
|
|
1838
2112
|
let targetTabId = options.tabId;
|
|
1839
|
-
if (
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
}
|
|
2113
|
+
if (isAbsolute) {
|
|
2114
|
+
let origin;
|
|
2115
|
+
let hostname;
|
|
2116
|
+
try {
|
|
2117
|
+
const parsed = new URL(url);
|
|
2118
|
+
origin = parsed.origin;
|
|
2119
|
+
hostname = parsed.hostname;
|
|
2120
|
+
} catch {
|
|
2121
|
+
throw new Error(`\u65E0\u6548\u7684 URL: ${url}`);
|
|
1849
2122
|
}
|
|
1850
2123
|
if (!targetTabId) {
|
|
1851
|
-
|
|
1852
|
-
id: generateId(),
|
|
1853
|
-
action: "tab_new",
|
|
1854
|
-
url: `https://${site.domain}`
|
|
1855
|
-
});
|
|
1856
|
-
targetTabId = newResp.data?.tabId;
|
|
1857
|
-
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
2124
|
+
targetTabId = await ensureTabForOrigin(origin, hostname);
|
|
1858
2125
|
}
|
|
1859
2126
|
}
|
|
2127
|
+
const script = buildFetchScript(url, options);
|
|
1860
2128
|
const evalReq = { id: generateId(), action: "eval", script, tabId: targetTabId };
|
|
1861
2129
|
const evalResp = await sendCommand(evalReq);
|
|
1862
2130
|
if (!evalResp.success) {
|
|
1863
|
-
|
|
1864
|
-
if (options.json) {
|
|
1865
|
-
console.log(JSON.stringify({ id: evalReq.id, success: false, error: evalResp.error || "eval failed", hint }));
|
|
1866
|
-
} else {
|
|
1867
|
-
console.error(`[error] site ${name}: ${evalResp.error || "eval failed"}`);
|
|
1868
|
-
if (hint) console.error(` Hint: ${hint}`);
|
|
1869
|
-
}
|
|
1870
|
-
process.exit(1);
|
|
2131
|
+
throw new Error(`Fetch \u5931\u8D25: ${evalResp.error}`);
|
|
1871
2132
|
}
|
|
1872
|
-
const
|
|
1873
|
-
if (
|
|
1874
|
-
|
|
1875
|
-
console.log(JSON.stringify({ id: evalReq.id, success: true, data: null }));
|
|
1876
|
-
} else {
|
|
1877
|
-
console.log("(no output)");
|
|
1878
|
-
}
|
|
1879
|
-
return;
|
|
2133
|
+
const rawResult = evalResp.data?.result;
|
|
2134
|
+
if (rawResult === void 0 || rawResult === null) {
|
|
2135
|
+
throw new Error("Fetch \u672A\u8FD4\u56DE\u7ED3\u679C");
|
|
1880
2136
|
}
|
|
1881
|
-
let
|
|
2137
|
+
let result;
|
|
1882
2138
|
try {
|
|
1883
|
-
|
|
2139
|
+
result = typeof rawResult === "string" ? JSON.parse(rawResult) : rawResult;
|
|
1884
2140
|
} catch {
|
|
1885
|
-
|
|
2141
|
+
console.log(rawResult);
|
|
2142
|
+
return;
|
|
1886
2143
|
}
|
|
1887
|
-
if (
|
|
1888
|
-
|
|
1889
|
-
const checkText = `${errObj.error} ${errObj.hint || ""}`;
|
|
1890
|
-
const isAuthError = /401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth/i.test(checkText);
|
|
1891
|
-
const loginHint = isAuthError && site.domain ? `Please log in to https://${site.domain} in your browser first, then retry.` : void 0;
|
|
1892
|
-
const hint = loginHint || errObj.hint;
|
|
1893
|
-
const reportHint = `If this is an adapter bug, report via: gh issue create --repo epiral/bb-sites --title "[${name}] <description>" OR: bb-browser site github/issue-create epiral/bb-sites --title "[${name}] <description>"`;
|
|
1894
|
-
if (options.json) {
|
|
1895
|
-
console.log(JSON.stringify({ id: evalReq.id, success: false, error: errObj.error, hint, reportHint }));
|
|
1896
|
-
} else {
|
|
1897
|
-
console.error(`[error] site ${name}: ${errObj.error}`);
|
|
1898
|
-
if (hint) console.error(` Hint: ${hint}`);
|
|
1899
|
-
console.error(` Report: gh issue create --repo epiral/bb-sites --title "[${name}] ..."`);
|
|
1900
|
-
console.error(` or: bb-browser site github/issue-create epiral/bb-sites --title "[${name}] ..."`);
|
|
1901
|
-
}
|
|
1902
|
-
process.exit(1);
|
|
2144
|
+
if (result.error) {
|
|
2145
|
+
throw new Error(`Fetch error: ${result.error}`);
|
|
1903
2146
|
}
|
|
1904
|
-
if (options.
|
|
1905
|
-
|
|
2147
|
+
if (options.output) {
|
|
2148
|
+
const { writeFileSync } = await import("fs");
|
|
2149
|
+
const content = typeof result.body === "object" ? JSON.stringify(result.body, null, 2) : String(result.body);
|
|
2150
|
+
writeFileSync(options.output, content, "utf-8");
|
|
2151
|
+
console.log(`\u5DF2\u5199\u5165 ${options.output} (${result.status}, ${content.length} bytes)`);
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
if (typeof result.body === "object") {
|
|
2155
|
+
console.log(JSON.stringify(result.body, null, 2));
|
|
1906
2156
|
} else {
|
|
1907
|
-
console.log(
|
|
2157
|
+
console.log(result.body);
|
|
1908
2158
|
}
|
|
1909
2159
|
}
|
|
1910
|
-
async function siteCommand(args, options = {}) {
|
|
1911
|
-
const subCommand = args[0];
|
|
1912
|
-
if (!subCommand || subCommand === "--help" || subCommand === "-h") {
|
|
1913
|
-
console.log(`bb-browser site - \u7F51\u7AD9 CLI \u5316\uFF08\u7BA1\u7406\u548C\u8FD0\u884C site adapter\uFF09
|
|
1914
|
-
|
|
1915
|
-
\u7528\u6CD5:
|
|
1916
|
-
bb-browser site list \u5217\u51FA\u6240\u6709\u53EF\u7528 adapter
|
|
1917
|
-
bb-browser site search <query> \u641C\u7D22 adapter
|
|
1918
|
-
bb-browser site <name> [args...] \u8FD0\u884C adapter\uFF08\u7B80\u5199\uFF09
|
|
1919
|
-
bb-browser site run <name> [args...] \u8FD0\u884C adapter
|
|
1920
|
-
bb-browser site update \u66F4\u65B0\u793E\u533A adapter \u5E93 (git clone/pull)
|
|
1921
|
-
|
|
1922
|
-
\u76EE\u5F55:
|
|
1923
|
-
${LOCAL_SITES_DIR} \u79C1\u6709 adapter\uFF08\u4F18\u5148\uFF09
|
|
1924
|
-
${COMMUNITY_SITES_DIR} \u793E\u533A adapter
|
|
1925
|
-
|
|
1926
|
-
\u793A\u4F8B:
|
|
1927
|
-
bb-browser site update
|
|
1928
|
-
bb-browser site list
|
|
1929
|
-
bb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...
|
|
1930
|
-
bb-browser site twitter/user yan5xu
|
|
1931
|
-
bb-browser site search reddit
|
|
1932
2160
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2161
|
+
// packages/cli/src/commands/history.ts
|
|
2162
|
+
async function historyCommand(subCommand, options = {}) {
|
|
2163
|
+
const response = await sendCommand({
|
|
2164
|
+
id: crypto.randomUUID(),
|
|
2165
|
+
action: "history",
|
|
2166
|
+
historyCommand: subCommand,
|
|
2167
|
+
text: options.query,
|
|
2168
|
+
ms: options.days
|
|
2169
|
+
});
|
|
2170
|
+
if (options.json) {
|
|
2171
|
+
console.log(JSON.stringify(response));
|
|
1936
2172
|
return;
|
|
1937
2173
|
}
|
|
2174
|
+
if (!response.success) {
|
|
2175
|
+
throw new Error(response.error || "History command failed");
|
|
2176
|
+
}
|
|
2177
|
+
const data = response.data;
|
|
1938
2178
|
switch (subCommand) {
|
|
1939
|
-
case "
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
if (
|
|
1944
|
-
console.
|
|
1945
|
-
|
|
1946
|
-
process.exit(1);
|
|
2179
|
+
case "search": {
|
|
2180
|
+
const items = data?.historyItems || [];
|
|
2181
|
+
console.log(`\u627E\u5230 ${items.length} \u6761\u5386\u53F2\u8BB0\u5F55
|
|
2182
|
+
`);
|
|
2183
|
+
if (items.length === 0) {
|
|
2184
|
+
console.log("\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u5386\u53F2\u8BB0\u5F55");
|
|
2185
|
+
break;
|
|
1947
2186
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
case "run":
|
|
1954
|
-
if (!args[1]) {
|
|
1955
|
-
console.error("[error] site run: <name> is required.");
|
|
1956
|
-
console.error(" Usage: bb-browser site run <name> [args...]");
|
|
1957
|
-
console.error(" Try: bb-browser site list");
|
|
1958
|
-
process.exit(1);
|
|
2187
|
+
for (let i = 0; i < items.length; i++) {
|
|
2188
|
+
const item = items[i];
|
|
2189
|
+
console.log(`${i + 1}. ${item.title || "(\u65E0\u6807\u9898)"}`);
|
|
2190
|
+
console.log(` ${item.url}`);
|
|
2191
|
+
console.log(` \u8BBF\u95EE\u6B21\u6570: ${item.visitCount}`);
|
|
1959
2192
|
}
|
|
1960
|
-
await siteRun(args[1], args.slice(2), options);
|
|
1961
2193
|
break;
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
console.
|
|
1969
|
-
|
|
2194
|
+
}
|
|
2195
|
+
case "domains": {
|
|
2196
|
+
const domains = data?.historyDomains || [];
|
|
2197
|
+
console.log(`\u627E\u5230 ${domains.length} \u4E2A\u57DF\u540D
|
|
2198
|
+
`);
|
|
2199
|
+
if (domains.length === 0) {
|
|
2200
|
+
console.log("\u6CA1\u6709\u627E\u5230\u5386\u53F2\u8BB0\u5F55");
|
|
2201
|
+
break;
|
|
2202
|
+
}
|
|
2203
|
+
for (let i = 0; i < domains.length; i++) {
|
|
2204
|
+
const domain = domains[i];
|
|
2205
|
+
console.log(`${i + 1}. ${domain.domain}`);
|
|
2206
|
+
console.log(` \u8BBF\u95EE\u6B21\u6570: ${domain.visits}`);
|
|
1970
2207
|
}
|
|
1971
2208
|
break;
|
|
2209
|
+
}
|
|
2210
|
+
default:
|
|
2211
|
+
throw new Error(`\u672A\u77E5\u7684 history \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1972
2212
|
}
|
|
1973
2213
|
}
|
|
1974
2214
|
|
|
@@ -1977,88 +2217,64 @@ var VERSION = "0.3.0";
|
|
|
1977
2217
|
var HELP_TEXT = `
|
|
1978
2218
|
bb-browser - AI Agent \u6D4F\u89C8\u5668\u81EA\u52A8\u5316\u5DE5\u5177
|
|
1979
2219
|
|
|
2220
|
+
\u63D0\u793A\uFF1A\u5927\u591A\u6570\u6570\u636E\u83B7\u53D6\u4EFB\u52A1\u8BF7\u76F4\u63A5\u4F7F\u7528 site \u547D\u4EE4\uFF0C\u65E0\u9700\u624B\u52A8\u64CD\u4F5C\u6D4F\u89C8\u5668\uFF1A
|
|
2221
|
+
bb-browser site list \u67E5\u770B\u6240\u6709\u53EF\u7528\u547D\u4EE4
|
|
2222
|
+
bb-browser site twitter/search "AI" \u793A\u4F8B\uFF1A\u641C\u7D22\u63A8\u6587
|
|
2223
|
+
bb-browser site xueqiu/hot-stock 5 \u793A\u4F8B\uFF1A\u83B7\u53D6\u4EBA\u6C14\u80A1\u7968
|
|
2224
|
+
|
|
1980
2225
|
\u7528\u6CD5\uFF1A
|
|
1981
2226
|
bb-browser <command> [options]
|
|
1982
2227
|
|
|
1983
|
-
\
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
press <key>
|
|
2001
|
-
scroll <dir> [px]
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
tab
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
network route <url> [--abort|--body <json>] \u62E6\u622A\u8BF7\u6C42
|
|
2022
|
-
network unroute [url] \u79FB\u9664\u62E6\u622A\u89C4\u5219
|
|
2023
|
-
network clear \u6E05\u7A7A\u8BF7\u6C42\u8BB0\u5F55
|
|
2024
|
-
console \u67E5\u770B\u63A7\u5236\u53F0\u6D88\u606F
|
|
2025
|
-
console --clear \u6E05\u7A7A\u63A7\u5236\u53F0
|
|
2026
|
-
errors \u67E5\u770B JS \u9519\u8BEF
|
|
2027
|
-
errors --clear \u6E05\u7A7A\u9519\u8BEF\u8BB0\u5F55
|
|
2028
|
-
trace start \u5F00\u59CB\u5F55\u5236\u7528\u6237\u64CD\u4F5C
|
|
2029
|
-
trace stop \u505C\u6B62\u5F55\u5236\uFF0C\u8F93\u51FA\u4E8B\u4EF6\u5217\u8868
|
|
2030
|
-
trace status \u67E5\u770B\u5F55\u5236\u72B6\u6001
|
|
2031
|
-
fetch <url> \u5728\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u4E2D fetch\uFF08\u81EA\u52A8\u540C\u6E90\u8DEF\u7531\uFF0C\u5E26\u767B\u5F55\u6001\uFF09
|
|
2032
|
-
site \u7F51\u7AD9 CLI \u5316 \u2014 \u7BA1\u7406\u548C\u8FD0\u884C site adapter
|
|
2033
|
-
site list \u5217\u51FA\u6240\u6709\u53EF\u7528 adapter
|
|
2034
|
-
site search <q> \u641C\u7D22 adapter
|
|
2035
|
-
site <name> \u8FD0\u884C adapter\uFF08\u5982 site reddit/thread <url>\uFF09
|
|
2036
|
-
site update \u66F4\u65B0\u793E\u533A adapter \u5E93
|
|
2037
|
-
guide \u5982\u4F55\u628A\u4EFB\u4F55\u7F51\u7AD9\u53D8\u6210 site adapter\uFF08\u5F00\u53D1\u6307\u5357\uFF09
|
|
2228
|
+
\u5F00\u59CB\u4F7F\u7528\uFF1A
|
|
2229
|
+
site recommend \u63A8\u8350\u4F60\u53EF\u80FD\u9700\u8981\u7684 adapter\uFF08\u57FA\u4E8E\u6D4F\u89C8\u5386\u53F2\uFF09
|
|
2230
|
+
site list \u5217\u51FA\u6240\u6709 adapter
|
|
2231
|
+
site info <name> \u67E5\u770B adapter \u7528\u6CD5\uFF08\u53C2\u6570\u3001\u8FD4\u56DE\u503C\u3001\u793A\u4F8B\uFF09
|
|
2232
|
+
site <name> [args] \u8FD0\u884C adapter
|
|
2233
|
+
site update \u66F4\u65B0\u793E\u533A adapter \u5E93
|
|
2234
|
+
guide \u5982\u4F55\u628A\u4EFB\u4F55\u7F51\u7AD9\u53D8\u6210 adapter
|
|
2235
|
+
|
|
2236
|
+
\u6D4F\u89C8\u5668\u64CD\u4F5C\uFF1A
|
|
2237
|
+
open <url> [--tab] \u6253\u5F00 URL
|
|
2238
|
+
snapshot [-i] [-c] [-d <n>] \u83B7\u53D6\u9875\u9762\u5FEB\u7167
|
|
2239
|
+
click <ref> \u70B9\u51FB\u5143\u7D20
|
|
2240
|
+
hover <ref> \u60AC\u505C\u5143\u7D20
|
|
2241
|
+
fill <ref> <text> \u586B\u5145\u8F93\u5165\u6846\uFF08\u6E05\u7A7A\u540E\u586B\u5165\uFF09
|
|
2242
|
+
type <ref> <text> \u9010\u5B57\u7B26\u8F93\u5165\uFF08\u4E0D\u6E05\u7A7A\uFF09
|
|
2243
|
+
check/uncheck <ref> \u52FE\u9009/\u53D6\u6D88\u590D\u9009\u6846
|
|
2244
|
+
select <ref> <val> \u4E0B\u62C9\u6846\u9009\u62E9
|
|
2245
|
+
press <key> \u53D1\u9001\u6309\u952E
|
|
2246
|
+
scroll <dir> [px] \u6EDA\u52A8\u9875\u9762
|
|
2247
|
+
|
|
2248
|
+
\u9875\u9762\u4FE1\u606F\uFF1A
|
|
2249
|
+
get text|url|title <ref> \u83B7\u53D6\u9875\u9762\u5185\u5BB9
|
|
2250
|
+
screenshot [path] \u622A\u56FE
|
|
2251
|
+
eval "<js>" \u6267\u884C JavaScript
|
|
2252
|
+
fetch <url> \u5E26\u767B\u5F55\u6001\u7684 HTTP \u8BF7\u6C42
|
|
2253
|
+
|
|
2254
|
+
\u6807\u7B7E\u9875\uFF1A
|
|
2255
|
+
tab [list|new|close|<n>] \u7BA1\u7406\u6807\u7B7E\u9875
|
|
2256
|
+
|
|
2257
|
+
\u5BFC\u822A\uFF1A
|
|
2258
|
+
back / forward / refresh \u540E\u9000 / \u524D\u8FDB / \u5237\u65B0
|
|
2259
|
+
|
|
2260
|
+
\u8C03\u8BD5\uFF1A
|
|
2261
|
+
network requests [filter] \u67E5\u770B\u7F51\u7EDC\u8BF7\u6C42
|
|
2262
|
+
console [--clear] \u67E5\u770B/\u6E05\u7A7A\u63A7\u5236\u53F0
|
|
2263
|
+
errors [--clear] \u67E5\u770B/\u6E05\u7A7A JS \u9519\u8BEF
|
|
2264
|
+
trace start|stop|status \u5F55\u5236\u7528\u6237\u64CD\u4F5C
|
|
2265
|
+
history search|domains \u67E5\u770B\u6D4F\u89C8\u5386\u53F2
|
|
2038
2266
|
|
|
2039
2267
|
\u9009\u9879\uFF1A
|
|
2040
|
-
--json
|
|
2041
|
-
|
|
2042
|
-
-
|
|
2043
|
-
-
|
|
2268
|
+
--json \u4EE5 JSON \u683C\u5F0F\u8F93\u51FA
|
|
2269
|
+
--jq <expr> \u5BF9 JSON \u8F93\u51FA\u5E94\u7528 jq \u8FC7\u6EE4\uFF08\u76F4\u63A5\u4F5C\u7528\u4E8E\u6570\u636E\uFF0C\u8DF3\u8FC7 id/success \u4FE1\u5C01\uFF09
|
|
2270
|
+
-i, --interactive \u53EA\u8F93\u51FA\u53EF\u4EA4\u4E92\u5143\u7D20\uFF08snapshot \u547D\u4EE4\uFF09
|
|
2271
|
+
-c, --compact \u79FB\u9664\u7A7A\u7ED3\u6784\u8282\u70B9\uFF08snapshot \u547D\u4EE4\uFF09
|
|
2272
|
+
-d, --depth <n> \u9650\u5236\u6811\u6DF1\u5EA6\uFF08snapshot \u547D\u4EE4\uFF09
|
|
2044
2273
|
-s, --selector <sel> \u9650\u5B9A CSS \u9009\u62E9\u5668\u8303\u56F4\uFF08snapshot \u547D\u4EE4\uFF09
|
|
2045
|
-
--tab <tabId>
|
|
2046
|
-
--mcp
|
|
2047
|
-
--help, -h
|
|
2048
|
-
--version, -v
|
|
2049
|
-
|
|
2050
|
-
\u793A\u4F8B\uFF1A
|
|
2051
|
-
bb-browser open https://example.com
|
|
2052
|
-
bb-browser snapshot --json
|
|
2053
|
-
bb-browser click @5
|
|
2054
|
-
bb-browser fill @3 "hello world"
|
|
2055
|
-
bb-browser type @3 "append text"
|
|
2056
|
-
bb-browser get text @5
|
|
2057
|
-
bb-browser get url
|
|
2058
|
-
bb-browser press Enter
|
|
2059
|
-
bb-browser press Control+a
|
|
2060
|
-
bb-browser daemon
|
|
2061
|
-
bb-browser stop
|
|
2274
|
+
--tab <tabId> \u6307\u5B9A\u64CD\u4F5C\u7684\u6807\u7B7E\u9875 ID
|
|
2275
|
+
--mcp \u542F\u52A8 MCP server\uFF08\u7528\u4E8E Claude Code / Cursor \u7B49 AI \u5DE5\u5177\uFF09
|
|
2276
|
+
--help, -h \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
|
|
2277
|
+
--version, -v \u663E\u793A\u7248\u672C\u53F7
|
|
2062
2278
|
`.trim();
|
|
2063
2279
|
function parseArgs(argv) {
|
|
2064
2280
|
const args = argv.slice(2);
|
|
@@ -2081,6 +2297,13 @@ function parseArgs(argv) {
|
|
|
2081
2297
|
}
|
|
2082
2298
|
if (arg === "--json") {
|
|
2083
2299
|
result.flags.json = true;
|
|
2300
|
+
} else if (arg === "--jq") {
|
|
2301
|
+
skipNext = true;
|
|
2302
|
+
const nextIdx = args.indexOf(arg) + 1;
|
|
2303
|
+
if (nextIdx < args.length) {
|
|
2304
|
+
result.flags.jq = args[nextIdx];
|
|
2305
|
+
result.flags.json = true;
|
|
2306
|
+
}
|
|
2084
2307
|
} else if (arg === "--help" || arg === "-h") {
|
|
2085
2308
|
result.flags.help = true;
|
|
2086
2309
|
} else if (arg === "--version" || arg === "-v") {
|
|
@@ -2101,6 +2324,12 @@ function parseArgs(argv) {
|
|
|
2101
2324
|
if (nextIdx < args.length) {
|
|
2102
2325
|
result.flags.selector = args[nextIdx];
|
|
2103
2326
|
}
|
|
2327
|
+
} else if (arg === "--days") {
|
|
2328
|
+
skipNext = true;
|
|
2329
|
+
const nextIdx = args.indexOf(arg) + 1;
|
|
2330
|
+
if (nextIdx < args.length) {
|
|
2331
|
+
result.flags.days = parseInt(args[nextIdx], 10);
|
|
2332
|
+
}
|
|
2104
2333
|
} else if (arg === "--id") {
|
|
2105
2334
|
skipNext = true;
|
|
2106
2335
|
} else if (arg === "--tab") {
|
|
@@ -2116,6 +2345,7 @@ function parseArgs(argv) {
|
|
|
2116
2345
|
}
|
|
2117
2346
|
async function main() {
|
|
2118
2347
|
const parsed = parseArgs(process.argv);
|
|
2348
|
+
setJqExpression(parsed.flags.jq);
|
|
2119
2349
|
const tabArgIdx = process.argv.indexOf("--tab");
|
|
2120
2350
|
const globalTabId = tabArgIdx >= 0 && process.argv[tabArgIdx + 1] ? parseInt(process.argv[tabArgIdx + 1], 10) : void 0;
|
|
2121
2351
|
if (parsed.flags.version) {
|
|
@@ -2429,6 +2659,23 @@ async function main() {
|
|
|
2429
2659
|
await traceCommand(subCmd, { json: parsed.flags.json, tabId: globalTabId });
|
|
2430
2660
|
break;
|
|
2431
2661
|
}
|
|
2662
|
+
case "history": {
|
|
2663
|
+
const subCmd = parsed.args[0];
|
|
2664
|
+
if (!subCmd || !["search", "domains"].includes(subCmd)) {
|
|
2665
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11\u6216\u65E0\u6548\u7684\u5B50\u547D\u4EE4");
|
|
2666
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser history <search|domains> [query] [--days <n>]");
|
|
2667
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser history search github");
|
|
2668
|
+
console.error(" bb-browser history domains --days 7");
|
|
2669
|
+
process.exit(1);
|
|
2670
|
+
}
|
|
2671
|
+
const query = parsed.args.slice(1).join(" ");
|
|
2672
|
+
await historyCommand(subCmd, {
|
|
2673
|
+
json: parsed.flags.json,
|
|
2674
|
+
days: parsed.flags.days || 30,
|
|
2675
|
+
query
|
|
2676
|
+
});
|
|
2677
|
+
break;
|
|
2678
|
+
}
|
|
2432
2679
|
case "fetch": {
|
|
2433
2680
|
const fetchUrl = parsed.args[0];
|
|
2434
2681
|
if (!fetchUrl) {
|
|
@@ -2456,7 +2703,12 @@ async function main() {
|
|
|
2456
2703
|
break;
|
|
2457
2704
|
}
|
|
2458
2705
|
case "site": {
|
|
2459
|
-
await siteCommand(parsed.args, {
|
|
2706
|
+
await siteCommand(parsed.args, {
|
|
2707
|
+
json: parsed.flags.json,
|
|
2708
|
+
jq: parsed.flags.jq,
|
|
2709
|
+
days: parsed.flags.days,
|
|
2710
|
+
tabId: globalTabId
|
|
2711
|
+
});
|
|
2460
2712
|
break;
|
|
2461
2713
|
}
|
|
2462
2714
|
case "guide": {
|