bb-browser 0.4.5 → 0.5.1
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 +82 -34
- package/README.zh-CN.md +82 -34
- 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 +1756 -1484
- 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,609 @@ 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
|
-
|
|
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
|
+
}
|
|
720
|
+
silentUpdate();
|
|
486
721
|
}
|
|
487
|
-
function
|
|
488
|
-
|
|
722
|
+
function silentUpdate() {
|
|
723
|
+
const gitDir = join(COMMUNITY_SITES_DIR, ".git");
|
|
724
|
+
if (!existsSync2(gitDir)) return;
|
|
725
|
+
import("child_process").then(({ spawn: spawn3 }) => {
|
|
726
|
+
const child = spawn3("git", ["pull", "--ff-only"], {
|
|
727
|
+
cwd: COMMUNITY_SITES_DIR,
|
|
728
|
+
stdio: "ignore",
|
|
729
|
+
detached: true
|
|
730
|
+
});
|
|
731
|
+
child.unref();
|
|
732
|
+
}).catch(() => {
|
|
733
|
+
});
|
|
489
734
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
735
|
+
|
|
736
|
+
// packages/cli/src/commands/open.ts
|
|
737
|
+
async function openCommand(url, options = {}) {
|
|
738
|
+
if (!url) {
|
|
739
|
+
throw new Error("\u7F3A\u5C11 URL \u53C2\u6570");
|
|
493
740
|
}
|
|
494
741
|
await ensureDaemonRunning();
|
|
495
|
-
let
|
|
496
|
-
if (
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
tabId
|
|
513
|
-
}
|
|
742
|
+
let normalizedUrl = url;
|
|
743
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
744
|
+
normalizedUrl = "https://" + url;
|
|
745
|
+
}
|
|
746
|
+
const request = {
|
|
747
|
+
id: generateId(),
|
|
748
|
+
action: "open",
|
|
749
|
+
url: normalizedUrl
|
|
750
|
+
};
|
|
751
|
+
if (options.tab !== void 0) {
|
|
752
|
+
if (options.tab === "current") {
|
|
753
|
+
request.tabId = "current";
|
|
754
|
+
} else {
|
|
755
|
+
const tabId = parseInt(options.tab, 10);
|
|
756
|
+
if (isNaN(tabId)) {
|
|
757
|
+
throw new Error(`\u65E0\u6548\u7684 tabId: ${options.tab}`);
|
|
758
|
+
}
|
|
759
|
+
request.tabId = tabId;
|
|
760
|
+
}
|
|
514
761
|
}
|
|
515
762
|
const response = await sendCommand(request);
|
|
516
763
|
if (options.json) {
|
|
517
764
|
console.log(JSON.stringify(response, null, 2));
|
|
518
765
|
} else {
|
|
519
766
|
if (response.success) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
767
|
+
console.log(`\u5DF2\u6253\u5F00: ${response.data?.url ?? normalizedUrl}`);
|
|
768
|
+
if (response.data?.title) {
|
|
769
|
+
console.log(`\u6807\u9898: ${response.data.title}`);
|
|
770
|
+
}
|
|
771
|
+
if (response.data?.tabId) {
|
|
772
|
+
console.log(`Tab ID: ${response.data.tabId}`);
|
|
773
|
+
}
|
|
774
|
+
const siteHint = getSiteHintForDomain(normalizedUrl);
|
|
775
|
+
if (siteHint) {
|
|
776
|
+
console.log(`
|
|
777
|
+
\u{1F4A1} ${siteHint}`);
|
|
524
778
|
}
|
|
525
779
|
} else {
|
|
526
780
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -529,35 +783,16 @@ async function waitCommand(target, options = {}) {
|
|
|
529
783
|
}
|
|
530
784
|
}
|
|
531
785
|
|
|
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
|
-
}
|
|
786
|
+
// packages/cli/src/commands/snapshot.ts
|
|
787
|
+
async function snapshotCommand(options = {}) {
|
|
551
788
|
await ensureDaemonRunning();
|
|
552
|
-
const { key, modifiers } = parseKey(keyString);
|
|
553
|
-
if (!key) {
|
|
554
|
-
throw new Error("\u65E0\u6548\u7684\u6309\u952E\u683C\u5F0F");
|
|
555
|
-
}
|
|
556
789
|
const request = {
|
|
557
790
|
id: generateId(),
|
|
558
|
-
action: "
|
|
559
|
-
|
|
560
|
-
|
|
791
|
+
action: "snapshot",
|
|
792
|
+
interactive: options.interactive,
|
|
793
|
+
compact: options.compact,
|
|
794
|
+
maxDepth: options.maxDepth,
|
|
795
|
+
selector: options.selector,
|
|
561
796
|
tabId: options.tabId
|
|
562
797
|
};
|
|
563
798
|
const response = await sendCommand(request);
|
|
@@ -565,8 +800,12 @@ async function pressCommand(keyString, options = {}) {
|
|
|
565
800
|
console.log(JSON.stringify(response, null, 2));
|
|
566
801
|
} else {
|
|
567
802
|
if (response.success) {
|
|
568
|
-
|
|
569
|
-
console.log(
|
|
803
|
+
console.log(`\u6807\u9898: ${response.data?.title ?? "(\u65E0\u6807\u9898)"}`);
|
|
804
|
+
console.log(`URL: ${response.data?.url ?? "(\u672A\u77E5)"}`);
|
|
805
|
+
if (response.data?.snapshotData?.snapshot) {
|
|
806
|
+
console.log("");
|
|
807
|
+
console.log(response.data.snapshotData.snapshot);
|
|
808
|
+
}
|
|
570
809
|
} else {
|
|
571
810
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
572
811
|
process.exit(1);
|
|
@@ -574,31 +813,20 @@ async function pressCommand(keyString, options = {}) {
|
|
|
574
813
|
}
|
|
575
814
|
}
|
|
576
815
|
|
|
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
|
-
}
|
|
816
|
+
// packages/cli/src/commands/click.ts
|
|
817
|
+
function parseRef(ref) {
|
|
818
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
819
|
+
}
|
|
820
|
+
async function clickCommand(ref, options = {}) {
|
|
821
|
+
if (!ref) {
|
|
822
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
595
823
|
}
|
|
596
824
|
await ensureDaemonRunning();
|
|
825
|
+
const parsedRef = parseRef(ref);
|
|
597
826
|
const request = {
|
|
598
827
|
id: generateId(),
|
|
599
|
-
action: "
|
|
600
|
-
|
|
601
|
-
pixels: pixelValue,
|
|
828
|
+
action: "click",
|
|
829
|
+
ref: parsedRef,
|
|
602
830
|
tabId: options.tabId
|
|
603
831
|
};
|
|
604
832
|
const response = await sendCommand(request);
|
|
@@ -606,7 +834,13 @@ async function scrollCommand(direction, pixels, options = {}) {
|
|
|
606
834
|
console.log(JSON.stringify(response, null, 2));
|
|
607
835
|
} else {
|
|
608
836
|
if (response.success) {
|
|
609
|
-
|
|
837
|
+
const role = response.data?.role ?? "element";
|
|
838
|
+
const name = response.data?.name;
|
|
839
|
+
if (name) {
|
|
840
|
+
console.log(`\u5DF2\u70B9\u51FB: ${role} "${name}"`);
|
|
841
|
+
} else {
|
|
842
|
+
console.log(`\u5DF2\u70B9\u51FB: ${role}`);
|
|
843
|
+
}
|
|
610
844
|
} else {
|
|
611
845
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
612
846
|
process.exit(1);
|
|
@@ -614,188 +848,20 @@ async function scrollCommand(direction, pixels, options = {}) {
|
|
|
614
848
|
}
|
|
615
849
|
}
|
|
616
850
|
|
|
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
|
-
}
|
|
851
|
+
// packages/cli/src/commands/hover.ts
|
|
852
|
+
function parseRef2(ref) {
|
|
853
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
684
854
|
}
|
|
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);
|
|
855
|
+
async function hoverCommand(ref, options = {}) {
|
|
856
|
+
if (!ref) {
|
|
857
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
790
858
|
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// packages/cli/src/commands/nav.ts
|
|
794
|
-
async function backCommand(options = {}) {
|
|
795
859
|
await ensureDaemonRunning();
|
|
860
|
+
const parsedRef = parseRef2(ref);
|
|
796
861
|
const request = {
|
|
797
862
|
id: generateId(),
|
|
798
|
-
action: "
|
|
863
|
+
action: "hover",
|
|
864
|
+
ref: parsedRef,
|
|
799
865
|
tabId: options.tabId
|
|
800
866
|
};
|
|
801
867
|
const response = await sendCommand(request);
|
|
@@ -803,11 +869,12 @@ async function backCommand(options = {}) {
|
|
|
803
869
|
console.log(JSON.stringify(response, null, 2));
|
|
804
870
|
} else {
|
|
805
871
|
if (response.success) {
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
872
|
+
const role = response.data?.role ?? "element";
|
|
873
|
+
const name = response.data?.name;
|
|
874
|
+
if (name) {
|
|
875
|
+
console.log(`\u5DF2\u60AC\u505C: ${role} "${name}"`);
|
|
809
876
|
} else {
|
|
810
|
-
console.log(
|
|
877
|
+
console.log(`\u5DF2\u60AC\u505C: ${role}`);
|
|
811
878
|
}
|
|
812
879
|
} else {
|
|
813
880
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -815,11 +882,25 @@ async function backCommand(options = {}) {
|
|
|
815
882
|
}
|
|
816
883
|
}
|
|
817
884
|
}
|
|
818
|
-
|
|
885
|
+
|
|
886
|
+
// packages/cli/src/commands/fill.ts
|
|
887
|
+
function parseRef3(ref) {
|
|
888
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
889
|
+
}
|
|
890
|
+
async function fillCommand(ref, text, options = {}) {
|
|
891
|
+
if (!ref) {
|
|
892
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
893
|
+
}
|
|
894
|
+
if (text === void 0 || text === null) {
|
|
895
|
+
throw new Error("\u7F3A\u5C11 text \u53C2\u6570");
|
|
896
|
+
}
|
|
819
897
|
await ensureDaemonRunning();
|
|
898
|
+
const parsedRef = parseRef3(ref);
|
|
820
899
|
const request = {
|
|
821
900
|
id: generateId(),
|
|
822
|
-
action: "
|
|
901
|
+
action: "fill",
|
|
902
|
+
ref: parsedRef,
|
|
903
|
+
text,
|
|
823
904
|
tabId: options.tabId
|
|
824
905
|
};
|
|
825
906
|
const response = await sendCommand(request);
|
|
@@ -827,36 +908,54 @@ async function forwardCommand(options = {}) {
|
|
|
827
908
|
console.log(JSON.stringify(response, null, 2));
|
|
828
909
|
} else {
|
|
829
910
|
if (response.success) {
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
911
|
+
const role = response.data?.role ?? "element";
|
|
912
|
+
const name = response.data?.name;
|
|
913
|
+
if (name) {
|
|
914
|
+
console.log(`\u5DF2\u586B\u5145: ${role} "${name}"`);
|
|
833
915
|
} else {
|
|
834
|
-
console.log(
|
|
916
|
+
console.log(`\u5DF2\u586B\u5145: ${role}`);
|
|
835
917
|
}
|
|
918
|
+
console.log(`\u5185\u5BB9: "${text}"`);
|
|
836
919
|
} else {
|
|
837
920
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
838
921
|
process.exit(1);
|
|
839
922
|
}
|
|
840
923
|
}
|
|
841
924
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
925
|
+
|
|
926
|
+
// packages/cli/src/commands/type.ts
|
|
927
|
+
function parseRef4(ref) {
|
|
928
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
929
|
+
}
|
|
930
|
+
async function typeCommand(ref, text, options = {}) {
|
|
931
|
+
if (!ref) {
|
|
932
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
933
|
+
}
|
|
934
|
+
if (text === void 0 || text === null) {
|
|
935
|
+
throw new Error("\u7F3A\u5C11 text \u53C2\u6570");
|
|
936
|
+
}
|
|
937
|
+
await ensureDaemonRunning();
|
|
938
|
+
const parsedRef = parseRef4(ref);
|
|
939
|
+
const request = {
|
|
940
|
+
id: generateId(),
|
|
941
|
+
action: "type",
|
|
942
|
+
ref: parsedRef,
|
|
943
|
+
text,
|
|
944
|
+
tabId: options.tabId
|
|
848
945
|
};
|
|
849
946
|
const response = await sendCommand(request);
|
|
850
947
|
if (options.json) {
|
|
851
948
|
console.log(JSON.stringify(response, null, 2));
|
|
852
949
|
} else {
|
|
853
950
|
if (response.success) {
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
951
|
+
const role = response.data?.role ?? "element";
|
|
952
|
+
const name = response.data?.name;
|
|
953
|
+
if (name) {
|
|
954
|
+
console.log(`\u5DF2\u8F93\u5165: ${role} "${name}"`);
|
|
857
955
|
} else {
|
|
858
|
-
console.log(
|
|
956
|
+
console.log(`\u5DF2\u8F93\u5165: ${role}`);
|
|
859
957
|
}
|
|
958
|
+
console.log(`\u5185\u5BB9: "${text}"`);
|
|
860
959
|
} else {
|
|
861
960
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
862
961
|
process.exit(1);
|
|
@@ -864,20 +963,12 @@ async function refreshCommand(options = {}) {
|
|
|
864
963
|
}
|
|
865
964
|
}
|
|
866
965
|
|
|
867
|
-
// packages/cli/src/commands/
|
|
868
|
-
function
|
|
869
|
-
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
870
|
-
}
|
|
871
|
-
async function checkCommand(ref, options = {}) {
|
|
872
|
-
if (!ref) {
|
|
873
|
-
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
874
|
-
}
|
|
966
|
+
// packages/cli/src/commands/close.ts
|
|
967
|
+
async function closeCommand(options = {}) {
|
|
875
968
|
await ensureDaemonRunning();
|
|
876
|
-
const parsedRef = parseRef7(ref);
|
|
877
969
|
const request = {
|
|
878
970
|
id: generateId(),
|
|
879
|
-
action: "
|
|
880
|
-
ref: parsedRef,
|
|
971
|
+
action: "close",
|
|
881
972
|
tabId: options.tabId
|
|
882
973
|
};
|
|
883
974
|
const response = await sendCommand(request);
|
|
@@ -885,21 +976,11 @@ async function checkCommand(ref, options = {}) {
|
|
|
885
976
|
console.log(JSON.stringify(response, null, 2));
|
|
886
977
|
} else {
|
|
887
978
|
if (response.success) {
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
if (wasAlreadyChecked) {
|
|
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
|
-
}
|
|
979
|
+
const title = response.data?.title ?? "";
|
|
980
|
+
if (title) {
|
|
981
|
+
console.log(`\u5DF2\u5173\u95ED: "${title}"`);
|
|
897
982
|
} else {
|
|
898
|
-
|
|
899
|
-
console.log(`\u5DF2\u52FE\u9009: ${role} "${name}"`);
|
|
900
|
-
} else {
|
|
901
|
-
console.log(`\u5DF2\u52FE\u9009: ${role}`);
|
|
902
|
-
}
|
|
983
|
+
console.log("\u5DF2\u5173\u95ED\u5F53\u524D\u6807\u7B7E\u9875");
|
|
903
984
|
}
|
|
904
985
|
} else {
|
|
905
986
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -907,16 +988,21 @@ async function checkCommand(ref, options = {}) {
|
|
|
907
988
|
}
|
|
908
989
|
}
|
|
909
990
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
991
|
+
|
|
992
|
+
// packages/cli/src/commands/get.ts
|
|
993
|
+
function parseRef5(ref) {
|
|
994
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
995
|
+
}
|
|
996
|
+
async function getCommand(attribute, ref, options = {}) {
|
|
997
|
+
if (attribute === "text" && !ref) {
|
|
998
|
+
throw new Error("get text \u9700\u8981 ref \u53C2\u6570\uFF0C\u5982: get text @5");
|
|
913
999
|
}
|
|
914
1000
|
await ensureDaemonRunning();
|
|
915
|
-
const parsedRef = parseRef7(ref);
|
|
916
1001
|
const request = {
|
|
917
1002
|
id: generateId(),
|
|
918
|
-
action: "
|
|
919
|
-
|
|
1003
|
+
action: "get",
|
|
1004
|
+
attribute,
|
|
1005
|
+
ref: ref ? parseRef5(ref) : void 0,
|
|
920
1006
|
tabId: options.tabId
|
|
921
1007
|
};
|
|
922
1008
|
const response = await sendCommand(request);
|
|
@@ -924,22 +1010,8 @@ async function uncheckCommand(ref, options = {}) {
|
|
|
924
1010
|
console.log(JSON.stringify(response, null, 2));
|
|
925
1011
|
} else {
|
|
926
1012
|
if (response.success) {
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
const wasAlreadyUnchecked = response.data?.wasAlreadyUnchecked;
|
|
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
|
-
}
|
|
936
|
-
} else {
|
|
937
|
-
if (name) {
|
|
938
|
-
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role} "${name}"`);
|
|
939
|
-
} else {
|
|
940
|
-
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role}`);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
1013
|
+
const value = response.data?.value ?? "";
|
|
1014
|
+
console.log(value);
|
|
943
1015
|
} else {
|
|
944
1016
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
945
1017
|
process.exit(1);
|
|
@@ -947,44 +1019,96 @@ async function uncheckCommand(ref, options = {}) {
|
|
|
947
1019
|
}
|
|
948
1020
|
}
|
|
949
1021
|
|
|
950
|
-
// packages/cli/src/commands/
|
|
951
|
-
|
|
952
|
-
|
|
1022
|
+
// packages/cli/src/commands/screenshot.ts
|
|
1023
|
+
import fs from "fs";
|
|
1024
|
+
import path from "path";
|
|
1025
|
+
import os from "os";
|
|
1026
|
+
function getDefaultPath() {
|
|
1027
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1028
|
+
const filename = `bb-screenshot-${timestamp}.png`;
|
|
1029
|
+
return path.join(os.tmpdir(), filename);
|
|
953
1030
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
if (
|
|
959
|
-
|
|
1031
|
+
function saveBase64Image(dataUrl, filePath) {
|
|
1032
|
+
const base64Data = dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
1033
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
1034
|
+
const dir = path.dirname(filePath);
|
|
1035
|
+
if (!fs.existsSync(dir)) {
|
|
1036
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
960
1037
|
}
|
|
1038
|
+
fs.writeFileSync(filePath, buffer);
|
|
1039
|
+
}
|
|
1040
|
+
async function screenshotCommand(outputPath, options = {}) {
|
|
961
1041
|
await ensureDaemonRunning();
|
|
962
|
-
const
|
|
1042
|
+
const filePath = outputPath ? path.resolve(outputPath) : getDefaultPath();
|
|
963
1043
|
const request = {
|
|
964
1044
|
id: generateId(),
|
|
965
|
-
action: "
|
|
966
|
-
ref: parsedRef,
|
|
967
|
-
value,
|
|
1045
|
+
action: "screenshot",
|
|
968
1046
|
tabId: options.tabId
|
|
969
1047
|
};
|
|
970
1048
|
const response = await sendCommand(request);
|
|
1049
|
+
if (response.success && response.data?.dataUrl) {
|
|
1050
|
+
const dataUrl = response.data.dataUrl;
|
|
1051
|
+
saveBase64Image(dataUrl, filePath);
|
|
1052
|
+
if (options.json) {
|
|
1053
|
+
console.log(JSON.stringify({
|
|
1054
|
+
success: true,
|
|
1055
|
+
path: filePath,
|
|
1056
|
+
base64: dataUrl
|
|
1057
|
+
}, null, 2));
|
|
1058
|
+
} else {
|
|
1059
|
+
console.log(`\u622A\u56FE\u5DF2\u4FDD\u5B58: ${filePath}`);
|
|
1060
|
+
}
|
|
1061
|
+
} else {
|
|
1062
|
+
if (options.json) {
|
|
1063
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1064
|
+
} else {
|
|
1065
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1066
|
+
}
|
|
1067
|
+
process.exit(1);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// packages/cli/src/commands/wait.ts
|
|
1072
|
+
function isTimeWait(target) {
|
|
1073
|
+
return /^\d+$/.test(target);
|
|
1074
|
+
}
|
|
1075
|
+
function parseRef6(ref) {
|
|
1076
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
1077
|
+
}
|
|
1078
|
+
async function waitCommand(target, options = {}) {
|
|
1079
|
+
if (!target) {
|
|
1080
|
+
throw new Error("\u7F3A\u5C11\u7B49\u5F85\u76EE\u6807\u53C2\u6570");
|
|
1081
|
+
}
|
|
1082
|
+
await ensureDaemonRunning();
|
|
1083
|
+
let request;
|
|
1084
|
+
if (isTimeWait(target)) {
|
|
1085
|
+
const ms = parseInt(target, 10);
|
|
1086
|
+
request = {
|
|
1087
|
+
id: generateId(),
|
|
1088
|
+
action: "wait",
|
|
1089
|
+
waitType: "time",
|
|
1090
|
+
ms,
|
|
1091
|
+
tabId: options.tabId
|
|
1092
|
+
};
|
|
1093
|
+
} else {
|
|
1094
|
+
const ref = parseRef6(target);
|
|
1095
|
+
request = {
|
|
1096
|
+
id: generateId(),
|
|
1097
|
+
action: "wait",
|
|
1098
|
+
waitType: "element",
|
|
1099
|
+
ref,
|
|
1100
|
+
tabId: options.tabId
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
const response = await sendCommand(request);
|
|
971
1104
|
if (options.json) {
|
|
972
1105
|
console.log(JSON.stringify(response, null, 2));
|
|
973
1106
|
} else {
|
|
974
1107
|
if (response.success) {
|
|
975
|
-
|
|
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}")`);
|
|
1108
|
+
if (isTimeWait(target)) {
|
|
1109
|
+
console.log(`\u5DF2\u7B49\u5F85 ${target}ms`);
|
|
986
1110
|
} else {
|
|
987
|
-
console.log(`\
|
|
1111
|
+
console.log(`\u5143\u7D20 @${parseRef6(target)} \u5DF2\u51FA\u73B0`);
|
|
988
1112
|
}
|
|
989
1113
|
} else {
|
|
990
1114
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
@@ -993,16 +1117,35 @@ async function selectCommand(ref, value, options = {}) {
|
|
|
993
1117
|
}
|
|
994
1118
|
}
|
|
995
1119
|
|
|
996
|
-
// packages/cli/src/commands/
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1120
|
+
// packages/cli/src/commands/press.ts
|
|
1121
|
+
function parseKey(keyString) {
|
|
1122
|
+
const parts = keyString.split("+");
|
|
1123
|
+
const modifierNames = ["Control", "Alt", "Shift", "Meta"];
|
|
1124
|
+
const modifiers = [];
|
|
1125
|
+
let key = "";
|
|
1126
|
+
for (const part of parts) {
|
|
1127
|
+
if (modifierNames.includes(part)) {
|
|
1128
|
+
modifiers.push(part);
|
|
1129
|
+
} else {
|
|
1130
|
+
key = part;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return { key, modifiers };
|
|
1134
|
+
}
|
|
1135
|
+
async function pressCommand(keyString, options = {}) {
|
|
1136
|
+
if (!keyString) {
|
|
1137
|
+
throw new Error("\u7F3A\u5C11 key \u53C2\u6570");
|
|
1000
1138
|
}
|
|
1001
1139
|
await ensureDaemonRunning();
|
|
1140
|
+
const { key, modifiers } = parseKey(keyString);
|
|
1141
|
+
if (!key) {
|
|
1142
|
+
throw new Error("\u65E0\u6548\u7684\u6309\u952E\u683C\u5F0F");
|
|
1143
|
+
}
|
|
1002
1144
|
const request = {
|
|
1003
1145
|
id: generateId(),
|
|
1004
|
-
action: "
|
|
1005
|
-
|
|
1146
|
+
action: "press",
|
|
1147
|
+
key,
|
|
1148
|
+
modifiers,
|
|
1006
1149
|
tabId: options.tabId
|
|
1007
1150
|
};
|
|
1008
1151
|
const response = await sendCommand(request);
|
|
@@ -1010,16 +1153,8 @@ async function evalCommand(script, options = {}) {
|
|
|
1010
1153
|
console.log(JSON.stringify(response, null, 2));
|
|
1011
1154
|
} else {
|
|
1012
1155
|
if (response.success) {
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
if (typeof result === "object" && result !== null) {
|
|
1016
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1017
|
-
} else {
|
|
1018
|
-
console.log(result);
|
|
1019
|
-
}
|
|
1020
|
-
} else {
|
|
1021
|
-
console.log("undefined");
|
|
1022
|
-
}
|
|
1156
|
+
const displayKey = modifiers.length > 0 ? `${modifiers.join("+")}+${key}` : key;
|
|
1157
|
+
console.log(`\u5DF2\u6309\u4E0B: ${displayKey}`);
|
|
1023
1158
|
} else {
|
|
1024
1159
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1025
1160
|
process.exit(1);
|
|
@@ -1027,101 +1162,39 @@ async function evalCommand(script, options = {}) {
|
|
|
1027
1162
|
}
|
|
1028
1163
|
}
|
|
1029
1164
|
|
|
1030
|
-
// packages/cli/src/commands/
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
tabId = parseInt(rawArgv[idIdx + 1], 10);
|
|
1037
|
-
if (isNaN(tabId)) {
|
|
1038
|
-
throw new Error(`\u65E0\u6548\u7684 tabId: ${rawArgv[idIdx + 1]}`);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
if (args.length === 0) {
|
|
1043
|
-
return { action: "tab_list" };
|
|
1044
|
-
}
|
|
1045
|
-
const first = args[0];
|
|
1046
|
-
if (first === "new") {
|
|
1047
|
-
return { action: "tab_new", url: args[1] };
|
|
1165
|
+
// packages/cli/src/commands/scroll.ts
|
|
1166
|
+
var VALID_DIRECTIONS = ["up", "down", "left", "right"];
|
|
1167
|
+
var DEFAULT_PIXELS = 300;
|
|
1168
|
+
async function scrollCommand(direction, pixels, options = {}) {
|
|
1169
|
+
if (!direction) {
|
|
1170
|
+
throw new Error("\u7F3A\u5C11 direction \u53C2\u6570");
|
|
1048
1171
|
}
|
|
1049
|
-
if (
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
throw new Error("tab select \u9700\u8981 --id \u53C2\u6570\uFF0C\u7528\u6CD5\uFF1Abb-browser tab select --id <tabId>");
|
|
1172
|
+
if (!VALID_DIRECTIONS.includes(direction)) {
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
`\u65E0\u6548\u7684\u6EDA\u52A8\u65B9\u5411: ${direction}\uFF0C\u652F\u6301: ${VALID_DIRECTIONS.join(", ")}`
|
|
1175
|
+
);
|
|
1054
1176
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
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 };
|
|
1177
|
+
let pixelValue = DEFAULT_PIXELS;
|
|
1178
|
+
if (pixels !== void 0) {
|
|
1179
|
+
pixelValue = parseInt(pixels, 10);
|
|
1180
|
+
if (isNaN(pixelValue) || pixelValue <= 0) {
|
|
1181
|
+
throw new Error(`\u65E0\u6548\u7684\u50CF\u7D20\u503C: ${pixels}`);
|
|
1066
1182
|
}
|
|
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
1183
|
}
|
|
1083
|
-
return lines.join("\n");
|
|
1084
|
-
}
|
|
1085
|
-
async function tabCommand(args, options = {}) {
|
|
1086
1184
|
await ensureDaemonRunning();
|
|
1087
|
-
const parsed = parseTabSubcommand(args, process.argv);
|
|
1088
1185
|
const request = {
|
|
1089
1186
|
id: generateId(),
|
|
1090
|
-
action:
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
tabId:
|
|
1187
|
+
action: "scroll",
|
|
1188
|
+
direction,
|
|
1189
|
+
pixels: pixelValue,
|
|
1190
|
+
tabId: options.tabId
|
|
1094
1191
|
};
|
|
1095
1192
|
const response = await sendCommand(request);
|
|
1096
1193
|
if (options.json) {
|
|
1097
1194
|
console.log(JSON.stringify(response, null, 2));
|
|
1098
1195
|
} else {
|
|
1099
1196
|
if (response.success) {
|
|
1100
|
-
|
|
1101
|
-
case "tab_list": {
|
|
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
|
-
}
|
|
1197
|
+
console.log(`\u5DF2\u6EDA\u52A8: ${direction} ${pixelValue}px`);
|
|
1125
1198
|
} else {
|
|
1126
1199
|
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1127
1200
|
process.exit(1);
|
|
@@ -1129,846 +1202,1027 @@ async function tabCommand(args, options = {}) {
|
|
|
1129
1202
|
}
|
|
1130
1203
|
}
|
|
1131
1204
|
|
|
1132
|
-
// packages/cli/src/commands/
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1205
|
+
// packages/cli/src/commands/daemon.ts
|
|
1206
|
+
import { spawn as spawn2 } from "child_process";
|
|
1207
|
+
async function daemonCommand(options = {}) {
|
|
1208
|
+
if (await isDaemonRunning()) {
|
|
1209
|
+
if (options.json) {
|
|
1210
|
+
console.log(JSON.stringify({ success: false, error: "Daemon \u5DF2\u5728\u8FD0\u884C" }));
|
|
1211
|
+
} else {
|
|
1212
|
+
console.log("Daemon \u5DF2\u5728\u8FD0\u884C");
|
|
1213
|
+
}
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
const daemonPath = getDaemonPath();
|
|
1217
|
+
const args = [daemonPath];
|
|
1218
|
+
if (options.host) {
|
|
1219
|
+
args.push("--host", options.host);
|
|
1136
1220
|
}
|
|
1137
|
-
await ensureDaemonRunning();
|
|
1138
|
-
const request = {
|
|
1139
|
-
id: generateId(),
|
|
1140
|
-
action: "frame",
|
|
1141
|
-
selector,
|
|
1142
|
-
tabId: options.tabId
|
|
1143
|
-
};
|
|
1144
|
-
const response = await sendCommand(request);
|
|
1145
1221
|
if (options.json) {
|
|
1146
|
-
console.log(JSON.stringify(
|
|
1222
|
+
console.log(JSON.stringify({ success: true, message: "Daemon \u542F\u52A8\u4E2D..." }));
|
|
1147
1223
|
} else {
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1224
|
+
console.log("Daemon \u542F\u52A8\u4E2D...");
|
|
1225
|
+
}
|
|
1226
|
+
await new Promise((resolve2, reject) => {
|
|
1227
|
+
const child = spawn2(process.execPath, args, {
|
|
1228
|
+
stdio: "inherit"
|
|
1229
|
+
});
|
|
1230
|
+
child.on("exit", (code) => {
|
|
1231
|
+
if (code && code !== 0) {
|
|
1232
|
+
reject(new Error(`Daemon exited with code ${code}`));
|
|
1152
1233
|
} else {
|
|
1153
|
-
|
|
1234
|
+
resolve2();
|
|
1154
1235
|
}
|
|
1236
|
+
});
|
|
1237
|
+
child.on("error", reject);
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
async function stopCommand(options = {}) {
|
|
1241
|
+
if (!await isDaemonRunning()) {
|
|
1242
|
+
if (options.json) {
|
|
1243
|
+
console.log(JSON.stringify({ success: false, error: "Daemon \u672A\u8FD0\u884C" }));
|
|
1155
1244
|
} else {
|
|
1156
|
-
console.
|
|
1157
|
-
process.exit(1);
|
|
1245
|
+
console.log("Daemon \u672A\u8FD0\u884C");
|
|
1158
1246
|
}
|
|
1247
|
+
return;
|
|
1159
1248
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
};
|
|
1168
|
-
const response = await sendCommand(request);
|
|
1169
|
-
if (options.json) {
|
|
1170
|
-
console.log(JSON.stringify(response, null, 2));
|
|
1249
|
+
const stopped = await stopDaemon();
|
|
1250
|
+
if (stopped) {
|
|
1251
|
+
if (options.json) {
|
|
1252
|
+
console.log(JSON.stringify({ success: true, message: "Daemon \u5DF2\u505C\u6B62" }));
|
|
1253
|
+
} else {
|
|
1254
|
+
console.log("Daemon \u5DF2\u505C\u6B62");
|
|
1255
|
+
}
|
|
1171
1256
|
} else {
|
|
1172
|
-
if (
|
|
1173
|
-
console.log("\
|
|
1257
|
+
if (options.json) {
|
|
1258
|
+
console.log(JSON.stringify({ success: false, error: "\u65E0\u6CD5\u505C\u6B62 Daemon" }));
|
|
1174
1259
|
} else {
|
|
1175
|
-
console.error(
|
|
1176
|
-
process.exit(1);
|
|
1260
|
+
console.error("\u65E0\u6CD5\u505C\u6B62 Daemon");
|
|
1177
1261
|
}
|
|
1262
|
+
process.exit(1);
|
|
1178
1263
|
}
|
|
1179
1264
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
async function dialogCommand(subCommand, promptText, options = {}) {
|
|
1183
|
-
if (!subCommand || !["accept", "dismiss"].includes(subCommand)) {
|
|
1184
|
-
throw new Error("\u8BF7\u4F7F\u7528 'dialog accept [text]' \u6216 'dialog dismiss'");
|
|
1185
|
-
}
|
|
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);
|
|
1265
|
+
async function statusCommand(options = {}) {
|
|
1266
|
+
const running = await isDaemonRunning();
|
|
1195
1267
|
if (options.json) {
|
|
1196
|
-
console.log(JSON.stringify(
|
|
1268
|
+
console.log(JSON.stringify({ running }));
|
|
1197
1269
|
} else {
|
|
1198
|
-
|
|
1199
|
-
const dialogInfo = response.data?.dialogInfo;
|
|
1200
|
-
if (dialogInfo) {
|
|
1201
|
-
const action = subCommand === "accept" ? "\u5DF2\u63A5\u53D7" : "\u5DF2\u62D2\u7EDD";
|
|
1202
|
-
console.log(`${action}\u5BF9\u8BDD\u6846\uFF08${dialogInfo.type}\uFF09: "${dialogInfo.message}"`);
|
|
1203
|
-
} else {
|
|
1204
|
-
console.log("\u5BF9\u8BDD\u6846\u5DF2\u5904\u7406");
|
|
1205
|
-
}
|
|
1206
|
-
} else {
|
|
1207
|
-
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1208
|
-
process.exit(1);
|
|
1209
|
-
}
|
|
1270
|
+
console.log(running ? "Daemon \u8FD0\u884C\u4E2D" : "Daemon \u672A\u8FD0\u884C");
|
|
1210
1271
|
}
|
|
1211
1272
|
}
|
|
1212
1273
|
|
|
1213
|
-
// packages/cli/src/commands/
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
abort: options.abort,
|
|
1223
|
-
body: options.body
|
|
1224
|
-
} : void 0,
|
|
1225
|
-
withBody: subCommand === "requests" ? options.withBody : void 0,
|
|
1226
|
-
tabId: options.tabId
|
|
1227
|
-
});
|
|
1228
|
-
if (options.json) {
|
|
1229
|
-
console.log(JSON.stringify(response));
|
|
1230
|
-
return;
|
|
1231
|
-
}
|
|
1232
|
-
if (!response.success) {
|
|
1233
|
-
throw new Error(response.error || "Network command failed");
|
|
1234
|
-
}
|
|
1235
|
-
const data = response.data;
|
|
1236
|
-
switch (subCommand) {
|
|
1237
|
-
case "requests": {
|
|
1238
|
-
const requests = data?.networkRequests || [];
|
|
1239
|
-
if (requests.length === 0) {
|
|
1240
|
-
console.log("\u6CA1\u6709\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55");
|
|
1241
|
-
console.log("\u63D0\u793A: \u4F7F\u7528 network requests \u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1242
|
-
} else {
|
|
1243
|
-
console.log(`\u7F51\u7EDC\u8BF7\u6C42 (${requests.length} \u6761):
|
|
1244
|
-
`);
|
|
1245
|
-
for (const req of requests) {
|
|
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;
|
|
1274
|
+
// packages/cli/src/commands/reload.ts
|
|
1275
|
+
import WebSocket from "ws";
|
|
1276
|
+
var EXTENSION_NAME = "bb-browser";
|
|
1277
|
+
async function reloadCommand(options = {}) {
|
|
1278
|
+
const port = options.port || 9222;
|
|
1279
|
+
try {
|
|
1280
|
+
const listRes = await fetch(`http://127.0.0.1:${port}/json/list`);
|
|
1281
|
+
if (!listRes.ok) {
|
|
1282
|
+
throw new Error(`CDP \u672A\u542F\u7528\u3002\u8BF7\u7528 --remote-debugging-port=${port} \u542F\u52A8 Chrome`);
|
|
1269
1283
|
}
|
|
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;
|
|
1284
|
+
const list = await listRes.json();
|
|
1285
|
+
const extPage = list.find(
|
|
1286
|
+
(t) => t.type === "page" && t.url.includes("chrome://extensions")
|
|
1287
|
+
);
|
|
1288
|
+
if (!extPage) {
|
|
1289
|
+
throw new Error("\u8BF7\u5148\u6253\u5F00 chrome://extensions \u9875\u9762");
|
|
1281
1290
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1291
|
+
const result = await new Promise((resolve2, reject) => {
|
|
1292
|
+
const ws = new WebSocket(extPage.webSocketDebuggerUrl);
|
|
1293
|
+
let resolved = false;
|
|
1294
|
+
const timeout = setTimeout(() => {
|
|
1295
|
+
if (!resolved) {
|
|
1296
|
+
resolved = true;
|
|
1297
|
+
ws.close();
|
|
1298
|
+
reject(new Error("CDP \u8FDE\u63A5\u8D85\u65F6"));
|
|
1299
|
+
}
|
|
1300
|
+
}, 1e4);
|
|
1301
|
+
ws.on("open", () => {
|
|
1302
|
+
const script = `
|
|
1303
|
+
(async function() {
|
|
1304
|
+
if (!chrome || !chrome.developerPrivate) {
|
|
1305
|
+
return { error: 'developerPrivate API not available' };
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
try {
|
|
1309
|
+
const exts = await chrome.developerPrivate.getExtensionsInfo();
|
|
1310
|
+
const bbExt = exts.find(e => e.name === '${EXTENSION_NAME}');
|
|
1311
|
+
|
|
1312
|
+
if (!bbExt) {
|
|
1313
|
+
return { error: '${EXTENSION_NAME} \u6269\u5C55\u672A\u5B89\u88C5' };
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
if (bbExt.state !== 'ENABLED') {
|
|
1317
|
+
return { error: '${EXTENSION_NAME} \u6269\u5C55\u5DF2\u7981\u7528' };
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
await chrome.developerPrivate.reload(bbExt.id, {failQuietly: true});
|
|
1321
|
+
return { success: true, extensionId: bbExt.id };
|
|
1322
|
+
} catch (e) {
|
|
1323
|
+
return { error: e.message };
|
|
1324
|
+
}
|
|
1325
|
+
})()
|
|
1326
|
+
`;
|
|
1327
|
+
ws.send(JSON.stringify({
|
|
1328
|
+
id: 1,
|
|
1329
|
+
method: "Runtime.evaluate",
|
|
1330
|
+
params: {
|
|
1331
|
+
expression: script,
|
|
1332
|
+
awaitPromise: true,
|
|
1333
|
+
returnByValue: true
|
|
1334
|
+
}
|
|
1335
|
+
}));
|
|
1336
|
+
});
|
|
1337
|
+
ws.on("message", (data) => {
|
|
1338
|
+
const msg = JSON.parse(data.toString());
|
|
1339
|
+
if (msg.id === 1) {
|
|
1340
|
+
clearTimeout(timeout);
|
|
1341
|
+
resolved = true;
|
|
1342
|
+
ws.close();
|
|
1343
|
+
const value = msg.result?.result?.value;
|
|
1344
|
+
if (value?.success) {
|
|
1345
|
+
resolve2({
|
|
1346
|
+
success: true,
|
|
1347
|
+
message: "\u6269\u5C55\u5DF2\u91CD\u8F7D",
|
|
1348
|
+
extensionId: value.extensionId
|
|
1349
|
+
});
|
|
1350
|
+
} else if (value?.error) {
|
|
1351
|
+
reject(new Error(value.error));
|
|
1352
|
+
} else {
|
|
1353
|
+
reject(new Error(`\u91CD\u8F7D\u5931\u8D25: ${JSON.stringify(value)}`));
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
ws.on("error", (err) => {
|
|
1358
|
+
clearTimeout(timeout);
|
|
1359
|
+
if (!resolved) {
|
|
1360
|
+
resolved = true;
|
|
1361
|
+
reject(new Error(`CDP \u8FDE\u63A5\u5931\u8D25: ${err.message}`));
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
});
|
|
1365
|
+
if (options.json) {
|
|
1366
|
+
console.log(JSON.stringify(result));
|
|
1367
|
+
} else {
|
|
1368
|
+
console.log(`${result.message} (${result.extensionId})`);
|
|
1290
1369
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1370
|
+
} catch (error) {
|
|
1371
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1372
|
+
if (options.json) {
|
|
1373
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
1374
|
+
} else {
|
|
1375
|
+
console.error(`\u9519\u8BEF: ${message}`);
|
|
1294
1376
|
}
|
|
1295
|
-
|
|
1296
|
-
throw new Error(`\u672A\u77E5\u7684 network \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1377
|
+
process.exit(1);
|
|
1297
1378
|
}
|
|
1298
1379
|
}
|
|
1299
1380
|
|
|
1300
|
-
// packages/cli/src/commands/
|
|
1301
|
-
async function
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1381
|
+
// packages/cli/src/commands/nav.ts
|
|
1382
|
+
async function backCommand(options = {}) {
|
|
1383
|
+
await ensureDaemonRunning();
|
|
1384
|
+
const request = {
|
|
1385
|
+
id: generateId(),
|
|
1386
|
+
action: "back",
|
|
1306
1387
|
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
1388
|
};
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1389
|
+
const response = await sendCommand(request);
|
|
1390
|
+
if (options.json) {
|
|
1391
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1392
|
+
} else {
|
|
1393
|
+
if (response.success) {
|
|
1394
|
+
const url = response.data?.url ?? "";
|
|
1395
|
+
if (url) {
|
|
1396
|
+
console.log(`\u540E\u9000\u81F3: ${url}`);
|
|
1397
|
+
} else {
|
|
1398
|
+
console.log("\u5DF2\u540E\u9000");
|
|
1399
|
+
}
|
|
1339
1400
|
} else {
|
|
1340
|
-
console.
|
|
1401
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1402
|
+
process.exit(1);
|
|
1341
1403
|
}
|
|
1342
1404
|
}
|
|
1343
1405
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
action: "errors",
|
|
1350
|
-
errorsCommand: options.clear ? "clear" : "get",
|
|
1406
|
+
async function forwardCommand(options = {}) {
|
|
1407
|
+
await ensureDaemonRunning();
|
|
1408
|
+
const request = {
|
|
1409
|
+
id: generateId(),
|
|
1410
|
+
action: "forward",
|
|
1351
1411
|
tabId: options.tabId
|
|
1352
|
-
}
|
|
1412
|
+
};
|
|
1413
|
+
const response = await sendCommand(request);
|
|
1353
1414
|
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"));
|
|
1415
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1416
|
+
} else {
|
|
1417
|
+
if (response.success) {
|
|
1418
|
+
const url = response.data?.url ?? "";
|
|
1419
|
+
if (url) {
|
|
1420
|
+
console.log(`\u524D\u8FDB\u81F3: ${url}`);
|
|
1421
|
+
} else {
|
|
1422
|
+
console.log("\u5DF2\u524D\u8FDB");
|
|
1423
|
+
}
|
|
1424
|
+
} else {
|
|
1425
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1426
|
+
process.exit(1);
|
|
1380
1427
|
}
|
|
1381
|
-
console.log("");
|
|
1382
1428
|
}
|
|
1383
1429
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
action: "trace",
|
|
1390
|
-
traceCommand: subCommand,
|
|
1430
|
+
async function refreshCommand(options = {}) {
|
|
1431
|
+
await ensureDaemonRunning();
|
|
1432
|
+
const request = {
|
|
1433
|
+
id: generateId(),
|
|
1434
|
+
action: "refresh",
|
|
1391
1435
|
tabId: options.tabId
|
|
1392
|
-
}
|
|
1436
|
+
};
|
|
1437
|
+
const response = await sendCommand(request);
|
|
1393
1438
|
if (options.json) {
|
|
1394
|
-
console.log(JSON.stringify(response));
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
case "start": {
|
|
1403
|
-
const status = data?.traceStatus;
|
|
1404
|
-
console.log("\u5F00\u59CB\u5F55\u5236\u7528\u6237\u64CD\u4F5C");
|
|
1405
|
-
console.log(`\u6807\u7B7E\u9875 ID: ${status?.tabId || "N/A"}`);
|
|
1406
|
-
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");
|
|
1407
|
-
break;
|
|
1408
|
-
}
|
|
1409
|
-
case "stop": {
|
|
1410
|
-
const events = data?.traceEvents || [];
|
|
1411
|
-
const status = data?.traceStatus;
|
|
1412
|
-
console.log(`\u5F55\u5236\u5B8C\u6210\uFF0C\u5171 ${events.length} \u4E2A\u4E8B\u4EF6
|
|
1413
|
-
`);
|
|
1414
|
-
if (events.length === 0) {
|
|
1415
|
-
console.log("\u6CA1\u6709\u5F55\u5236\u5230\u4EFB\u4F55\u64CD\u4F5C");
|
|
1416
|
-
break;
|
|
1439
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1440
|
+
} else {
|
|
1441
|
+
if (response.success) {
|
|
1442
|
+
const title = response.data?.title ?? "";
|
|
1443
|
+
if (title) {
|
|
1444
|
+
console.log(`\u5DF2\u5237\u65B0: "${title}"`);
|
|
1445
|
+
} else {
|
|
1446
|
+
console.log("\u5DF2\u5237\u65B0\u9875\u9762");
|
|
1417
1447
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1448
|
+
} else {
|
|
1449
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1450
|
+
process.exit(1);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// packages/cli/src/commands/check.ts
|
|
1456
|
+
function parseRef7(ref) {
|
|
1457
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
1458
|
+
}
|
|
1459
|
+
async function checkCommand(ref, options = {}) {
|
|
1460
|
+
if (!ref) {
|
|
1461
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1462
|
+
}
|
|
1463
|
+
await ensureDaemonRunning();
|
|
1464
|
+
const parsedRef = parseRef7(ref);
|
|
1465
|
+
const request = {
|
|
1466
|
+
id: generateId(),
|
|
1467
|
+
action: "check",
|
|
1468
|
+
ref: parsedRef,
|
|
1469
|
+
tabId: options.tabId
|
|
1470
|
+
};
|
|
1471
|
+
const response = await sendCommand(request);
|
|
1472
|
+
if (options.json) {
|
|
1473
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1474
|
+
} else {
|
|
1475
|
+
if (response.success) {
|
|
1476
|
+
const role = response.data?.role ?? "checkbox";
|
|
1477
|
+
const name = response.data?.name;
|
|
1478
|
+
const wasAlreadyChecked = response.data?.wasAlreadyChecked;
|
|
1479
|
+
if (wasAlreadyChecked) {
|
|
1480
|
+
if (name) {
|
|
1481
|
+
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
1482
|
+
} else {
|
|
1483
|
+
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role}`);
|
|
1484
|
+
}
|
|
1485
|
+
} else {
|
|
1486
|
+
if (name) {
|
|
1487
|
+
console.log(`\u5DF2\u52FE\u9009: ${role} "${name}"`);
|
|
1488
|
+
} else {
|
|
1489
|
+
console.log(`\u5DF2\u52FE\u9009: ${role}`);
|
|
1445
1490
|
}
|
|
1446
1491
|
}
|
|
1447
|
-
|
|
1448
|
-
\
|
|
1449
|
-
|
|
1492
|
+
} else {
|
|
1493
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1494
|
+
process.exit(1);
|
|
1450
1495
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
async function uncheckCommand(ref, options = {}) {
|
|
1499
|
+
if (!ref) {
|
|
1500
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1501
|
+
}
|
|
1502
|
+
await ensureDaemonRunning();
|
|
1503
|
+
const parsedRef = parseRef7(ref);
|
|
1504
|
+
const request = {
|
|
1505
|
+
id: generateId(),
|
|
1506
|
+
action: "uncheck",
|
|
1507
|
+
ref: parsedRef,
|
|
1508
|
+
tabId: options.tabId
|
|
1509
|
+
};
|
|
1510
|
+
const response = await sendCommand(request);
|
|
1511
|
+
if (options.json) {
|
|
1512
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1513
|
+
} else {
|
|
1514
|
+
if (response.success) {
|
|
1515
|
+
const role = response.data?.role ?? "checkbox";
|
|
1516
|
+
const name = response.data?.name;
|
|
1517
|
+
const wasAlreadyUnchecked = response.data?.wasAlreadyUnchecked;
|
|
1518
|
+
if (wasAlreadyUnchecked) {
|
|
1519
|
+
if (name) {
|
|
1520
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
1521
|
+
} else {
|
|
1522
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role}`);
|
|
1523
|
+
}
|
|
1456
1524
|
} else {
|
|
1457
|
-
|
|
1525
|
+
if (name) {
|
|
1526
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role} "${name}"`);
|
|
1527
|
+
} else {
|
|
1528
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role}`);
|
|
1529
|
+
}
|
|
1458
1530
|
}
|
|
1459
|
-
|
|
1531
|
+
} else {
|
|
1532
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1533
|
+
process.exit(1);
|
|
1460
1534
|
}
|
|
1461
|
-
default:
|
|
1462
|
-
throw new Error(`\u672A\u77E5\u7684 trace \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1463
1535
|
}
|
|
1464
1536
|
}
|
|
1465
1537
|
|
|
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
|
-
}
|
|
1538
|
+
// packages/cli/src/commands/select.ts
|
|
1539
|
+
function parseRef8(ref) {
|
|
1540
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
1474
1541
|
}
|
|
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}`);
|
|
1542
|
+
async function selectCommand(ref, value, options = {}) {
|
|
1543
|
+
if (!ref) {
|
|
1544
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1489
1545
|
}
|
|
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
|
-
}
|
|
1546
|
+
if (value === void 0 || value === null) {
|
|
1547
|
+
throw new Error("\u7F3A\u5C11 value \u53C2\u6570");
|
|
1504
1548
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1549
|
+
await ensureDaemonRunning();
|
|
1550
|
+
const parsedRef = parseRef8(ref);
|
|
1551
|
+
const request = {
|
|
1552
|
+
id: generateId(),
|
|
1553
|
+
action: "select",
|
|
1554
|
+
ref: parsedRef,
|
|
1555
|
+
value,
|
|
1556
|
+
tabId: options.tabId
|
|
1557
|
+
};
|
|
1558
|
+
const response = await sendCommand(request);
|
|
1559
|
+
if (options.json) {
|
|
1560
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1561
|
+
} else {
|
|
1562
|
+
if (response.success) {
|
|
1563
|
+
const role = response.data?.role ?? "combobox";
|
|
1564
|
+
const name = response.data?.name;
|
|
1565
|
+
const selectedValue = response.data?.selectedValue;
|
|
1566
|
+
const selectedLabel = response.data?.selectedLabel;
|
|
1567
|
+
if (name) {
|
|
1568
|
+
console.log(`\u5DF2\u9009\u62E9: ${role} "${name}"`);
|
|
1517
1569
|
} else {
|
|
1518
|
-
|
|
1570
|
+
console.log(`\u5DF2\u9009\u62E9: ${role}`);
|
|
1519
1571
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1572
|
+
if (selectedLabel && selectedLabel !== selectedValue) {
|
|
1573
|
+
console.log(`\u9009\u9879: "${selectedLabel}" (value="${selectedValue}")`);
|
|
1574
|
+
} else {
|
|
1575
|
+
console.log(`\u9009\u9879: "${selectedValue}"`);
|
|
1576
|
+
}
|
|
1577
|
+
} else {
|
|
1578
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1579
|
+
process.exit(1);
|
|
1527
1580
|
}
|
|
1528
|
-
}
|
|
1581
|
+
}
|
|
1529
1582
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
);
|
|
1583
|
+
|
|
1584
|
+
// packages/cli/src/commands/eval.ts
|
|
1585
|
+
async function evalCommand(script, options = {}) {
|
|
1586
|
+
if (!script) {
|
|
1587
|
+
throw new Error("\u7F3A\u5C11 script \u53C2\u6570");
|
|
1535
1588
|
}
|
|
1536
1589
|
await ensureDaemonRunning();
|
|
1537
|
-
const
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1590
|
+
const request = {
|
|
1591
|
+
id: generateId(),
|
|
1592
|
+
action: "eval",
|
|
1593
|
+
script,
|
|
1594
|
+
tabId: options.tabId
|
|
1595
|
+
};
|
|
1596
|
+
const response = await sendCommand(request);
|
|
1597
|
+
if (options.json) {
|
|
1598
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1599
|
+
} else {
|
|
1600
|
+
if (response.success) {
|
|
1601
|
+
const result = response.data?.result;
|
|
1602
|
+
if (result !== void 0) {
|
|
1603
|
+
if (typeof result === "object" && result !== null) {
|
|
1604
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1605
|
+
} else {
|
|
1606
|
+
console.log(result);
|
|
1607
|
+
}
|
|
1608
|
+
} else {
|
|
1609
|
+
console.log("undefined");
|
|
1610
|
+
}
|
|
1611
|
+
} else {
|
|
1612
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1613
|
+
process.exit(1);
|
|
1548
1614
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// packages/cli/src/commands/tab.ts
|
|
1619
|
+
function parseTabSubcommand(args, rawArgv) {
|
|
1620
|
+
let tabId;
|
|
1621
|
+
if (rawArgv) {
|
|
1622
|
+
const idIdx = rawArgv.indexOf("--id");
|
|
1623
|
+
if (idIdx >= 0 && rawArgv[idIdx + 1]) {
|
|
1624
|
+
tabId = parseInt(rawArgv[idIdx + 1], 10);
|
|
1625
|
+
if (isNaN(tabId)) {
|
|
1626
|
+
throw new Error(`\u65E0\u6548\u7684 tabId: ${rawArgv[idIdx + 1]}`);
|
|
1627
|
+
}
|
|
1551
1628
|
}
|
|
1552
1629
|
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
const evalResp = await sendCommand(evalReq);
|
|
1556
|
-
if (!evalResp.success) {
|
|
1557
|
-
throw new Error(`Fetch \u5931\u8D25: ${evalResp.error}`);
|
|
1630
|
+
if (args.length === 0) {
|
|
1631
|
+
return { action: "tab_list" };
|
|
1558
1632
|
}
|
|
1559
|
-
const
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1633
|
+
const first = args[0];
|
|
1634
|
+
if (first === "new") {
|
|
1635
|
+
return { action: "tab_new", url: args[1] };
|
|
1562
1636
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
return;
|
|
1637
|
+
if (first === "select") {
|
|
1638
|
+
if (tabId !== void 0) {
|
|
1639
|
+
return { action: "tab_select", tabId };
|
|
1640
|
+
}
|
|
1641
|
+
throw new Error("tab select \u9700\u8981 --id \u53C2\u6570\uFF0C\u7528\u6CD5\uFF1Abb-browser tab select --id <tabId>");
|
|
1569
1642
|
}
|
|
1570
|
-
if (
|
|
1571
|
-
|
|
1643
|
+
if (first === "close") {
|
|
1644
|
+
if (tabId !== void 0) {
|
|
1645
|
+
return { action: "tab_close", tabId };
|
|
1646
|
+
}
|
|
1647
|
+
const indexArg = args[1];
|
|
1648
|
+
if (indexArg !== void 0) {
|
|
1649
|
+
const index2 = parseInt(indexArg, 10);
|
|
1650
|
+
if (isNaN(index2) || index2 < 0) {
|
|
1651
|
+
throw new Error(`\u65E0\u6548\u7684\u6807\u7B7E\u9875\u7D22\u5F15: ${indexArg}`);
|
|
1652
|
+
}
|
|
1653
|
+
return { action: "tab_close", index: index2 };
|
|
1654
|
+
}
|
|
1655
|
+
return { action: "tab_close" };
|
|
1572
1656
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
writeFileSync(options.output, content, "utf-8");
|
|
1577
|
-
console.log(`\u5DF2\u5199\u5165 ${options.output} (${result.status}, ${content.length} bytes)`);
|
|
1578
|
-
return;
|
|
1657
|
+
const index = parseInt(first, 10);
|
|
1658
|
+
if (!isNaN(index) && index >= 0) {
|
|
1659
|
+
return { action: "tab_select", index };
|
|
1579
1660
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1661
|
+
throw new Error(`\u672A\u77E5\u7684 tab \u5B50\u547D\u4EE4: ${first}`);
|
|
1662
|
+
}
|
|
1663
|
+
function formatTabList(tabs, activeIndex) {
|
|
1664
|
+
const lines = [];
|
|
1665
|
+
lines.push(`\u6807\u7B7E\u9875\u5217\u8868\uFF08\u5171 ${tabs.length} \u4E2A\uFF0C\u5F53\u524D #${activeIndex}\uFF09\uFF1A`);
|
|
1666
|
+
for (const tab of tabs) {
|
|
1667
|
+
const prefix = tab.active ? "*" : " ";
|
|
1668
|
+
const title = tab.title || "(\u65E0\u6807\u9898)";
|
|
1669
|
+
lines.push(`${prefix} [${tab.index}] ${tab.url} - ${title}`);
|
|
1670
|
+
}
|
|
1671
|
+
return lines.join("\n");
|
|
1672
|
+
}
|
|
1673
|
+
async function tabCommand(args, options = {}) {
|
|
1674
|
+
await ensureDaemonRunning();
|
|
1675
|
+
const parsed = parseTabSubcommand(args, process.argv);
|
|
1676
|
+
const request = {
|
|
1677
|
+
id: generateId(),
|
|
1678
|
+
action: parsed.action,
|
|
1679
|
+
url: parsed.url,
|
|
1680
|
+
index: parsed.index,
|
|
1681
|
+
tabId: parsed.tabId
|
|
1682
|
+
};
|
|
1683
|
+
const response = await sendCommand(request);
|
|
1684
|
+
if (options.json) {
|
|
1685
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1582
1686
|
} else {
|
|
1583
|
-
|
|
1687
|
+
if (response.success) {
|
|
1688
|
+
switch (parsed.action) {
|
|
1689
|
+
case "tab_list": {
|
|
1690
|
+
const tabs = response.data?.tabs ?? [];
|
|
1691
|
+
const activeIndex = response.data?.activeIndex ?? 0;
|
|
1692
|
+
console.log(formatTabList(tabs, activeIndex));
|
|
1693
|
+
break;
|
|
1694
|
+
}
|
|
1695
|
+
case "tab_new": {
|
|
1696
|
+
const url = response.data?.url ?? "about:blank";
|
|
1697
|
+
console.log(`\u5DF2\u521B\u5EFA\u65B0\u6807\u7B7E\u9875: ${url}`);
|
|
1698
|
+
break;
|
|
1699
|
+
}
|
|
1700
|
+
case "tab_select": {
|
|
1701
|
+
const title = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1702
|
+
const url = response.data?.url ?? "";
|
|
1703
|
+
console.log(`\u5DF2\u5207\u6362\u5230\u6807\u7B7E\u9875 #${parsed.index}: ${title}`);
|
|
1704
|
+
console.log(` URL: ${url}`);
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1707
|
+
case "tab_close": {
|
|
1708
|
+
const closedTitle = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1709
|
+
console.log(`\u5DF2\u5173\u95ED\u6807\u7B7E\u9875: ${closedTitle}`);
|
|
1710
|
+
break;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
} else {
|
|
1714
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1715
|
+
process.exit(1);
|
|
1716
|
+
}
|
|
1584
1717
|
}
|
|
1585
1718
|
}
|
|
1586
1719
|
|
|
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;
|
|
1720
|
+
// packages/cli/src/commands/frame.ts
|
|
1721
|
+
async function frameCommand(selector, options = {}) {
|
|
1722
|
+
if (!selector) {
|
|
1723
|
+
throw new Error("\u7F3A\u5C11 selector \u53C2\u6570");
|
|
1602
1724
|
}
|
|
1603
|
-
|
|
1604
|
-
const
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1725
|
+
await ensureDaemonRunning();
|
|
1726
|
+
const request = {
|
|
1727
|
+
id: generateId(),
|
|
1728
|
+
action: "frame",
|
|
1729
|
+
selector,
|
|
1730
|
+
tabId: options.tabId
|
|
1731
|
+
};
|
|
1732
|
+
const response = await sendCommand(request);
|
|
1733
|
+
if (options.json) {
|
|
1734
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1735
|
+
} else {
|
|
1736
|
+
if (response.success) {
|
|
1737
|
+
const frameInfo = response.data?.frameInfo;
|
|
1738
|
+
if (frameInfo?.url) {
|
|
1739
|
+
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector} (${frameInfo.url})`);
|
|
1740
|
+
} else {
|
|
1741
|
+
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector}`);
|
|
1742
|
+
}
|
|
1743
|
+
} else {
|
|
1744
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1745
|
+
process.exit(1);
|
|
1622
1746
|
}
|
|
1623
1747
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1748
|
+
}
|
|
1749
|
+
async function frameMainCommand(options = {}) {
|
|
1750
|
+
await ensureDaemonRunning();
|
|
1751
|
+
const request = {
|
|
1752
|
+
id: generateId(),
|
|
1753
|
+
action: "frame_main",
|
|
1754
|
+
tabId: options.tabId
|
|
1631
1755
|
};
|
|
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;
|
|
1756
|
+
const response = await sendCommand(request);
|
|
1757
|
+
if (options.json) {
|
|
1758
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1759
|
+
} else {
|
|
1760
|
+
if (response.success) {
|
|
1761
|
+
console.log("\u5DF2\u8FD4\u56DE\u4E3B frame");
|
|
1762
|
+
} else {
|
|
1763
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1764
|
+
process.exit(1);
|
|
1654
1765
|
}
|
|
1655
1766
|
}
|
|
1656
|
-
return meta;
|
|
1657
1767
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1768
|
+
|
|
1769
|
+
// packages/cli/src/commands/dialog.ts
|
|
1770
|
+
async function dialogCommand(subCommand, promptText, options = {}) {
|
|
1771
|
+
if (!subCommand || !["accept", "dismiss"].includes(subCommand)) {
|
|
1772
|
+
throw new Error("\u8BF7\u4F7F\u7528 'dialog accept [text]' \u6216 'dialog dismiss'");
|
|
1773
|
+
}
|
|
1774
|
+
await ensureDaemonRunning();
|
|
1775
|
+
const request = {
|
|
1776
|
+
id: generateId(),
|
|
1777
|
+
action: "dialog",
|
|
1778
|
+
dialogResponse: subCommand,
|
|
1779
|
+
promptText: subCommand === "accept" ? promptText : void 0,
|
|
1780
|
+
tabId: options.tabId
|
|
1781
|
+
};
|
|
1782
|
+
const response = await sendCommand(request);
|
|
1783
|
+
if (options.json) {
|
|
1784
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1785
|
+
} else {
|
|
1786
|
+
if (response.success) {
|
|
1787
|
+
const dialogInfo = response.data?.dialogInfo;
|
|
1788
|
+
if (dialogInfo) {
|
|
1789
|
+
const action = subCommand === "accept" ? "\u5DF2\u63A5\u53D7" : "\u5DF2\u62D2\u7EDD";
|
|
1790
|
+
console.log(`${action}\u5BF9\u8BDD\u6846\uFF08${dialogInfo.type}\uFF09: "${dialogInfo.message}"`);
|
|
1791
|
+
} else {
|
|
1792
|
+
console.log("\u5BF9\u8BDD\u6846\u5DF2\u5904\u7406");
|
|
1675
1793
|
}
|
|
1794
|
+
} else {
|
|
1795
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1796
|
+
process.exit(1);
|
|
1676
1797
|
}
|
|
1677
1798
|
}
|
|
1678
|
-
walk(dir);
|
|
1679
|
-
return sites;
|
|
1680
1799
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
const
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1800
|
+
|
|
1801
|
+
// packages/cli/src/commands/network.ts
|
|
1802
|
+
async function networkCommand(subCommand, urlOrFilter, options = {}) {
|
|
1803
|
+
const response = await sendCommand({
|
|
1804
|
+
id: crypto.randomUUID(),
|
|
1805
|
+
action: "network",
|
|
1806
|
+
networkCommand: subCommand,
|
|
1807
|
+
url: subCommand === "route" || subCommand === "unroute" ? urlOrFilter : void 0,
|
|
1808
|
+
filter: subCommand === "requests" ? urlOrFilter : void 0,
|
|
1809
|
+
routeOptions: subCommand === "route" ? {
|
|
1810
|
+
abort: options.abort,
|
|
1811
|
+
body: options.body
|
|
1812
|
+
} : void 0,
|
|
1813
|
+
withBody: subCommand === "requests" ? options.withBody : void 0,
|
|
1814
|
+
tabId: options.tabId
|
|
1815
|
+
});
|
|
1816
|
+
if (options.json) {
|
|
1817
|
+
console.log(JSON.stringify(response));
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
if (!response.success) {
|
|
1821
|
+
throw new Error(response.error || "Network command failed");
|
|
1822
|
+
}
|
|
1823
|
+
const data = response.data;
|
|
1824
|
+
switch (subCommand) {
|
|
1825
|
+
case "requests": {
|
|
1826
|
+
const requests = data?.networkRequests || [];
|
|
1827
|
+
if (requests.length === 0) {
|
|
1828
|
+
console.log("\u6CA1\u6709\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55");
|
|
1829
|
+
console.log("\u63D0\u793A: \u4F7F\u7528 network requests \u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1830
|
+
} else {
|
|
1831
|
+
console.log(`\u7F51\u7EDC\u8BF7\u6C42 (${requests.length} \u6761):
|
|
1832
|
+
`);
|
|
1833
|
+
for (const req of requests) {
|
|
1834
|
+
const status = req.failed ? `FAILED (${req.failureReason})` : req.status ? `${req.status} ${req.statusText || ""}` : "pending";
|
|
1835
|
+
console.log(`${req.method} ${req.url}`);
|
|
1836
|
+
console.log(` \u7C7B\u578B: ${req.type}, \u72B6\u6001: ${status}`);
|
|
1837
|
+
if (options.withBody) {
|
|
1838
|
+
const requestHeaderCount = req.requestHeaders ? Object.keys(req.requestHeaders).length : 0;
|
|
1839
|
+
const responseHeaderCount = req.responseHeaders ? Object.keys(req.responseHeaders).length : 0;
|
|
1840
|
+
console.log(` \u8BF7\u6C42\u5934: ${requestHeaderCount}, \u54CD\u5E94\u5934: ${responseHeaderCount}`);
|
|
1841
|
+
if (req.requestBody !== void 0) {
|
|
1842
|
+
const preview = req.requestBody.length > 200 ? `${req.requestBody.slice(0, 200)}...` : req.requestBody;
|
|
1843
|
+
console.log(` \u8BF7\u6C42\u4F53: ${preview}`);
|
|
1844
|
+
}
|
|
1845
|
+
if (req.responseBody !== void 0) {
|
|
1846
|
+
const preview = req.responseBody.length > 200 ? `${req.responseBody.slice(0, 200)}...` : req.responseBody;
|
|
1847
|
+
console.log(` \u54CD\u5E94\u4F53: ${preview}`);
|
|
1848
|
+
}
|
|
1849
|
+
if (req.bodyError) {
|
|
1850
|
+
console.log(` Body\u9519\u8BEF: ${req.bodyError}`);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
console.log("");
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
break;
|
|
1857
|
+
}
|
|
1858
|
+
case "route": {
|
|
1859
|
+
console.log(`\u5DF2\u6DFB\u52A0\u62E6\u622A\u89C4\u5219: ${urlOrFilter}`);
|
|
1860
|
+
if (options.abort) {
|
|
1861
|
+
console.log(" \u884C\u4E3A: \u963B\u6B62\u8BF7\u6C42");
|
|
1862
|
+
} else if (options.body) {
|
|
1863
|
+
console.log(" \u884C\u4E3A: \u8FD4\u56DE mock \u6570\u636E");
|
|
1864
|
+
} else {
|
|
1865
|
+
console.log(" \u884C\u4E3A: \u7EE7\u7EED\u8BF7\u6C42");
|
|
1866
|
+
}
|
|
1867
|
+
console.log(`\u5F53\u524D\u89C4\u5219\u6570: ${data?.routeCount || 0}`);
|
|
1868
|
+
break;
|
|
1869
|
+
}
|
|
1870
|
+
case "unroute": {
|
|
1871
|
+
if (urlOrFilter) {
|
|
1872
|
+
console.log(`\u5DF2\u79FB\u9664\u62E6\u622A\u89C4\u5219: ${urlOrFilter}`);
|
|
1873
|
+
} else {
|
|
1874
|
+
console.log("\u5DF2\u79FB\u9664\u6240\u6709\u62E6\u622A\u89C4\u5219");
|
|
1875
|
+
}
|
|
1876
|
+
console.log(`\u5269\u4F59\u89C4\u5219\u6570: ${data?.routeCount || 0}`);
|
|
1877
|
+
break;
|
|
1878
|
+
}
|
|
1879
|
+
case "clear": {
|
|
1880
|
+
console.log("\u5DF2\u6E05\u7A7A\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55");
|
|
1881
|
+
break;
|
|
1882
|
+
}
|
|
1883
|
+
default:
|
|
1884
|
+
throw new Error(`\u672A\u77E5\u7684 network \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1695
1885
|
}
|
|
1696
1886
|
}
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1887
|
+
|
|
1888
|
+
// packages/cli/src/commands/console.ts
|
|
1889
|
+
async function consoleCommand(options = {}) {
|
|
1890
|
+
const response = await sendCommand({
|
|
1891
|
+
id: crypto.randomUUID(),
|
|
1892
|
+
action: "console",
|
|
1893
|
+
consoleCommand: options.clear ? "clear" : "get",
|
|
1894
|
+
tabId: options.tabId
|
|
1895
|
+
});
|
|
1896
|
+
if (options.json) {
|
|
1897
|
+
console.log(JSON.stringify(response));
|
|
1703
1898
|
return;
|
|
1704
1899
|
}
|
|
1705
|
-
if (
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
args: s.args,
|
|
1711
|
-
source: s.source
|
|
1712
|
-
})), null, 2));
|
|
1900
|
+
if (!response.success) {
|
|
1901
|
+
throw new Error(response.error || "Console command failed");
|
|
1902
|
+
}
|
|
1903
|
+
if (options.clear) {
|
|
1904
|
+
console.log("\u5DF2\u6E05\u7A7A\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1713
1905
|
return;
|
|
1714
1906
|
}
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1907
|
+
const messages = response.data?.consoleMessages || [];
|
|
1908
|
+
if (messages.length === 0) {
|
|
1909
|
+
console.log("\u6CA1\u6709\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1910
|
+
console.log("\u63D0\u793A: console \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1911
|
+
return;
|
|
1720
1912
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1913
|
+
console.log(`\u63A7\u5236\u53F0\u6D88\u606F (${messages.length} \u6761):
|
|
1914
|
+
`);
|
|
1915
|
+
const typeColors = {
|
|
1916
|
+
log: "",
|
|
1917
|
+
info: "[INFO]",
|
|
1918
|
+
warn: "[WARN]",
|
|
1919
|
+
error: "[ERROR]",
|
|
1920
|
+
debug: "[DEBUG]"
|
|
1921
|
+
};
|
|
1922
|
+
for (const msg of messages) {
|
|
1923
|
+
const prefix = typeColors[msg.type] || `[${msg.type.toUpperCase()}]`;
|
|
1924
|
+
const location = msg.url ? ` (${msg.url}${msg.lineNumber ? `:${msg.lineNumber}` : ""})` : "";
|
|
1925
|
+
if (prefix) {
|
|
1926
|
+
console.log(`${prefix} ${msg.text}${location}`);
|
|
1927
|
+
} else {
|
|
1928
|
+
console.log(`${msg.text}${location}`);
|
|
1729
1929
|
}
|
|
1730
1930
|
}
|
|
1731
|
-
console.log();
|
|
1732
1931
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1932
|
+
|
|
1933
|
+
// packages/cli/src/commands/errors.ts
|
|
1934
|
+
async function errorsCommand(options = {}) {
|
|
1935
|
+
const response = await sendCommand({
|
|
1936
|
+
id: crypto.randomUUID(),
|
|
1937
|
+
action: "errors",
|
|
1938
|
+
errorsCommand: options.clear ? "clear" : "get",
|
|
1939
|
+
tabId: options.tabId
|
|
1940
|
+
});
|
|
1941
|
+
if (options.json) {
|
|
1942
|
+
console.log(JSON.stringify(response));
|
|
1742
1943
|
return;
|
|
1743
1944
|
}
|
|
1744
|
-
if (
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
source: s.source
|
|
1750
|
-
})), null, 2));
|
|
1945
|
+
if (!response.success) {
|
|
1946
|
+
throw new Error(response.error || "Errors command failed");
|
|
1947
|
+
}
|
|
1948
|
+
if (options.clear) {
|
|
1949
|
+
console.log("\u5DF2\u6E05\u7A7A JS \u9519\u8BEF\u8BB0\u5F55");
|
|
1751
1950
|
return;
|
|
1752
1951
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
console.log(
|
|
1952
|
+
const errors = response.data?.jsErrors || [];
|
|
1953
|
+
if (errors.length === 0) {
|
|
1954
|
+
console.log("\u6CA1\u6709 JS \u9519\u8BEF");
|
|
1955
|
+
console.log("\u63D0\u793A: errors \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1956
|
+
return;
|
|
1756
1957
|
}
|
|
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);
|
|
1958
|
+
console.log(`JS \u9519\u8BEF (${errors.length} \u6761):
|
|
1959
|
+
`);
|
|
1960
|
+
for (const err of errors) {
|
|
1961
|
+
console.log(`[ERROR] ${err.message}`);
|
|
1962
|
+
if (err.url) {
|
|
1963
|
+
console.log(` \u4F4D\u7F6E: ${err.url}:${err.lineNumber || 0}:${err.columnNumber || 0}`);
|
|
1769
1964
|
}
|
|
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);
|
|
1965
|
+
if (err.stackTrace) {
|
|
1966
|
+
console.log(` \u5806\u6808:`);
|
|
1967
|
+
console.log(err.stackTrace.split("\n").map((line) => ` ${line}`).join("\n"));
|
|
1779
1968
|
}
|
|
1969
|
+
console.log("");
|
|
1780
1970
|
}
|
|
1781
|
-
const sites = scanSites(COMMUNITY_SITES_DIR, "community");
|
|
1782
|
-
console.log(`\u5DF2\u5B89\u88C5 ${sites.length} \u4E2A\u793E\u533A adapter\u3002`);
|
|
1783
1971
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1972
|
+
|
|
1973
|
+
// packages/cli/src/commands/trace.ts
|
|
1974
|
+
async function traceCommand(subCommand, options = {}) {
|
|
1975
|
+
const response = await sendCommand({
|
|
1976
|
+
id: crypto.randomUUID(),
|
|
1977
|
+
action: "trace",
|
|
1978
|
+
traceCommand: subCommand,
|
|
1979
|
+
tabId: options.tabId
|
|
1980
|
+
});
|
|
1981
|
+
if (options.json) {
|
|
1982
|
+
console.log(JSON.stringify(response));
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
if (!response.success) {
|
|
1986
|
+
throw new Error(response.error || "Trace command failed");
|
|
1987
|
+
}
|
|
1988
|
+
const data = response.data;
|
|
1989
|
+
switch (subCommand) {
|
|
1990
|
+
case "start": {
|
|
1991
|
+
const status = data?.traceStatus;
|
|
1992
|
+
console.log("\u5F00\u59CB\u5F55\u5236\u7528\u6237\u64CD\u4F5C");
|
|
1993
|
+
console.log(`\u6807\u7B7E\u9875 ID: ${status?.tabId || "N/A"}`);
|
|
1994
|
+
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");
|
|
1995
|
+
break;
|
|
1996
|
+
}
|
|
1997
|
+
case "stop": {
|
|
1998
|
+
const events = data?.traceEvents || [];
|
|
1999
|
+
const status = data?.traceStatus;
|
|
2000
|
+
console.log(`\u5F55\u5236\u5B8C\u6210\uFF0C\u5171 ${events.length} \u4E2A\u4E8B\u4EF6
|
|
2001
|
+
`);
|
|
2002
|
+
if (events.length === 0) {
|
|
2003
|
+
console.log("\u6CA1\u6709\u5F55\u5236\u5230\u4EFB\u4F55\u64CD\u4F5C");
|
|
2004
|
+
break;
|
|
1794
2005
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
2006
|
+
for (let i = 0; i < events.length; i++) {
|
|
2007
|
+
const event = events[i];
|
|
2008
|
+
const refStr = event.ref !== void 0 ? `@${event.ref}` : "";
|
|
2009
|
+
switch (event.type) {
|
|
2010
|
+
case "navigation":
|
|
2011
|
+
console.log(`${i + 1}. \u5BFC\u822A\u5230: ${event.url}`);
|
|
2012
|
+
break;
|
|
2013
|
+
case "click":
|
|
2014
|
+
console.log(`${i + 1}. \u70B9\u51FB ${refStr} [${event.elementRole}] "${event.elementName || ""}"`);
|
|
2015
|
+
break;
|
|
2016
|
+
case "fill":
|
|
2017
|
+
console.log(`${i + 1}. \u586B\u5145 ${refStr} [${event.elementRole}] "${event.elementName || ""}" <- "${event.value}"`);
|
|
2018
|
+
break;
|
|
2019
|
+
case "select":
|
|
2020
|
+
console.log(`${i + 1}. \u9009\u62E9 ${refStr} [${event.elementRole}] "${event.elementName || ""}" <- "${event.value}"`);
|
|
2021
|
+
break;
|
|
2022
|
+
case "check":
|
|
2023
|
+
console.log(`${i + 1}. ${event.checked ? "\u52FE\u9009" : "\u53D6\u6D88\u52FE\u9009"} ${refStr} [${event.elementRole}] "${event.elementName || ""}"`);
|
|
2024
|
+
break;
|
|
2025
|
+
case "press":
|
|
2026
|
+
console.log(`${i + 1}. \u6309\u952E ${event.key}`);
|
|
2027
|
+
break;
|
|
2028
|
+
case "scroll":
|
|
2029
|
+
console.log(`${i + 1}. \u6EDA\u52A8 ${event.direction} ${event.pixels}px`);
|
|
2030
|
+
break;
|
|
2031
|
+
default:
|
|
2032
|
+
console.log(`${i + 1}. ${event.type}`);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
console.log(`
|
|
2036
|
+
\u72B6\u6001: ${status?.recording ? "\u5F55\u5236\u4E2D" : "\u5DF2\u505C\u6B62"}`);
|
|
2037
|
+
break;
|
|
2038
|
+
}
|
|
2039
|
+
case "status": {
|
|
2040
|
+
const status = data?.traceStatus;
|
|
2041
|
+
if (status?.recording) {
|
|
2042
|
+
console.log(`\u5F55\u5236\u4E2D (\u6807\u7B7E\u9875 ${status.tabId})`);
|
|
2043
|
+
console.log(`\u5DF2\u5F55\u5236 ${status.eventCount} \u4E2A\u4E8B\u4EF6`);
|
|
2044
|
+
} else {
|
|
2045
|
+
console.log("\u672A\u5728\u5F55\u5236");
|
|
2046
|
+
}
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
2049
|
+
default:
|
|
2050
|
+
throw new Error(`\u672A\u77E5\u7684 trace \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
// packages/cli/src/commands/fetch.ts
|
|
2055
|
+
function matchTabOrigin2(tabUrl, targetHostname) {
|
|
2056
|
+
try {
|
|
2057
|
+
const tabHostname = new URL(tabUrl).hostname;
|
|
2058
|
+
return tabHostname === targetHostname || tabHostname.endsWith("." + targetHostname);
|
|
2059
|
+
} catch {
|
|
2060
|
+
return false;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
async function ensureTabForOrigin(origin, hostname) {
|
|
2064
|
+
const listReq = { id: generateId(), action: "tab_list" };
|
|
2065
|
+
const listResp = await sendCommand(listReq);
|
|
2066
|
+
if (listResp.success && listResp.data?.tabs) {
|
|
2067
|
+
const matchingTab = listResp.data.tabs.find(
|
|
2068
|
+
(tab) => matchTabOrigin2(tab.url, hostname)
|
|
2069
|
+
);
|
|
2070
|
+
if (matchingTab) {
|
|
2071
|
+
return matchingTab.tabId;
|
|
1798
2072
|
}
|
|
1799
|
-
process.exit(1);
|
|
1800
2073
|
}
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
for (let i = 0; i < args.length; i++) {
|
|
1805
|
-
if (args[i].startsWith("--")) {
|
|
1806
|
-
const flagName = args[i].slice(2);
|
|
1807
|
-
if (flagName in site.args && args[i + 1]) {
|
|
1808
|
-
argMap[flagName] = args[i + 1];
|
|
1809
|
-
i++;
|
|
1810
|
-
}
|
|
1811
|
-
} else {
|
|
1812
|
-
positionalArgs.push(args[i]);
|
|
1813
|
-
}
|
|
2074
|
+
const newResp = await sendCommand({ id: generateId(), action: "tab_new", url: origin });
|
|
2075
|
+
if (!newResp.success) {
|
|
2076
|
+
throw new Error(`\u65E0\u6CD5\u6253\u5F00 ${origin}: ${newResp.error}`);
|
|
1814
2077
|
}
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2078
|
+
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
2079
|
+
return newResp.data?.tabId;
|
|
2080
|
+
}
|
|
2081
|
+
function buildFetchScript(url, options) {
|
|
2082
|
+
const method = (options.method || "GET").toUpperCase();
|
|
2083
|
+
const hasBody = options.body && method !== "GET" && method !== "HEAD";
|
|
2084
|
+
let headersExpr = "{}";
|
|
2085
|
+
if (options.headers) {
|
|
2086
|
+
try {
|
|
2087
|
+
JSON.parse(options.headers);
|
|
2088
|
+
headersExpr = options.headers;
|
|
2089
|
+
} catch {
|
|
2090
|
+
throw new Error(`--headers must be valid JSON. Got: ${options.headers}`);
|
|
1819
2091
|
}
|
|
1820
2092
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
2093
|
+
return `(async () => {
|
|
2094
|
+
try {
|
|
2095
|
+
const resp = await fetch(${JSON.stringify(url)}, {
|
|
2096
|
+
method: ${JSON.stringify(method)},
|
|
2097
|
+
credentials: 'include',
|
|
2098
|
+
headers: ${headersExpr}${hasBody ? `,
|
|
2099
|
+
body: ${JSON.stringify(options.body)}` : ""}
|
|
2100
|
+
});
|
|
2101
|
+
const contentType = resp.headers.get('content-type') || '';
|
|
2102
|
+
let body;
|
|
2103
|
+
if (contentType.includes('application/json') && resp.status !== 204) {
|
|
2104
|
+
try { body = await resp.json(); } catch { body = await resp.text(); }
|
|
2105
|
+
} else {
|
|
2106
|
+
body = await resp.text();
|
|
2107
|
+
}
|
|
2108
|
+
return JSON.stringify({
|
|
2109
|
+
status: resp.status,
|
|
2110
|
+
contentType,
|
|
2111
|
+
body
|
|
2112
|
+
});
|
|
2113
|
+
} catch (e) {
|
|
2114
|
+
return JSON.stringify({ error: e.message });
|
|
1831
2115
|
}
|
|
2116
|
+
})()`;
|
|
2117
|
+
}
|
|
2118
|
+
async function fetchCommand(url, options = {}) {
|
|
2119
|
+
if (!url) {
|
|
2120
|
+
throw new Error(
|
|
2121
|
+
"\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"
|
|
2122
|
+
);
|
|
1832
2123
|
}
|
|
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
2124
|
await ensureDaemonRunning();
|
|
2125
|
+
const isAbsolute = url.startsWith("http://") || url.startsWith("https://");
|
|
1838
2126
|
let targetTabId = options.tabId;
|
|
1839
|
-
if (
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
const
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
}
|
|
2127
|
+
if (isAbsolute) {
|
|
2128
|
+
let origin;
|
|
2129
|
+
let hostname;
|
|
2130
|
+
try {
|
|
2131
|
+
const parsed = new URL(url);
|
|
2132
|
+
origin = parsed.origin;
|
|
2133
|
+
hostname = parsed.hostname;
|
|
2134
|
+
} catch {
|
|
2135
|
+
throw new Error(`\u65E0\u6548\u7684 URL: ${url}`);
|
|
1849
2136
|
}
|
|
1850
2137
|
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));
|
|
2138
|
+
targetTabId = await ensureTabForOrigin(origin, hostname);
|
|
1858
2139
|
}
|
|
1859
2140
|
}
|
|
2141
|
+
const script = buildFetchScript(url, options);
|
|
1860
2142
|
const evalReq = { id: generateId(), action: "eval", script, tabId: targetTabId };
|
|
1861
2143
|
const evalResp = await sendCommand(evalReq);
|
|
1862
2144
|
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);
|
|
2145
|
+
throw new Error(`Fetch \u5931\u8D25: ${evalResp.error}`);
|
|
1871
2146
|
}
|
|
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;
|
|
2147
|
+
const rawResult = evalResp.data?.result;
|
|
2148
|
+
if (rawResult === void 0 || rawResult === null) {
|
|
2149
|
+
throw new Error("Fetch \u672A\u8FD4\u56DE\u7ED3\u679C");
|
|
1880
2150
|
}
|
|
1881
|
-
let
|
|
2151
|
+
let result;
|
|
1882
2152
|
try {
|
|
1883
|
-
|
|
2153
|
+
result = typeof rawResult === "string" ? JSON.parse(rawResult) : rawResult;
|
|
1884
2154
|
} catch {
|
|
1885
|
-
|
|
2155
|
+
console.log(rawResult);
|
|
2156
|
+
return;
|
|
1886
2157
|
}
|
|
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);
|
|
2158
|
+
if (result.error) {
|
|
2159
|
+
throw new Error(`Fetch error: ${result.error}`);
|
|
1903
2160
|
}
|
|
1904
|
-
if (options.
|
|
1905
|
-
|
|
2161
|
+
if (options.output) {
|
|
2162
|
+
const { writeFileSync } = await import("fs");
|
|
2163
|
+
const content = typeof result.body === "object" ? JSON.stringify(result.body, null, 2) : String(result.body);
|
|
2164
|
+
writeFileSync(options.output, content, "utf-8");
|
|
2165
|
+
console.log(`\u5DF2\u5199\u5165 ${options.output} (${result.status}, ${content.length} bytes)`);
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
if (typeof result.body === "object") {
|
|
2169
|
+
console.log(JSON.stringify(result.body, null, 2));
|
|
1906
2170
|
} else {
|
|
1907
|
-
console.log(
|
|
2171
|
+
console.log(result.body);
|
|
1908
2172
|
}
|
|
1909
2173
|
}
|
|
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
2174
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
\u521B\u5EFA\u65B0 adapter: bb-browser guide
|
|
1934
|
-
\u62A5\u544A\u95EE\u9898: gh issue create --repo epiral/bb-sites --title "[adapter-name] \u63CF\u8FF0"
|
|
1935
|
-
\u8D21\u732E\u793E\u533A: https://github.com/epiral/bb-sites`);
|
|
2175
|
+
// packages/cli/src/commands/history.ts
|
|
2176
|
+
async function historyCommand(subCommand, options = {}) {
|
|
2177
|
+
const response = await sendCommand({
|
|
2178
|
+
id: crypto.randomUUID(),
|
|
2179
|
+
action: "history",
|
|
2180
|
+
historyCommand: subCommand,
|
|
2181
|
+
text: options.query,
|
|
2182
|
+
ms: options.days
|
|
2183
|
+
});
|
|
2184
|
+
if (options.json) {
|
|
2185
|
+
console.log(JSON.stringify(response));
|
|
1936
2186
|
return;
|
|
1937
2187
|
}
|
|
2188
|
+
if (!response.success) {
|
|
2189
|
+
throw new Error(response.error || "History command failed");
|
|
2190
|
+
}
|
|
2191
|
+
const data = response.data;
|
|
1938
2192
|
switch (subCommand) {
|
|
1939
|
-
case "
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
if (
|
|
1944
|
-
console.
|
|
1945
|
-
|
|
1946
|
-
process.exit(1);
|
|
2193
|
+
case "search": {
|
|
2194
|
+
const items = data?.historyItems || [];
|
|
2195
|
+
console.log(`\u627E\u5230 ${items.length} \u6761\u5386\u53F2\u8BB0\u5F55
|
|
2196
|
+
`);
|
|
2197
|
+
if (items.length === 0) {
|
|
2198
|
+
console.log("\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u5386\u53F2\u8BB0\u5F55");
|
|
2199
|
+
break;
|
|
1947
2200
|
}
|
|
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);
|
|
2201
|
+
for (let i = 0; i < items.length; i++) {
|
|
2202
|
+
const item = items[i];
|
|
2203
|
+
console.log(`${i + 1}. ${item.title || "(\u65E0\u6807\u9898)"}`);
|
|
2204
|
+
console.log(` ${item.url}`);
|
|
2205
|
+
console.log(` \u8BBF\u95EE\u6B21\u6570: ${item.visitCount}`);
|
|
1959
2206
|
}
|
|
1960
|
-
await siteRun(args[1], args.slice(2), options);
|
|
1961
2207
|
break;
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
console.
|
|
1969
|
-
|
|
2208
|
+
}
|
|
2209
|
+
case "domains": {
|
|
2210
|
+
const domains = data?.historyDomains || [];
|
|
2211
|
+
console.log(`\u627E\u5230 ${domains.length} \u4E2A\u57DF\u540D
|
|
2212
|
+
`);
|
|
2213
|
+
if (domains.length === 0) {
|
|
2214
|
+
console.log("\u6CA1\u6709\u627E\u5230\u5386\u53F2\u8BB0\u5F55");
|
|
2215
|
+
break;
|
|
2216
|
+
}
|
|
2217
|
+
for (let i = 0; i < domains.length; i++) {
|
|
2218
|
+
const domain = domains[i];
|
|
2219
|
+
console.log(`${i + 1}. ${domain.domain}`);
|
|
2220
|
+
console.log(` \u8BBF\u95EE\u6B21\u6570: ${domain.visits}`);
|
|
1970
2221
|
}
|
|
1971
2222
|
break;
|
|
2223
|
+
}
|
|
2224
|
+
default:
|
|
2225
|
+
throw new Error(`\u672A\u77E5\u7684 history \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1972
2226
|
}
|
|
1973
2227
|
}
|
|
1974
2228
|
|
|
@@ -1977,74 +2231,56 @@ var VERSION = "0.3.0";
|
|
|
1977
2231
|
var HELP_TEXT = `
|
|
1978
2232
|
bb-browser - AI Agent \u6D4F\u89C8\u5668\u81EA\u52A8\u5316\u5DE5\u5177
|
|
1979
2233
|
|
|
2234
|
+
\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
|
|
2235
|
+
bb-browser site list \u67E5\u770B\u6240\u6709\u53EF\u7528\u547D\u4EE4
|
|
2236
|
+
bb-browser site twitter/search "AI" \u793A\u4F8B\uFF1A\u641C\u7D22\u63A8\u6587
|
|
2237
|
+
bb-browser site xueqiu/hot-stock 5 \u793A\u4F8B\uFF1A\u83B7\u53D6\u4EBA\u6C14\u80A1\u7968
|
|
2238
|
+
|
|
1980
2239
|
\u7528\u6CD5\uFF1A
|
|
1981
2240
|
bb-browser <command> [options]
|
|
1982
2241
|
|
|
1983
|
-
\
|
|
1984
|
-
site
|
|
1985
|
-
site
|
|
1986
|
-
site <name>
|
|
1987
|
-
site
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
\u793A\u4F8B\uFF1A
|
|
1991
|
-
bb-browser site twitter/search "claude code"
|
|
1992
|
-
bb-browser site reddit/thread <url>
|
|
1993
|
-
bb-browser site github/pr-create owner/repo --title "feat: ..."
|
|
1994
|
-
|
|
1995
|
-
\u9875\u9762\u5BFC\u822A\uFF1A
|
|
1996
|
-
open <url> [--tab] \u6253\u5F00\u6307\u5B9A URL\uFF08\u9ED8\u8BA4\u65B0 tab\uFF0C--tab current \u5F53\u524D tab\uFF09
|
|
1997
|
-
back / forward \u540E\u9000 / \u524D\u8FDB
|
|
1998
|
-
refresh \u5237\u65B0\u9875\u9762
|
|
1999
|
-
close \u5173\u95ED\u5F53\u524D\u6807\u7B7E\u9875
|
|
2000
|
-
tab \u5217\u51FA\u6240\u6709\u6807\u7B7E\u9875
|
|
2001
|
-
tab new [url] \u65B0\u5EFA\u6807\u7B7E\u9875
|
|
2002
|
-
tab <n> \u5207\u6362\u5230\u7B2C n \u4E2A\u6807\u7B7E\u9875\uFF08\u6309 index\uFF09
|
|
2003
|
-
tab select --id <id> \u5207\u6362\u5230\u6307\u5B9A tabId \u7684\u6807\u7B7E\u9875
|
|
2004
|
-
tab close [n|--id <id>] \u5173\u95ED\u6807\u7B7E\u9875
|
|
2005
|
-
frame <selector> \u5207\u6362\u5230\u6307\u5B9A iframe
|
|
2006
|
-
frame main \u8FD4\u56DE\u4E3B frame
|
|
2007
|
-
wait <ms|@ref> \u7B49\u5F85\u65F6\u95F4\u6216\u5143\u7D20
|
|
2242
|
+
\u5F00\u59CB\u4F7F\u7528\uFF1A
|
|
2243
|
+
site recommend \u63A8\u8350\u4F60\u53EF\u80FD\u9700\u8981\u7684 adapter\uFF08\u57FA\u4E8E\u6D4F\u89C8\u5386\u53F2\uFF09
|
|
2244
|
+
site list \u5217\u51FA\u6240\u6709 adapter
|
|
2245
|
+
site info <name> \u67E5\u770B adapter \u7528\u6CD5\uFF08\u53C2\u6570\u3001\u8FD4\u56DE\u503C\u3001\u793A\u4F8B\uFF09
|
|
2246
|
+
site <name> [args] \u8FD0\u884C adapter
|
|
2247
|
+
site update \u66F4\u65B0\u793E\u533A adapter \u5E93
|
|
2248
|
+
guide \u5982\u4F55\u628A\u4EFB\u4F55\u7F51\u7AD9\u53D8\u6210 adapter
|
|
2008
2249
|
|
|
2009
|
-
\
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
dialog dismiss \u62D2\u7EDD/\u5173\u95ED\u5BF9\u8BDD\u6846
|
|
2250
|
+
\u6D4F\u89C8\u5668\u64CD\u4F5C\uFF1A
|
|
2251
|
+
open <url> [--tab] \u6253\u5F00 URL
|
|
2252
|
+
snapshot [-i] [-c] [-d <n>] \u83B7\u53D6\u9875\u9762\u5FEB\u7167
|
|
2253
|
+
click <ref> \u70B9\u51FB\u5143\u7D20
|
|
2254
|
+
hover <ref> \u60AC\u505C\u5143\u7D20
|
|
2255
|
+
fill <ref> <text> \u586B\u5145\u8F93\u5165\u6846\uFF08\u6E05\u7A7A\u540E\u586B\u5165\uFF09
|
|
2256
|
+
type <ref> <text> \u9010\u5B57\u7B26\u8F93\u5165\uFF08\u4E0D\u6E05\u7A7A\uFF09
|
|
2257
|
+
check/uncheck <ref> \u52FE\u9009/\u53D6\u6D88\u590D\u9009\u6846
|
|
2258
|
+
select <ref> <val> \u4E0B\u62C9\u6846\u9009\u62E9
|
|
2259
|
+
press <key> \u53D1\u9001\u6309\u952E
|
|
2260
|
+
scroll <dir> [px] \u6EDA\u52A8\u9875\u9762
|
|
2021
2261
|
|
|
2022
2262
|
\u9875\u9762\u4FE1\u606F\uFF1A
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
screenshot [path] \u622A\u53D6\u5F53\u524D\u9875\u9762
|
|
2028
|
-
eval "<js>" \u6267\u884C JavaScript
|
|
2029
|
-
fetch <url> \u5728\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u4E2D fetch\uFF08\u81EA\u52A8\u540C\u6E90\u8DEF\u7531\uFF0C\u5E26\u767B\u5F55\u6001\uFF09
|
|
2263
|
+
get text|url|title <ref> \u83B7\u53D6\u9875\u9762\u5185\u5BB9
|
|
2264
|
+
screenshot [path] \u622A\u56FE
|
|
2265
|
+
eval "<js>" \u6267\u884C JavaScript
|
|
2266
|
+
fetch <url> \u5E26\u767B\u5F55\u6001\u7684 HTTP \u8BF7\u6C42
|
|
2030
2267
|
|
|
2031
|
-
\
|
|
2032
|
-
|
|
2033
|
-
network route <url> [--abort|--body <json>] \u62E6\u622A\u8BF7\u6C42
|
|
2034
|
-
network unroute [url] \u79FB\u9664\u62E6\u622A\u89C4\u5219
|
|
2035
|
-
network clear \u6E05\u7A7A\u8BF7\u6C42\u8BB0\u5F55
|
|
2036
|
-
console [--clear] \u67E5\u770B/\u6E05\u7A7A\u63A7\u5236\u53F0\u6D88\u606F
|
|
2037
|
-
errors [--clear] \u67E5\u770B/\u6E05\u7A7A JS \u9519\u8BEF
|
|
2038
|
-
trace start|stop|status \u5F55\u5236\u7528\u6237\u64CD\u4F5C
|
|
2268
|
+
\u6807\u7B7E\u9875\uFF1A
|
|
2269
|
+
tab [list|new|close|<n>] \u7BA1\u7406\u6807\u7B7E\u9875
|
|
2039
2270
|
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2271
|
+
\u5BFC\u822A\uFF1A
|
|
2272
|
+
back / forward / refresh \u540E\u9000 / \u524D\u8FDB / \u5237\u65B0
|
|
2273
|
+
|
|
2274
|
+
\u8C03\u8BD5\uFF1A
|
|
2275
|
+
network requests [filter] \u67E5\u770B\u7F51\u7EDC\u8BF7\u6C42
|
|
2276
|
+
console [--clear] \u67E5\u770B/\u6E05\u7A7A\u63A7\u5236\u53F0
|
|
2277
|
+
errors [--clear] \u67E5\u770B/\u6E05\u7A7A JS \u9519\u8BEF
|
|
2278
|
+
trace start|stop|status \u5F55\u5236\u7528\u6237\u64CD\u4F5C
|
|
2279
|
+
history search|domains \u67E5\u770B\u6D4F\u89C8\u5386\u53F2
|
|
2045
2280
|
|
|
2046
2281
|
\u9009\u9879\uFF1A
|
|
2047
2282
|
--json \u4EE5 JSON \u683C\u5F0F\u8F93\u51FA
|
|
2283
|
+
--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
|
|
2048
2284
|
-i, --interactive \u53EA\u8F93\u51FA\u53EF\u4EA4\u4E92\u5143\u7D20\uFF08snapshot \u547D\u4EE4\uFF09
|
|
2049
2285
|
-c, --compact \u79FB\u9664\u7A7A\u7ED3\u6784\u8282\u70B9\uFF08snapshot \u547D\u4EE4\uFF09
|
|
2050
2286
|
-d, --depth <n> \u9650\u5236\u6811\u6DF1\u5EA6\uFF08snapshot \u547D\u4EE4\uFF09
|
|
@@ -2075,6 +2311,13 @@ function parseArgs(argv) {
|
|
|
2075
2311
|
}
|
|
2076
2312
|
if (arg === "--json") {
|
|
2077
2313
|
result.flags.json = true;
|
|
2314
|
+
} else if (arg === "--jq") {
|
|
2315
|
+
skipNext = true;
|
|
2316
|
+
const nextIdx = args.indexOf(arg) + 1;
|
|
2317
|
+
if (nextIdx < args.length) {
|
|
2318
|
+
result.flags.jq = args[nextIdx];
|
|
2319
|
+
result.flags.json = true;
|
|
2320
|
+
}
|
|
2078
2321
|
} else if (arg === "--help" || arg === "-h") {
|
|
2079
2322
|
result.flags.help = true;
|
|
2080
2323
|
} else if (arg === "--version" || arg === "-v") {
|
|
@@ -2095,6 +2338,12 @@ function parseArgs(argv) {
|
|
|
2095
2338
|
if (nextIdx < args.length) {
|
|
2096
2339
|
result.flags.selector = args[nextIdx];
|
|
2097
2340
|
}
|
|
2341
|
+
} else if (arg === "--days") {
|
|
2342
|
+
skipNext = true;
|
|
2343
|
+
const nextIdx = args.indexOf(arg) + 1;
|
|
2344
|
+
if (nextIdx < args.length) {
|
|
2345
|
+
result.flags.days = parseInt(args[nextIdx], 10);
|
|
2346
|
+
}
|
|
2098
2347
|
} else if (arg === "--id") {
|
|
2099
2348
|
skipNext = true;
|
|
2100
2349
|
} else if (arg === "--tab") {
|
|
@@ -2110,6 +2359,7 @@ function parseArgs(argv) {
|
|
|
2110
2359
|
}
|
|
2111
2360
|
async function main() {
|
|
2112
2361
|
const parsed = parseArgs(process.argv);
|
|
2362
|
+
setJqExpression(parsed.flags.jq);
|
|
2113
2363
|
const tabArgIdx = process.argv.indexOf("--tab");
|
|
2114
2364
|
const globalTabId = tabArgIdx >= 0 && process.argv[tabArgIdx + 1] ? parseInt(process.argv[tabArgIdx + 1], 10) : void 0;
|
|
2115
2365
|
if (parsed.flags.version) {
|
|
@@ -2423,6 +2673,23 @@ async function main() {
|
|
|
2423
2673
|
await traceCommand(subCmd, { json: parsed.flags.json, tabId: globalTabId });
|
|
2424
2674
|
break;
|
|
2425
2675
|
}
|
|
2676
|
+
case "history": {
|
|
2677
|
+
const subCmd = parsed.args[0];
|
|
2678
|
+
if (!subCmd || !["search", "domains"].includes(subCmd)) {
|
|
2679
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11\u6216\u65E0\u6548\u7684\u5B50\u547D\u4EE4");
|
|
2680
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser history <search|domains> [query] [--days <n>]");
|
|
2681
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser history search github");
|
|
2682
|
+
console.error(" bb-browser history domains --days 7");
|
|
2683
|
+
process.exit(1);
|
|
2684
|
+
}
|
|
2685
|
+
const query = parsed.args.slice(1).join(" ");
|
|
2686
|
+
await historyCommand(subCmd, {
|
|
2687
|
+
json: parsed.flags.json,
|
|
2688
|
+
days: parsed.flags.days || 30,
|
|
2689
|
+
query
|
|
2690
|
+
});
|
|
2691
|
+
break;
|
|
2692
|
+
}
|
|
2426
2693
|
case "fetch": {
|
|
2427
2694
|
const fetchUrl = parsed.args[0];
|
|
2428
2695
|
if (!fetchUrl) {
|
|
@@ -2450,7 +2717,12 @@ async function main() {
|
|
|
2450
2717
|
break;
|
|
2451
2718
|
}
|
|
2452
2719
|
case "site": {
|
|
2453
|
-
await siteCommand(parsed.args, {
|
|
2720
|
+
await siteCommand(parsed.args, {
|
|
2721
|
+
json: parsed.flags.json,
|
|
2722
|
+
jq: parsed.flags.jq,
|
|
2723
|
+
days: parsed.flags.days,
|
|
2724
|
+
tabId: globalTabId
|
|
2725
|
+
});
|
|
2454
2726
|
break;
|
|
2455
2727
|
}
|
|
2456
2728
|
case "guide": {
|