browser-web-search 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +132 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +691 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +45 -0
  6. package/sites/36kr/newsflash.js +76 -0
  7. package/sites/baidu/search.js +62 -0
  8. package/sites/bilibili/comments.js +74 -0
  9. package/sites/bilibili/feed.js +79 -0
  10. package/sites/bilibili/history.js +43 -0
  11. package/sites/bilibili/me.js +45 -0
  12. package/sites/bilibili/popular.js +44 -0
  13. package/sites/bilibili/ranking.js +42 -0
  14. package/sites/bilibili/search.js +46 -0
  15. package/sites/bilibili/trending.js +32 -0
  16. package/sites/bilibili/video.js +73 -0
  17. package/sites/bing/search.js +40 -0
  18. package/sites/boss/detail.js +38 -0
  19. package/sites/boss/search.js +44 -0
  20. package/sites/cnblogs/search.js +68 -0
  21. package/sites/csdn/search.js +51 -0
  22. package/sites/douban/comments.js +58 -0
  23. package/sites/douban/movie-hot.js +64 -0
  24. package/sites/douban/movie-top.js +65 -0
  25. package/sites/douban/movie.js +117 -0
  26. package/sites/douban/search.js +90 -0
  27. package/sites/douban/top250.js +73 -0
  28. package/sites/github/fork.js +38 -0
  29. package/sites/github/issue-create.js +42 -0
  30. package/sites/github/issues.js +32 -0
  31. package/sites/github/me.js +22 -0
  32. package/sites/github/pr-create.js +55 -0
  33. package/sites/github/repo.js +27 -0
  34. package/sites/google/search.js +54 -0
  35. package/sites/toutiao/hot.js +107 -0
  36. package/sites/toutiao/search.js +146 -0
  37. package/sites/xiaohongshu/comments.js +56 -0
  38. package/sites/xiaohongshu/feed.js +50 -0
  39. package/sites/xiaohongshu/me.js +49 -0
  40. package/sites/xiaohongshu/note.js +63 -0
  41. package/sites/xiaohongshu/search.js +56 -0
  42. package/sites/xiaohongshu/user_posts.js +53 -0
  43. package/sites/zhihu/hot.js +36 -0
  44. package/sites/zhihu/me.js +43 -0
  45. package/sites/zhihu/question.js +62 -0
  46. package/sites/zhihu/search.js +58 -0
package/dist/index.js ADDED
@@ -0,0 +1,691 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/site.ts
4
+ import { readFileSync, readdirSync, existsSync } from "fs";
5
+ import { join, relative } from "path";
6
+ import { homedir } from "os";
7
+
8
+ // src/openclaw-bridge.ts
9
+ import { execFileSync } from "child_process";
10
+
11
+ // src/openclaw-json.ts
12
+ function buildPreview(raw) {
13
+ return raw.length > 200 ? `${raw.slice(0, 200)}...` : raw;
14
+ }
15
+ function tryParseJson(raw) {
16
+ try {
17
+ return { ok: true, value: JSON.parse(raw) };
18
+ } catch (error) {
19
+ return {
20
+ ok: false,
21
+ error: error instanceof Error ? error : new Error(String(error))
22
+ };
23
+ }
24
+ }
25
+ function tryParseLastJsonLineBlock(raw) {
26
+ const lines = raw.split(/\r?\n/);
27
+ for (let end = lines.length; end > 0; end -= 1) {
28
+ for (let start = end - 1; start >= 0; start -= 1) {
29
+ const candidate = lines.slice(start, end).join("\n").trim();
30
+ if (!candidate) {
31
+ continue;
32
+ }
33
+ const parsed = tryParseJson(candidate);
34
+ if (parsed.ok) {
35
+ return parsed;
36
+ }
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+ function parseOpenClawJson(raw) {
42
+ const trimmed = raw.trim();
43
+ if (!trimmed) {
44
+ throw new Error("OpenClaw returned empty output");
45
+ }
46
+ const direct = tryParseJson(trimmed);
47
+ if (direct.ok) {
48
+ return direct.value;
49
+ }
50
+ const lineBlock = tryParseLastJsonLineBlock(trimmed);
51
+ if (lineBlock) {
52
+ return lineBlock.value;
53
+ }
54
+ throw new Error(`Failed to parse OpenClaw JSON output: ${direct.error.message}
55
+ Raw (preview): ${buildPreview(trimmed)}`);
56
+ }
57
+
58
+ // src/openclaw-bridge.ts
59
+ var OPENCLAW_EVALUATE_TIMEOUT_MS = 12e4;
60
+ var EXEC_TIMEOUT_BUFFER_MS = 5e3;
61
+ function buildOpenClawArgs(args, timeout) {
62
+ const [subcommand, ...rest] = args;
63
+ if (!subcommand) {
64
+ throw new Error("OpenClaw browser command requires a subcommand");
65
+ }
66
+ return ["openclaw", "browser", subcommand, "--timeout", String(timeout), ...rest];
67
+ }
68
+ function getOpenClawExecTimeout(timeout) {
69
+ return timeout + EXEC_TIMEOUT_BUFFER_MS;
70
+ }
71
+ function runOpenClaw(args, timeout) {
72
+ return execFileSync("npx", buildOpenClawArgs(args, timeout), {
73
+ encoding: "utf-8",
74
+ timeout: getOpenClawExecTimeout(timeout),
75
+ stdio: ["pipe", "pipe", "pipe"]
76
+ }).trim();
77
+ }
78
+ function ocGetTabs() {
79
+ const raw = runOpenClaw(["tabs", "--json"], 15e3);
80
+ const data = parseOpenClawJson(raw);
81
+ return (data.tabs || []).filter((tab) => tab.type === "page");
82
+ }
83
+ function ocFindTabByDomain(tabs, domain) {
84
+ return tabs.find((tab) => {
85
+ try {
86
+ const hostname = new URL(tab.url).hostname;
87
+ return hostname === domain || hostname.endsWith(`.${domain}`);
88
+ } catch {
89
+ return false;
90
+ }
91
+ });
92
+ }
93
+ function ocOpenTab(url) {
94
+ const raw = runOpenClaw(["open", url, "--json"], 3e4);
95
+ const data = parseOpenClawJson(raw);
96
+ return data.id || data.targetId || "";
97
+ }
98
+ function ocEvaluate(targetId, fn) {
99
+ const raw = runOpenClaw(["evaluate", "--fn", fn, "--target-id", targetId], OPENCLAW_EVALUATE_TIMEOUT_MS);
100
+ return parseOpenClawJson(raw);
101
+ }
102
+
103
+ // src/jq.ts
104
+ function splitTopLevel(input, separator) {
105
+ const parts = [];
106
+ let current = "";
107
+ let depth = 0;
108
+ let inString = false;
109
+ for (let i = 0; i < input.length; i++) {
110
+ const char = input[i];
111
+ const prev = input[i - 1];
112
+ if (char === '"' && prev !== "\\") inString = !inString;
113
+ if (!inString) {
114
+ if (char === "{" || char === "(" || char === "[") depth++;
115
+ if (char === "}" || char === ")" || char === "]") depth--;
116
+ if (depth === 0 && input.slice(i, i + separator.length) === separator) {
117
+ parts.push(current.trim());
118
+ current = "";
119
+ i += separator.length - 1;
120
+ continue;
121
+ }
122
+ }
123
+ current += char;
124
+ }
125
+ if (current.trim()) parts.push(current.trim());
126
+ return parts;
127
+ }
128
+ function parseLiteral(value) {
129
+ const trimmed = value.trim();
130
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) return JSON.parse(trimmed);
131
+ if (trimmed === "true") return true;
132
+ if (trimmed === "false") return false;
133
+ if (trimmed === "null") return null;
134
+ return Number(trimmed);
135
+ }
136
+ function getField(value, field) {
137
+ return value !== null && typeof value === "object" ? value[field] : void 0;
138
+ }
139
+ function applySegment(inputs, expr) {
140
+ if (expr === ".") return inputs;
141
+ if (expr.startsWith("select(")) {
142
+ const match = expr.match(/^select\((.+?)\s*(==|>)\s*(.+)\)$/);
143
+ if (!match) throw new Error(`\u4E0D\u652F\u6301\u7684 jq \u8868\u8FBE\u5F0F: ${expr}`);
144
+ const [, leftExpr, op, rightExpr] = match;
145
+ const expected = parseLiteral(rightExpr);
146
+ return inputs.filter((item) => {
147
+ const left = applyExpression([item], leftExpr)[0];
148
+ return op === "==" ? left === expected : Number(left) > Number(expected);
149
+ });
150
+ }
151
+ if (expr.startsWith("{") && expr.endsWith("}")) {
152
+ const body = expr.slice(1, -1).trim();
153
+ if (!body) return inputs.map(() => ({}));
154
+ const entries = splitTopLevel(body, ",");
155
+ return inputs.map((item) => {
156
+ const obj = {};
157
+ for (const entry of entries) {
158
+ const colon = entry.indexOf(":");
159
+ if (colon === -1) {
160
+ const key = entry.trim().replace(/^\./, "");
161
+ obj[key] = applyExpression([item], `.${key}`)[0];
162
+ } else {
163
+ const key = entry.slice(0, colon).trim();
164
+ const valueExpr = entry.slice(colon + 1).trim();
165
+ obj[key] = applyExpression([item], valueExpr)[0];
166
+ }
167
+ }
168
+ return obj;
169
+ });
170
+ }
171
+ if (!expr.startsWith(".")) throw new Error(`\u4E0D\u652F\u6301\u7684 jq \u8868\u8FBE\u5F0F: ${expr}`);
172
+ let current = inputs;
173
+ let remaining = expr.slice(1);
174
+ while (remaining.length > 0) {
175
+ if (remaining.startsWith("[]")) {
176
+ current = current.flatMap((item) => Array.isArray(item) ? item : []);
177
+ remaining = remaining.slice(2);
178
+ } else if (remaining.startsWith("[")) {
179
+ const match = remaining.match(/^\[(-?\d+)\]/);
180
+ if (!match) throw new Error(`\u4E0D\u652F\u6301\u7684 jq \u8868\u8FBE\u5F0F: .${remaining}`);
181
+ const index = Number(match[1]);
182
+ current = current.map((item) => {
183
+ if (!Array.isArray(item)) return void 0;
184
+ return item[index >= 0 ? index : item.length + index];
185
+ });
186
+ remaining = remaining.slice(match[0].length);
187
+ } else if (remaining.startsWith(".")) {
188
+ remaining = remaining.slice(1);
189
+ } else {
190
+ const match = remaining.match(/^([A-Za-z_][A-Za-z0-9_]*)/);
191
+ if (!match) throw new Error(`\u4E0D\u652F\u6301\u7684 jq \u8868\u8FBE\u5F0F: .${remaining}`);
192
+ const field = match[1];
193
+ current = current.map((item) => getField(item, field));
194
+ remaining = remaining.slice(field.length);
195
+ }
196
+ }
197
+ return current;
198
+ }
199
+ function applyExpression(inputs, expression) {
200
+ const segments = splitTopLevel(expression.trim(), "|");
201
+ return segments.reduce((current, segment) => applySegment(current, segment.trim()), inputs);
202
+ }
203
+ function applyJq(data, expression) {
204
+ return applyExpression([data], expression).filter((item) => item !== void 0);
205
+ }
206
+
207
+ // src/commands/site.ts
208
+ import { fileURLToPath } from "url";
209
+ import { dirname } from "path";
210
+ var BWS_DIR = join(homedir(), ".bws");
211
+ var LOCAL_SITES_DIR = join(BWS_DIR, "sites");
212
+ var __filename = fileURLToPath(import.meta.url);
213
+ var __dirname = dirname(__filename);
214
+ var BUILTIN_SITES_DIR = join(__dirname, "../sites");
215
+ function exitJsonError(error, extra = {}) {
216
+ console.log(JSON.stringify({ success: false, error, ...extra }, null, 2));
217
+ process.exit(1);
218
+ }
219
+ function parseSiteMeta(filePath, source) {
220
+ let content;
221
+ try {
222
+ content = readFileSync(filePath, "utf-8");
223
+ } catch {
224
+ return null;
225
+ }
226
+ const sitesDir = source === "local" ? LOCAL_SITES_DIR : BUILTIN_SITES_DIR;
227
+ const relPath = relative(sitesDir, filePath);
228
+ const defaultName = relPath.replace(/\.js$/, "").replace(/\\/g, "/");
229
+ const metaMatch = content.match(/\/\*\s*@meta\s*\n([\s\S]*?)\*\//);
230
+ if (metaMatch) {
231
+ try {
232
+ const metaJson = JSON.parse(metaMatch[1]);
233
+ return {
234
+ name: metaJson.name || defaultName,
235
+ description: metaJson.description || "",
236
+ domain: metaJson.domain || "",
237
+ args: metaJson.args || {},
238
+ capabilities: metaJson.capabilities,
239
+ readOnly: metaJson.readOnly,
240
+ example: metaJson.example,
241
+ filePath,
242
+ source
243
+ };
244
+ } catch {
245
+ }
246
+ }
247
+ const meta = {
248
+ name: defaultName,
249
+ description: "",
250
+ domain: "",
251
+ args: {},
252
+ filePath,
253
+ source
254
+ };
255
+ const tagPattern = /\/\/\s*@(\w+)[ \t]+(.*)/g;
256
+ let match;
257
+ while ((match = tagPattern.exec(content)) !== null) {
258
+ const [, key, value] = match;
259
+ switch (key) {
260
+ case "name":
261
+ meta.name = value.trim();
262
+ break;
263
+ case "description":
264
+ meta.description = value.trim();
265
+ break;
266
+ case "domain":
267
+ meta.domain = value.trim();
268
+ break;
269
+ case "args":
270
+ for (const arg of value.trim().split(/[,\s]+/).filter(Boolean)) {
271
+ meta.args[arg] = { required: true };
272
+ }
273
+ break;
274
+ case "example":
275
+ meta.example = value.trim();
276
+ break;
277
+ }
278
+ }
279
+ return meta;
280
+ }
281
+ function scanSites(dir, source) {
282
+ if (!existsSync(dir)) return [];
283
+ const sites = [];
284
+ function walk(currentDir) {
285
+ let entries;
286
+ try {
287
+ entries = readdirSync(currentDir, { withFileTypes: true });
288
+ } catch {
289
+ return;
290
+ }
291
+ for (const entry of entries) {
292
+ const fullPath = join(currentDir, entry.name);
293
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
294
+ walk(fullPath);
295
+ } else if (entry.isFile() && entry.name.endsWith(".js")) {
296
+ const meta = parseSiteMeta(fullPath, source);
297
+ if (meta) sites.push(meta);
298
+ }
299
+ }
300
+ }
301
+ walk(dir);
302
+ return sites;
303
+ }
304
+ function getAllSites() {
305
+ const builtin = scanSites(BUILTIN_SITES_DIR, "builtin");
306
+ const local = scanSites(LOCAL_SITES_DIR, "local");
307
+ const byName = /* @__PURE__ */ new Map();
308
+ for (const s of builtin) byName.set(s.name, s);
309
+ for (const s of local) byName.set(s.name, s);
310
+ return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
311
+ }
312
+ function siteList(options) {
313
+ const sites = getAllSites();
314
+ if (sites.length === 0) {
315
+ if (options.json) {
316
+ console.log("[]");
317
+ return;
318
+ }
319
+ console.log("\u672A\u627E\u5230\u4EFB\u4F55 site adapter\u3002");
320
+ console.log(` \u5185\u7F6E adapter \u76EE\u5F55: ${BUILTIN_SITES_DIR}`);
321
+ console.log(` \u79C1\u6709 adapter \u76EE\u5F55: ${LOCAL_SITES_DIR}`);
322
+ return;
323
+ }
324
+ if (options.json) {
325
+ console.log(JSON.stringify(sites.map((s) => ({
326
+ name: s.name,
327
+ description: s.description,
328
+ domain: s.domain,
329
+ args: s.args,
330
+ source: s.source
331
+ })), null, 2));
332
+ return;
333
+ }
334
+ const groups = /* @__PURE__ */ new Map();
335
+ for (const s of sites) {
336
+ const platform = s.name.split("/")[0];
337
+ if (!groups.has(platform)) groups.set(platform, []);
338
+ groups.get(platform).push(s);
339
+ }
340
+ for (const [platform, items] of groups) {
341
+ console.log(`
342
+ ${platform}/`);
343
+ for (const s of items) {
344
+ const cmd = s.name.split("/").slice(1).join("/");
345
+ const src = s.source === "local" ? " (local)" : "";
346
+ const desc = s.description ? ` - ${s.description}` : "";
347
+ console.log(` ${cmd.padEnd(20)}${desc}${src}`);
348
+ }
349
+ }
350
+ console.log();
351
+ }
352
+ function siteSearch(query, options) {
353
+ const sites = getAllSites();
354
+ const q = query.toLowerCase();
355
+ const matches = sites.filter(
356
+ (s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.domain.toLowerCase().includes(q)
357
+ );
358
+ if (matches.length === 0) {
359
+ if (options.json) {
360
+ console.log("[]");
361
+ return;
362
+ }
363
+ console.log(`\u672A\u627E\u5230\u5339\u914D "${query}" \u7684 adapter\u3002`);
364
+ console.log(" \u67E5\u770B\u6240\u6709: bws site list");
365
+ return;
366
+ }
367
+ if (options.json) {
368
+ console.log(JSON.stringify(matches.map((s) => ({
369
+ name: s.name,
370
+ description: s.description,
371
+ domain: s.domain,
372
+ source: s.source
373
+ })), null, 2));
374
+ return;
375
+ }
376
+ for (const s of matches) {
377
+ const src = s.source === "local" ? " (local)" : "";
378
+ console.log(`${s.name.padEnd(24)} ${s.description}${src}`);
379
+ }
380
+ }
381
+ function findSiteByName(name) {
382
+ return getAllSites().find((site) => site.name === name);
383
+ }
384
+ function siteInfo(name, options) {
385
+ const site = findSiteByName(name);
386
+ if (!site) {
387
+ if (options.json) {
388
+ exitJsonError(`adapter "${name}" not found`, { action: "bws site list" });
389
+ }
390
+ console.error(`[error] site info: adapter "${name}" not found.`);
391
+ console.error(" Try: bws site list");
392
+ process.exit(1);
393
+ }
394
+ const meta = {
395
+ name: site.name,
396
+ description: site.description,
397
+ domain: site.domain,
398
+ args: site.args,
399
+ example: site.example,
400
+ readOnly: site.readOnly
401
+ };
402
+ if (options.json) {
403
+ console.log(JSON.stringify(meta, null, 2));
404
+ return;
405
+ }
406
+ console.log(`${site.name} \u2014 ${site.description}`);
407
+ console.log();
408
+ console.log("\u53C2\u6570\uFF1A");
409
+ const argEntries = Object.entries(site.args);
410
+ if (argEntries.length === 0) {
411
+ console.log(" \uFF08\u65E0\uFF09");
412
+ } else {
413
+ for (const [argName, argDef] of argEntries) {
414
+ const requiredText = argDef.required ? "\u5FC5\u586B" : "\u53EF\u9009";
415
+ const description = argDef.description || "";
416
+ console.log(` ${argName} (${requiredText}) ${description}`.trimEnd());
417
+ }
418
+ }
419
+ console.log();
420
+ console.log("\u793A\u4F8B\uFF1A");
421
+ console.log(` ${site.example || `bws site ${site.name}`}`);
422
+ console.log();
423
+ console.log(`\u57DF\u540D\uFF1A${site.domain || "\uFF08\u672A\u58F0\u660E\uFF09"}`);
424
+ console.log(`\u53EA\u8BFB\uFF1A${site.readOnly ? "\u662F" : "\u5426"}`);
425
+ }
426
+ async function siteRun(name, args, options) {
427
+ const sites = getAllSites();
428
+ const site = sites.find((s) => s.name === name);
429
+ if (!site) {
430
+ const fuzzy = sites.filter((s) => s.name.includes(name));
431
+ if (options.json) {
432
+ exitJsonError(`site "${name}" not found`, {
433
+ suggestions: fuzzy.slice(0, 5).map((s) => s.name),
434
+ action: fuzzy.length > 0 ? void 0 : "bws site update"
435
+ });
436
+ }
437
+ console.error(`[error] site: "${name}" not found.`);
438
+ if (fuzzy.length > 0) {
439
+ console.error(" Did you mean:");
440
+ for (const s of fuzzy.slice(0, 5)) {
441
+ console.error(` bws site ${s.name}`);
442
+ }
443
+ } else {
444
+ console.error(" Try: bws site list");
445
+ console.error(" Or: bws site update");
446
+ }
447
+ process.exit(1);
448
+ }
449
+ const argNames = Object.keys(site.args);
450
+ const argMap = {};
451
+ const positionalArgs = [];
452
+ for (let i = 0; i < args.length; i++) {
453
+ if (args[i].startsWith("--")) {
454
+ const flagName = args[i].slice(2);
455
+ if (flagName in site.args && args[i + 1]) {
456
+ argMap[flagName] = args[i + 1];
457
+ i++;
458
+ }
459
+ } else {
460
+ positionalArgs.push(args[i]);
461
+ }
462
+ }
463
+ let posIdx = 0;
464
+ for (const argName of argNames) {
465
+ if (!argMap[argName] && posIdx < positionalArgs.length) {
466
+ argMap[argName] = positionalArgs[posIdx++];
467
+ }
468
+ }
469
+ for (const [argName, argDef] of Object.entries(site.args)) {
470
+ if (argDef.required && !argMap[argName]) {
471
+ const usage = argNames.map((a) => {
472
+ const def = site.args[a];
473
+ return def.required ? `<${a}>` : `[${a}]`;
474
+ }).join(" ");
475
+ if (options.json) {
476
+ exitJsonError(`missing required argument "${argName}"`, {
477
+ usage: `bws site ${name} ${usage}`,
478
+ example: site.example
479
+ });
480
+ }
481
+ console.error(`[error] site ${name}: missing required argument "${argName}".`);
482
+ console.error(` Usage: bws site ${name} ${usage}`);
483
+ if (site.example) console.error(` Example: ${site.example}`);
484
+ process.exit(1);
485
+ }
486
+ }
487
+ const jsContent = readFileSync(site.filePath, "utf-8");
488
+ const jsBody = jsContent.replace(/\/\*\s*@meta[\s\S]*?\*\//, "").trim();
489
+ const argsJson = JSON.stringify(argMap);
490
+ let targetId;
491
+ if (site.domain) {
492
+ const tabs = ocGetTabs();
493
+ const existing = ocFindTabByDomain(tabs, site.domain);
494
+ if (existing) {
495
+ targetId = existing.targetId;
496
+ } else {
497
+ targetId = ocOpenTab(`https://${site.domain}`);
498
+ await new Promise((resolve) => setTimeout(resolve, 3e3));
499
+ }
500
+ } else {
501
+ const tabs = ocGetTabs();
502
+ if (tabs.length === 0) {
503
+ throw new Error("No tabs open in OpenClaw browser");
504
+ }
505
+ targetId = tabs[0].targetId;
506
+ }
507
+ const wrappedFn = `async () => { const __fn = ${jsBody}; return await __fn(${argsJson}); }`;
508
+ const parsed = ocEvaluate(targetId, wrappedFn);
509
+ if (typeof parsed === "object" && parsed !== null && "error" in parsed) {
510
+ const errObj = parsed;
511
+ const checkText = `${errObj.error} ${errObj.hint || ""}`;
512
+ const isAuthError = /401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth/i.test(checkText);
513
+ const loginHint = isAuthError && site.domain ? `Please log in to https://${site.domain} in your OpenClaw browser first, then retry.` : void 0;
514
+ const hint = loginHint || errObj.hint;
515
+ if (options.json) {
516
+ console.log(JSON.stringify({ success: false, error: errObj.error, hint }));
517
+ } else {
518
+ console.error(`[error] site ${name}: ${errObj.error}`);
519
+ if (hint) console.error(` Hint: ${hint}`);
520
+ }
521
+ process.exit(1);
522
+ }
523
+ if (options.jq) {
524
+ const expr = options.jq.replace(/^\.data\./, ".");
525
+ const results = applyJq(parsed, expr);
526
+ for (const r of results) {
527
+ console.log(typeof r === "string" ? r : JSON.stringify(r));
528
+ }
529
+ } else if (options.json) {
530
+ console.log(JSON.stringify({ success: true, data: parsed }));
531
+ } else {
532
+ console.log(JSON.stringify(parsed, null, 2));
533
+ }
534
+ }
535
+ async function siteCommand(args, options = {}) {
536
+ const subCommand = args[0];
537
+ if (!subCommand || subCommand === "--help" || subCommand === "-h") {
538
+ console.log(`bws site - \u7F51\u7AD9 CLI \u5316
539
+
540
+ \u7528\u6CD5:
541
+ bws site list \u5217\u51FA\u6240\u6709\u53EF\u7528 adapter
542
+ bws site info <name> \u67E5\u770B adapter \u8BE6\u60C5
543
+ bws site search <query> \u641C\u7D22 adapter
544
+ bws site <name> [args...] \u8FD0\u884C adapter
545
+
546
+ \u793A\u4F8B:
547
+ bws site list
548
+ bws site zhihu/hot
549
+ bws site xiaohongshu/search "\u65C5\u884C"
550
+ bws site bilibili/popular`);
551
+ return;
552
+ }
553
+ switch (subCommand) {
554
+ case "list":
555
+ siteList(options);
556
+ break;
557
+ case "search":
558
+ if (!args[1]) {
559
+ console.error("[error] site search: <query> is required.");
560
+ console.error(" Usage: bws site search <query>");
561
+ process.exit(1);
562
+ }
563
+ siteSearch(args[1], options);
564
+ break;
565
+ case "info":
566
+ if (!args[1]) {
567
+ console.error("[error] site info: <name> is required.");
568
+ console.error(" Usage: bws site info <name>");
569
+ process.exit(1);
570
+ }
571
+ siteInfo(args[1], options);
572
+ break;
573
+ case "run":
574
+ if (!args[1]) {
575
+ console.error("[error] site run: <name> is required.");
576
+ console.error(" Usage: bws site run <name> [args...]");
577
+ console.error(" Try: bws site list");
578
+ process.exit(1);
579
+ }
580
+ await siteRun(args[1], args.slice(2), options);
581
+ break;
582
+ default:
583
+ if (subCommand.includes("/")) {
584
+ await siteRun(subCommand, args.slice(1), options);
585
+ } else {
586
+ console.error(`[error] site: unknown subcommand "${subCommand}".`);
587
+ console.error(" Available: list, info, search, run");
588
+ console.error(" Try: bws site --help");
589
+ process.exit(1);
590
+ }
591
+ break;
592
+ }
593
+ }
594
+
595
+ // src/index.ts
596
+ var VERSION = "0.1.0";
597
+ var HELP_TEXT = `
598
+ Browser Web Search (BWS) v${VERSION}
599
+
600
+ \u628A\u4EFB\u4F55\u7F51\u7AD9\u53D8\u6210\u547D\u4EE4\u884C API\uFF0C\u4F7F\u7528 OpenClaw \u6D4F\u89C8\u5668 + \u7528\u6237\u767B\u5F55\u6001\u3002
601
+
602
+ \u7528\u6CD5:
603
+ bws site list \u5217\u51FA\u6240\u6709\u53EF\u7528 adapter
604
+ bws site info <name> \u67E5\u770B adapter \u5143\u4FE1\u606F
605
+ bws site search <query> \u641C\u7D22 adapter
606
+ bws site <name> [args...] \u8FD0\u884C adapter
607
+ bws <name> [args...] \u8FD0\u884C adapter\uFF08\u7B80\u5199\uFF09
608
+
609
+ \u793A\u4F8B:
610
+ bws site list # \u67E5\u770B\u6240\u6709\u53EF\u7528\u547D\u4EE4
611
+ bws zhihu/hot # \u77E5\u4E4E\u70ED\u699C
612
+ bws xiaohongshu/search "\u65C5\u884C" # \u5C0F\u7EA2\u4E66\u641C\u7D22
613
+ bws bilibili/popular # B\u7AD9\u70ED\u95E8
614
+
615
+ \u9009\u9879:
616
+ --json \u4EE5 JSON \u683C\u5F0F\u8F93\u51FA
617
+ --jq <expr> \u5BF9 JSON \u8F93\u51FA\u5E94\u7528 jq \u8FC7\u6EE4
618
+ --help, -h \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
619
+ --version, -v \u663E\u793A\u7248\u672C\u53F7
620
+
621
+ \u9700\u8981 OpenClaw \u73AF\u5883\u8FD0\u884C\u3002
622
+ `.trim();
623
+ function parseArgs(args) {
624
+ const result = {
625
+ command: "",
626
+ args: [],
627
+ flags: {}
628
+ };
629
+ let skipNext = false;
630
+ for (let i = 0; i < args.length; i++) {
631
+ if (skipNext) {
632
+ skipNext = false;
633
+ continue;
634
+ }
635
+ const arg = args[i];
636
+ if (arg === "--json") {
637
+ result.flags.json = true;
638
+ } else if (arg === "--jq") {
639
+ skipNext = true;
640
+ const nextIdx = i + 1;
641
+ if (nextIdx < args.length) {
642
+ result.flags.jq = args[nextIdx];
643
+ result.flags.json = true;
644
+ }
645
+ } else if (arg === "--help" || arg === "-h") {
646
+ console.log(HELP_TEXT);
647
+ process.exit(0);
648
+ } else if (arg === "--version" || arg === "-v") {
649
+ console.log(VERSION);
650
+ process.exit(0);
651
+ } else if (!result.command) {
652
+ result.command = arg;
653
+ } else {
654
+ result.args.push(arg);
655
+ }
656
+ }
657
+ return result;
658
+ }
659
+ async function main() {
660
+ const args = process.argv.slice(2);
661
+ if (args.length === 0) {
662
+ console.log(HELP_TEXT);
663
+ process.exit(0);
664
+ }
665
+ const parsed = parseArgs(args);
666
+ switch (parsed.command) {
667
+ case "site":
668
+ await siteCommand(parsed.args, {
669
+ json: parsed.flags.json,
670
+ jq: parsed.flags.jq
671
+ });
672
+ break;
673
+ default:
674
+ if (parsed.command.includes("/")) {
675
+ await siteCommand([parsed.command, ...parsed.args], {
676
+ json: parsed.flags.json,
677
+ jq: parsed.flags.jq
678
+ });
679
+ } else {
680
+ console.error(`[error] \u672A\u77E5\u547D\u4EE4: ${parsed.command}`);
681
+ console.error(" \u8FD0\u884C bws --help \u67E5\u770B\u5E2E\u52A9");
682
+ process.exit(1);
683
+ }
684
+ break;
685
+ }
686
+ }
687
+ main().catch((error) => {
688
+ console.error(`[error] ${error.message || error}`);
689
+ process.exit(1);
690
+ });
691
+ //# sourceMappingURL=index.js.map