browser-web-search 0.2.0 → 0.2.2

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 (42) hide show
  1. package/README.md +42 -64
  2. package/dist/index.js +15 -1
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/sites/36kr/newsflash.js +1 -1
  6. package/sites/baidu/search.js +1 -1
  7. package/sites/bilibili/comments.js +1 -1
  8. package/sites/bilibili/feed.js +1 -1
  9. package/sites/bilibili/history.js +1 -1
  10. package/sites/bilibili/me.js +1 -1
  11. package/sites/bilibili/popular.js +1 -1
  12. package/sites/bilibili/ranking.js +1 -1
  13. package/sites/bilibili/search.js +1 -1
  14. package/sites/bilibili/trending.js +1 -1
  15. package/sites/bilibili/video.js +1 -1
  16. package/sites/bing/search.js +1 -1
  17. package/sites/boss/detail.js +1 -1
  18. package/sites/boss/search.js +1 -1
  19. package/sites/cnblogs/search.js +1 -1
  20. package/sites/csdn/search.js +1 -1
  21. package/sites/douban/comments.js +1 -1
  22. package/sites/douban/movie-hot.js +1 -1
  23. package/sites/douban/movie-top.js +1 -1
  24. package/sites/douban/movie.js +1 -1
  25. package/sites/douban/search.js +1 -1
  26. package/sites/douban/top250.js +1 -1
  27. package/sites/github/fork.js +1 -1
  28. package/sites/github/issue-create.js +1 -1
  29. package/sites/github/issues.js +1 -1
  30. package/sites/github/pr-create.js +1 -1
  31. package/sites/github/repo.js +2 -2
  32. package/sites/google/search.js +1 -1
  33. package/sites/toutiao/hot.js +3 -3
  34. package/sites/toutiao/search.js +3 -3
  35. package/sites/xiaohongshu/comments.js +1 -1
  36. package/sites/xiaohongshu/note.js +1 -1
  37. package/sites/xiaohongshu/search.js +1 -1
  38. package/sites/xiaohongshu/user_posts.js +1 -1
  39. package/sites/zhihu/hot.js +1 -1
  40. package/sites/zhihu/me.js +1 -1
  41. package/sites/zhihu/question.js +1 -1
  42. package/sites/zhihu/search.js +1 -1
package/README.md CHANGED
@@ -1,28 +1,13 @@
1
1
  # Browser Web Search (BWS)
2
2
 
3
- **把任何网站变成命令行 API**
3
+ **把任何网站变成命令行 API** — 专为 OpenClaw 设计的轻量级浏览器自动化工具
4
4
 
5
- ## 项目特点
5
+ ## 特点
6
6
 
7
- ### 🎯 专为 OpenClaw 设计
8
- - **零配置**:无需 Chrome Extension、无需 Daemon,开箱即用
9
- - **深度集成**:直接使用 OpenClaw 浏览器,与其他 Skill 共享登录态
10
- - **轻量精简**:核心代码仅 22KB,无运行时依赖
11
-
12
- ### 🔐 复用登录态
13
- - **无需 API Key**:使用你在浏览器中的登录状态
14
- - **绕过反爬**:请求来自真实浏览器,不会被封禁
15
- - **隐私安全**:数据在本地处理,不经过第三方服务器
16
-
17
- ### 📦 内置 Adapter
18
- - **13 个平台,41 个命令**:开箱即用
19
- - **可扩展**:支持添加私有 adapter 到 `~/.bws/sites/`
20
- - **易维护**:adapter 是简单的 JS 文件,易于修改和调试
21
-
22
- ### 🤖 AI Agent 友好
23
- - **结构化输出**:所有命令返回 JSON,便于 AI 解析
24
- - **jq 过滤**:内置 jq 支持,精确提取所需数据
25
- - **错误提示**:清晰的错误信息和修复建议
7
+ - **零配置** 无需 Chrome Extension、无需 Daemon,开箱即用
8
+ - **复用登录态** 使用浏览器中的登录状态,无需 API Key
9
+ - **13 个平台,41 个命令** 覆盖搜索、社交、新闻、开发、视频、招聘等场景
10
+ - **AI Agent 友好** — 结构化 JSON 输出,内置 jq 过滤
26
11
 
27
12
  ## 安装
28
13
 
@@ -33,74 +18,68 @@ npm install -g browser-web-search
33
18
  ## 快速开始
34
19
 
35
20
  ```bash
36
- # 查看所有可用命令
37
- bws site list
38
-
39
- # 运行
40
- bws site zhihu/hot # 知乎热榜
21
+ bws site list # 查看所有命令
22
+ bws site toutiao/hot # 今日头条热榜
41
23
  bws site xiaohongshu/search "旅行" # 小红书搜索
42
- bws site github/repo owner/repo # GitHub 仓库信息
24
+ bws site zhihu/hot # 知乎热榜
43
25
  bws site bilibili/popular # B站热门视频
44
26
  ```
45
27
 
46
- ## 内置平台
47
-
48
- | 分类 | 平台 | 命令数 |
49
- |-----|-----|-------|
50
- | **搜索** | Google, Baidu, Bing | 3 |
51
- | **社交** | 小红书, 知乎 | 10 |
52
- | **新闻** | 36kr, 今日头条 | 3 |
53
- | **开发** | GitHub, CSDN, 博客园 | 8 |
54
- | **视频** | Bilibili | 9 |
55
- | **娱乐** | 豆瓣 | 6 |
56
- | **招聘** | BOSS直聘 | 2 |
57
-
58
- ## 命令
28
+ ## 内置平台(13 个)
29
+
30
+ | 平台 | 说明 | 命令 |
31
+ |-----|------|-----|
32
+ | **今日头条** | 新闻资讯 | `toutiao/hot`, `toutiao/search` |
33
+ | **小红书** | 生活分享 | `xiaohongshu/search`, `xiaohongshu/note`, `xiaohongshu/comments`, `xiaohongshu/user_posts` |
34
+ | **36kr** | 科技创投 | `36kr/newsflash` |
35
+ | **知乎** | 问答社区 | `zhihu/hot`, `zhihu/search`, `zhihu/question`, `zhihu/me` |
36
+ | **CSDN** | 开发者社区 | `csdn/search` |
37
+ | **博客园** | 技术博客 | `cnblogs/search` |
38
+ | **豆瓣** | 影视书籍 | `douban/movie`, `douban/search`, `douban/top250`, `douban/movie-hot`, `douban/movie-top`, `douban/comments` |
39
+ | **Bilibili** | 视频弹幕 | `bilibili/popular`, `bilibili/trending`, `bilibili/ranking`, `bilibili/search`, `bilibili/video`, `bilibili/comments`, `bilibili/feed`, `bilibili/history`, `bilibili/me` |
40
+ | **BOSS直聘** | 招聘平台 | `boss/search`, `boss/detail` |
41
+ | **GitHub** | 代码托管 | `github/repo`, `github/issues`, `github/fork`, `github/pr-create`, `github/issue-create` |
42
+ | **Baidu** | 百度搜索 | `baidu/search` |
43
+ | **Bing** | 必应搜索 | `bing/search` |
44
+ | **Google** | 谷歌搜索 | `google/search` |
45
+
46
+ ## 命令参考
59
47
 
60
48
  ```bash
61
49
  bws site list # 列出所有 adapter
62
- bws site search <query> # 搜索 adapter
63
50
  bws site info <name> # 查看 adapter 详情
64
51
  bws site <name> [args...] # 运行 adapter
65
- ```
66
-
67
- ## 选项
68
-
69
- ```bash
70
- --json # JSON 格式输出
71
- --jq <expr> # jq 过滤表达式
52
+ bws site <name> --json # JSON 格式输出
53
+ bws site <name> --jq <expr> # jq 过滤表达式
72
54
  ```
73
55
 
74
56
  ## 示例
75
57
 
76
58
  ```bash
77
- # 获取知乎热榜
78
- bws site zhihu/hot
59
+ # 知乎热榜前 10
60
+ bws site zhihu/hot 10
79
61
 
80
- # 搜索小红书并过滤标题
62
+ # 小红书搜索,只取标题
81
63
  bws site xiaohongshu/search "美食" --jq '.notes[].title'
82
64
 
83
- # 获取 B站热门视频
65
+ # B站热门视频 JSON
84
66
  bws site bilibili/popular --json
85
67
 
86
- # 搜索 GitHub 仓库
68
+ # GitHub 仓库信息
87
69
  bws site github/repo facebook/react
88
70
  ```
89
71
 
90
- ## 登录态管理
72
+ ## 登录态
91
73
 
92
- adapter 运行在 OpenClaw 浏览器中。如果网站需要登录:
74
+ 需要登录的网站,先在 OpenClaw 浏览器中登录:
93
75
 
94
- 1. 命令会返回 401/403 错误
95
- 2. OpenClaw 浏览器中打开网站并登录:
96
- ```bash
97
- openclaw browser open https://xiaohongshu.com
98
- ```
99
- 3. 重试命令
76
+ ```bash
77
+ openclaw browser open https://xiaohongshu.com
78
+ ```
100
79
 
101
- ## 添加私有 Adapter
80
+ ## 自定义 Adapter
102
81
 
103
- 将自定义 adapter 放到 `~/.bws/sites/` 目录:
82
+ adapter 放到 `~/.bws/sites/` 目录:
104
83
 
105
84
  ```javascript
106
85
  /* @meta
@@ -113,7 +92,6 @@ adapter 运行在 OpenClaw 浏览器中。如果网站需要登录:
113
92
  }
114
93
  */
115
94
  async function(args) {
116
- // 在浏览器上下文中执行
117
95
  const results = document.querySelectorAll('.result');
118
96
  return Array.from(results).map(el => ({
119
97
  title: el.querySelector('.title').textContent,
package/dist/index.js CHANGED
@@ -58,12 +58,26 @@ Raw (preview): ${buildPreview(trimmed)}`);
58
58
  // src/openclaw-bridge.ts
59
59
  var OPENCLAW_EVALUATE_TIMEOUT_MS = 12e4;
60
60
  var EXEC_TIMEOUT_BUFFER_MS = 5e3;
61
+ var GLOBAL_FLAGS = /* @__PURE__ */ new Set(["--json", "--timeout"]);
61
62
  function buildOpenClawArgs(args, timeout) {
62
63
  const [subcommand, ...rest] = args;
63
64
  if (!subcommand) {
64
65
  throw new Error("OpenClaw browser command requires a subcommand");
65
66
  }
66
- return ["openclaw", "browser", subcommand, "--timeout", String(timeout), ...rest];
67
+ const globalFlags = ["--timeout", String(timeout)];
68
+ const subcommandArgs = [];
69
+ for (let i = 0; i < rest.length; i++) {
70
+ const arg = rest[i];
71
+ if (GLOBAL_FLAGS.has(arg)) {
72
+ globalFlags.push(arg);
73
+ if (arg !== "--json" && i + 1 < rest.length) {
74
+ globalFlags.push(rest[++i]);
75
+ }
76
+ } else {
77
+ subcommandArgs.push(arg);
78
+ }
79
+ }
80
+ return ["openclaw", "browser", ...globalFlags, subcommand, ...subcommandArgs];
67
81
  }
68
82
  function getOpenClawExecTimeout(timeout) {
69
83
  return timeout + EXEC_TIMEOUT_BUFFER_MS;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/site.ts","../src/openclaw-bridge.ts","../src/openclaw-json.ts","../src/jq.ts","../src/index.ts"],"sourcesContent":["/**\n * site 命令 - 管理和运行网站适配器\n *\n * 用法:\n * bws site list 列出所有可用 adapter\n * bws site search <query> 搜索 adapter\n * bws site info <name> 查看 adapter 详情\n * bws site <name> [args...] 运行 adapter\n *\n * 目录:\n * 内置 adapter:随项目发布\n * ~/.bws/sites/:用户私有 adapter(优先级更高)\n */\n\nimport { readFileSync, readdirSync, existsSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ocGetTabs, ocFindTabByDomain, ocOpenTab, ocEvaluate } from \"../openclaw-bridge.js\";\nimport { applyJq } from \"../jq.js\";\n\nconst BWS_DIR = join(homedir(), \".bws\");\nconst LOCAL_SITES_DIR = join(BWS_DIR, \"sites\"); // 用户私有 adapter(优先级最高)\n\n// 内置 adapter 目录(项目自带)\nimport { fileURLToPath } from \"node:url\";\nimport { dirname } from \"node:path\";\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst BUILTIN_SITES_DIR = join(__dirname, \"../sites\"); // 项目内置 adapter\n\nexport interface SiteOptions {\n json?: boolean;\n jq?: string;\n}\n\n/** Adapter 参数定义 */\ninterface ArgDef {\n required?: boolean;\n description?: string;\n}\n\n/** Adapter 元数据 */\ninterface SiteMeta {\n name: string;\n description: string;\n domain: string;\n args: Record<string, ArgDef>;\n capabilities?: string[];\n readOnly?: boolean;\n example?: string;\n filePath: string;\n source: \"local\" | \"builtin\";\n}\n\nfunction exitJsonError(error: string, extra: Record<string, unknown> = {}): never {\n console.log(JSON.stringify({ success: false, error, ...extra }, null, 2));\n process.exit(1);\n}\n\n/**\n * 从 JS 文件的 /* @meta JSON * / 块解析元数据\n */\nfunction parseSiteMeta(filePath: string, source: \"local\" | \"builtin\"): SiteMeta | null {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n // 从文件路径推断默认 name\n const sitesDir = source === \"local\" ? LOCAL_SITES_DIR : BUILTIN_SITES_DIR;\n const relPath = relative(sitesDir, filePath);\n const defaultName = relPath.replace(/\\.js$/, \"\").replace(/\\\\/g, \"/\");\n\n // 解析 /* @meta { ... } */ 块\n const metaMatch = content.match(/\\/\\*\\s*@meta\\s*\\n([\\s\\S]*?)\\*\\//);\n if (metaMatch) {\n try {\n const metaJson = JSON.parse(metaMatch[1]);\n return {\n name: metaJson.name || defaultName,\n description: metaJson.description || \"\",\n domain: metaJson.domain || \"\",\n args: metaJson.args || {},\n capabilities: metaJson.capabilities,\n readOnly: metaJson.readOnly,\n example: metaJson.example,\n filePath,\n source,\n };\n } catch {\n // JSON 解析失败,回退到 @tag 模式\n }\n }\n\n // 回退:解析 // @tag 格式(兼容旧格式)\n const meta: SiteMeta = {\n name: defaultName,\n description: \"\",\n domain: \"\",\n args: {},\n filePath,\n source,\n };\n\n const tagPattern = /\\/\\/\\s*@(\\w+)[ \\t]+(.*)/g;\n let match;\n while ((match = tagPattern.exec(content)) !== null) {\n const [, key, value] = match;\n switch (key) {\n case \"name\": meta.name = value.trim(); break;\n case \"description\": meta.description = value.trim(); break;\n case \"domain\": meta.domain = value.trim(); break;\n case \"args\":\n for (const arg of value.trim().split(/[,\\s]+/).filter(Boolean)) {\n meta.args[arg] = { required: true };\n }\n break;\n case \"example\": meta.example = value.trim(); break;\n }\n }\n\n return meta;\n}\n\n/**\n * 扫描目录下所有 .js 文件\n */\nfunction scanSites(dir: string, source: \"local\" | \"builtin\"): SiteMeta[] {\n if (!existsSync(dir)) return [];\n const sites: SiteMeta[] = [];\n\n function walk(currentDir: string): void {\n let entries;\n try { entries = readdirSync(currentDir, { withFileTypes: true }); } catch { return; }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory() && !entry.name.startsWith(\".\")) {\n walk(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".js\")) {\n const meta = parseSiteMeta(fullPath, source);\n if (meta) sites.push(meta);\n }\n }\n }\n\n walk(dir);\n return sites;\n}\n\n/**\n * 获取所有 adapter(用户私有 > 内置)\n */\nfunction getAllSites(): SiteMeta[] {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n\n const byName = new Map<string, SiteMeta>();\n for (const s of builtin) byName.set(s.name, s);\n for (const s of local) byName.set(s.name, s); // 用户私有覆盖内置\n\n return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));\n}\n\n// ── 子命令 ──────────────────────────────────────────────────────\n\nfunction siteList(options: SiteOptions): void {\n const sites = getAllSites();\n\n if (sites.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(\"未找到任何 site adapter。\");\n console.log(` 内置 adapter 目录: ${BUILTIN_SITES_DIR}`);\n console.log(` 私有 adapter 目录: ${LOCAL_SITES_DIR}`);\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(sites.map(s => ({\n name: s.name, description: s.description, domain: s.domain,\n args: s.args, source: s.source,\n })), null, 2));\n return;\n }\n\n const groups = new Map<string, SiteMeta[]>();\n for (const s of sites) {\n const platform = s.name.split(\"/\")[0];\n if (!groups.has(platform)) groups.set(platform, []);\n groups.get(platform)!.push(s);\n }\n\n for (const [platform, items] of groups) {\n console.log(`\\n${platform}/`);\n for (const s of items) {\n const cmd = s.name.split(\"/\").slice(1).join(\"/\");\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n const desc = s.description ? ` - ${s.description}` : \"\";\n console.log(` ${cmd.padEnd(20)}${desc}${src}`);\n }\n }\n console.log();\n}\n\nfunction siteSearch(query: string, options: SiteOptions): void {\n const sites = getAllSites();\n const q = query.toLowerCase();\n const matches = sites.filter(s =>\n s.name.toLowerCase().includes(q) ||\n s.description.toLowerCase().includes(q) ||\n s.domain.toLowerCase().includes(q)\n );\n\n if (matches.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(`未找到匹配 \"${query}\" 的 adapter。`);\n console.log(\" 查看所有: bws site list\");\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(matches.map(s => ({\n name: s.name, description: s.description, domain: s.domain, source: s.source,\n })), null, 2));\n return;\n }\n\n for (const s of matches) {\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n console.log(`${s.name.padEnd(24)} ${s.description}${src}`);\n }\n}\n\nfunction siteInfo_showDirs(options: SiteOptions): void {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n \n if (options.json) {\n console.log(JSON.stringify({\n builtinDir: BUILTIN_SITES_DIR,\n localDir: LOCAL_SITES_DIR,\n builtinCount: builtin.length,\n localCount: local.length,\n }, null, 2));\n return;\n }\n \n console.log(`内置 adapter: ${BUILTIN_SITES_DIR} (${builtin.length} 个)`);\n console.log(`私有 adapter: ${LOCAL_SITES_DIR} (${local.length} 个)`);\n}\n\nfunction findSiteByName(name: string): SiteMeta | undefined {\n return getAllSites().find((site) => site.name === name);\n}\n\nfunction siteInfo(name: string, options: SiteOptions): void {\n const site = findSiteByName(name);\n\n if (!site) {\n if (options.json) {\n exitJsonError(`adapter \"${name}\" not found`, { action: \"bws site list\" });\n }\n console.error(`[error] site info: adapter \"${name}\" not found.`);\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n\n const meta = {\n name: site.name,\n description: site.description,\n domain: site.domain,\n args: site.args,\n example: site.example,\n readOnly: site.readOnly,\n };\n\n if (options.json) {\n console.log(JSON.stringify(meta, null, 2));\n return;\n }\n\n console.log(`${site.name} — ${site.description}`);\n console.log();\n console.log(\"参数:\");\n\n const argEntries = Object.entries(site.args);\n if (argEntries.length === 0) {\n console.log(\" (无)\");\n } else {\n for (const [argName, argDef] of argEntries) {\n const requiredText = argDef.required ? \"必填\" : \"可选\";\n const description = argDef.description || \"\";\n console.log(` ${argName} (${requiredText}) ${description}`.trimEnd());\n }\n }\n\n console.log();\n console.log(\"示例:\");\n console.log(` ${site.example || `bws site ${site.name}`}`);\n console.log();\n console.log(`域名:${site.domain || \"(未声明)\"}`);\n console.log(`只读:${site.readOnly ? \"是\" : \"否\"}`);\n}\n\nasync function siteRun(\n name: string,\n args: string[],\n options: SiteOptions\n): Promise<void> {\n const sites = getAllSites();\n const site = sites.find(s => s.name === name);\n\n if (!site) {\n const fuzzy = sites.filter(s => s.name.includes(name));\n if (options.json) {\n exitJsonError(`site \"${name}\" not found`, {\n suggestions: fuzzy.slice(0, 5).map(s => s.name),\n action: fuzzy.length > 0 ? undefined : \"bws site update\",\n });\n }\n console.error(`[error] site: \"${name}\" not found.`);\n if (fuzzy.length > 0) {\n console.error(\" Did you mean:\");\n for (const s of fuzzy.slice(0, 5)) {\n console.error(` bws site ${s.name}`);\n }\n } else {\n console.error(\" Try: bws site list\");\n console.error(\" Or: bws site update\");\n }\n process.exit(1);\n }\n\n // 解析参数\n const argNames = Object.keys(site.args);\n const argMap: Record<string, string> = {};\n\n // 过滤掉 --flag value 对,收集位置参数\n const positionalArgs: string[] = [];\n for (let i = 0; i < args.length; i++) {\n if (args[i].startsWith(\"--\")) {\n const flagName = args[i].slice(2);\n if (flagName in site.args && args[i + 1]) {\n argMap[flagName] = args[i + 1];\n i++; // 跳过值\n }\n } else {\n positionalArgs.push(args[i]);\n }\n }\n\n // 位置参数按 argNames 顺序填入(跳过已通过 --flag 提供的)\n let posIdx = 0;\n for (const argName of argNames) {\n if (!argMap[argName] && posIdx < positionalArgs.length) {\n argMap[argName] = positionalArgs[posIdx++];\n }\n }\n\n // 只检查 required 参数\n for (const [argName, argDef] of Object.entries(site.args)) {\n if (argDef.required && !argMap[argName]) {\n const usage = argNames.map(a => {\n const def = site.args[a];\n return def.required ? `<${a}>` : `[${a}]`;\n }).join(\" \");\n if (options.json) {\n exitJsonError(`missing required argument \"${argName}\"`, {\n usage: `bws site ${name} ${usage}`,\n example: site.example,\n });\n }\n console.error(`[error] site ${name}: missing required argument \"${argName}\".`);\n console.error(` Usage: bws site ${name} ${usage}`);\n if (site.example) console.error(` Example: ${site.example}`);\n process.exit(1);\n }\n }\n\n // 读取并解析 JS\n const jsContent = readFileSync(site.filePath, \"utf-8\");\n\n // 移除 /* @meta ... */ 块,保留函数体\n const jsBody = jsContent.replace(/\\/\\*\\s*@meta[\\s\\S]*?\\*\\//, \"\").trim();\n\n // 构造执行脚本\n const argsJson = JSON.stringify(argMap);\n\n // OpenClaw 模式执行\n let targetId: string;\n\n if (site.domain) {\n const tabs = ocGetTabs();\n const existing = ocFindTabByDomain(tabs, site.domain);\n if (existing) {\n targetId = existing.targetId;\n } else {\n targetId = ocOpenTab(`https://${site.domain}`);\n await new Promise((resolve) => setTimeout(resolve, 3000));\n }\n } else {\n const tabs = ocGetTabs();\n if (tabs.length === 0) {\n throw new Error(\"No tabs open in OpenClaw browser\");\n }\n targetId = tabs[0].targetId;\n }\n\n const wrappedFn = `async () => { const __fn = ${jsBody}; return await __fn(${argsJson}); }`;\n const parsed = ocEvaluate(targetId, wrappedFn);\n\n if (typeof parsed === \"object\" && parsed !== null && \"error\" in parsed) {\n const errObj = parsed as { error: string; hint?: string };\n const checkText = `${errObj.error} ${errObj.hint || \"\"}`;\n const isAuthError = /401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth/i.test(checkText);\n const loginHint = isAuthError && site.domain\n ? `Please log in to https://${site.domain} in your OpenClaw browser first, then retry.`\n : undefined;\n const hint = loginHint || errObj.hint;\n\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: errObj.error, hint }));\n } else {\n console.error(`[error] site ${name}: ${errObj.error}`);\n if (hint) console.error(` Hint: ${hint}`);\n }\n process.exit(1);\n }\n\n if (options.jq) {\n const expr = options.jq.replace(/^\\.data\\./, '.');\n const results = applyJq(parsed, expr);\n for (const r of results) {\n console.log(typeof r === \"string\" ? r : JSON.stringify(r));\n }\n } else if (options.json) {\n console.log(JSON.stringify({ success: true, data: parsed }));\n } else {\n console.log(JSON.stringify(parsed, null, 2));\n }\n}\n\n// ── 入口 ────────────────────────────────────────────────────────\n\nexport async function siteCommand(\n args: string[],\n options: SiteOptions = {}\n): Promise<void> {\n const subCommand = args[0];\n\n if (!subCommand || subCommand === \"--help\" || subCommand === \"-h\") {\n console.log(`bws site - 网站 CLI 化\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 详情\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n\n示例:\n bws site list\n bws site zhihu/hot\n bws site xiaohongshu/search \"旅行\"\n bws site bilibili/popular`);\n return;\n }\n\n switch (subCommand) {\n case \"list\": siteList(options); break;\n case \"search\":\n if (!args[1]) {\n console.error(\"[error] site search: <query> is required.\");\n console.error(\" Usage: bws site search <query>\");\n process.exit(1);\n }\n siteSearch(args[1], options);\n break;\n case \"info\":\n if (!args[1]) {\n console.error(\"[error] site info: <name> is required.\");\n console.error(\" Usage: bws site info <name>\");\n process.exit(1);\n }\n siteInfo(args[1], options);\n break;\n case \"run\":\n if (!args[1]) {\n console.error(\"[error] site run: <name> is required.\");\n console.error(\" Usage: bws site run <name> [args...]\");\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n await siteRun(args[1], args.slice(2), options);\n break;\n default:\n if (subCommand.includes(\"/\")) {\n await siteRun(subCommand, args.slice(1), options);\n } else {\n console.error(`[error] site: unknown subcommand \"${subCommand}\".`);\n console.error(\" Available: list, info, search, run\");\n console.error(\" Try: bws site --help\");\n process.exit(1);\n }\n break;\n }\n\n}\n","import { execFileSync } from \"node:child_process\";\nimport { parseOpenClawJson } from \"./openclaw-json.js\";\n\nconst OPENCLAW_EVALUATE_TIMEOUT_MS = 120000;\nconst EXEC_TIMEOUT_BUFFER_MS = 5000;\n\nexport interface OCTab {\n targetId: string;\n url: string;\n title: string;\n type: string;\n}\n\nexport function buildOpenClawArgs(args: string[], timeout: number): string[] {\n const [subcommand, ...rest] = args;\n if (!subcommand) {\n throw new Error(\"OpenClaw browser command requires a subcommand\");\n }\n\n return [\"openclaw\", \"browser\", subcommand, \"--timeout\", String(timeout), ...rest];\n}\n\nexport function getOpenClawExecTimeout(timeout: number): number {\n return timeout + EXEC_TIMEOUT_BUFFER_MS;\n}\n\nfunction runOpenClaw(args: string[], timeout: number): string {\n return execFileSync(\"npx\", buildOpenClawArgs(args, timeout), {\n encoding: \"utf-8\",\n timeout: getOpenClawExecTimeout(timeout),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n}\n\nexport function ocGetTabs(): OCTab[] {\n const raw = runOpenClaw([\"tabs\", \"--json\"], 15000);\n const data = parseOpenClawJson<{ tabs?: OCTab[] }>(raw);\n return (data.tabs || []).filter((tab: OCTab) => tab.type === \"page\");\n}\n\nexport function ocFindTabByDomain(tabs: OCTab[], domain: string): OCTab | undefined {\n return tabs.find((tab) => {\n try {\n const hostname = new URL(tab.url).hostname;\n return hostname === domain || hostname.endsWith(`.${domain}`);\n } catch {\n return false;\n }\n });\n}\n\nexport function ocOpenTab(url: string): string {\n const raw = runOpenClaw([\"open\", url, \"--json\"], 30000);\n const data = parseOpenClawJson<{ id?: string; targetId?: string }>(raw);\n return data.id || data.targetId || \"\";\n}\n\nexport function ocEvaluate(targetId: string, fn: string): unknown {\n const raw = runOpenClaw([\"evaluate\", \"--fn\", fn, \"--target-id\", targetId], OPENCLAW_EVALUATE_TIMEOUT_MS);\n return parseOpenClawJson(raw);\n}\n","interface ParseSuccess<T> {\n ok: true;\n value: T;\n}\n\ninterface ParseFailure {\n ok: false;\n error: Error;\n}\n\nfunction buildPreview(raw: string): string {\n return raw.length > 200 ? `${raw.slice(0, 200)}...` : raw;\n}\n\nfunction tryParseJson<T>(raw: string): ParseSuccess<T> | ParseFailure {\n try {\n return { ok: true, value: JSON.parse(raw) as T };\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\nfunction tryParseLastJsonLineBlock<T>(raw: string): ParseSuccess<T> | null {\n const lines = raw.split(/\\r?\\n/);\n\n // OpenClaw diagnostics are emitted as extra lines around the JSON payload,\n // so prefer the last contiguous block of lines that parses cleanly as JSON.\n for (let end = lines.length; end > 0; end -= 1) {\n for (let start = end - 1; start >= 0; start -= 1) {\n const candidate = lines.slice(start, end).join(\"\\n\").trim();\n if (!candidate) {\n continue;\n }\n\n const parsed = tryParseJson<T>(candidate);\n if (parsed.ok) {\n return parsed;\n }\n }\n }\n\n return null;\n}\n\nexport function parseOpenClawJson<T>(raw: string): T {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new Error(\"OpenClaw returned empty output\");\n }\n\n const direct = tryParseJson<T>(trimmed);\n if (direct.ok) {\n return direct.value;\n }\n\n const lineBlock = tryParseLastJsonLineBlock<T>(trimmed);\n if (lineBlock) {\n return lineBlock.value;\n }\n\n throw new Error(`Failed to parse OpenClaw JSON output: ${direct.error.message}\\nRaw (preview): ${buildPreview(trimmed)}`);\n}\n","function splitTopLevel(input: string, separator: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let depth = 0;\n let inString = false;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n const prev = input[i - 1];\n if (char === '\"' && prev !== '\\\\') inString = !inString;\n if (!inString) {\n if (char === '{' || char === '(' || char === '[') depth++;\n if (char === '}' || char === ')' || char === ']') depth--;\n if (depth === 0 && input.slice(i, i + separator.length) === separator) {\n parts.push(current.trim());\n current = \"\";\n i += separator.length - 1;\n continue;\n }\n }\n current += char;\n }\n\n if (current.trim()) parts.push(current.trim());\n return parts;\n}\n\nfunction parseLiteral(value: string): unknown {\n const trimmed = value.trim();\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) return JSON.parse(trimmed);\n if (trimmed === \"true\") return true;\n if (trimmed === \"false\") return false;\n if (trimmed === \"null\") return null;\n return Number(trimmed);\n}\n\nfunction getField(value: unknown, field: string): unknown {\n return value !== null && typeof value === \"object\" ? (value as Record<string, unknown>)[field] : undefined;\n}\n\nfunction applySegment(inputs: unknown[], expr: string): unknown[] {\n if (expr === \".\") return inputs;\n if (expr.startsWith(\"select(\")) {\n const match = expr.match(/^select\\((.+?)\\s*(==|>)\\s*(.+)\\)$/);\n if (!match) throw new Error(`不支持的 jq 表达式: ${expr}`);\n const [, leftExpr, op, rightExpr] = match;\n const expected = parseLiteral(rightExpr);\n return inputs.filter((item) => {\n const left = applyExpression([item], leftExpr)[0];\n return op === \"==\" ? left === expected : Number(left) > Number(expected);\n });\n }\n if (expr.startsWith(\"{\") && expr.endsWith(\"}\")) {\n const body = expr.slice(1, -1).trim();\n if (!body) return inputs.map(() => ({}));\n const entries = splitTopLevel(body, \",\");\n return inputs.map((item) => {\n const obj: Record<string, unknown> = {};\n for (const entry of entries) {\n const colon = entry.indexOf(\":\");\n if (colon === -1) {\n const key = entry.trim().replace(/^\\./, \"\");\n obj[key] = applyExpression([item], `.${key}`)[0];\n } else {\n const key = entry.slice(0, colon).trim();\n const valueExpr = entry.slice(colon + 1).trim();\n obj[key] = applyExpression([item], valueExpr)[0];\n }\n }\n return obj;\n });\n }\n if (!expr.startsWith(\".\")) throw new Error(`不支持的 jq 表达式: ${expr}`);\n\n let current = inputs;\n let remaining = expr.slice(1);\n while (remaining.length > 0) {\n if (remaining.startsWith(\"[]\")) {\n current = current.flatMap((item) => Array.isArray(item) ? item : []);\n remaining = remaining.slice(2);\n } else if (remaining.startsWith(\"[\")) {\n const match = remaining.match(/^\\[(-?\\d+)\\]/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const index = Number(match[1]);\n current = current.map((item) => {\n if (!Array.isArray(item)) return undefined;\n return item[index >= 0 ? index : item.length + index];\n });\n remaining = remaining.slice(match[0].length);\n } else if (remaining.startsWith(\".\")) {\n remaining = remaining.slice(1);\n } else {\n const match = remaining.match(/^([A-Za-z_][A-Za-z0-9_]*)/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const field = match[1];\n current = current.map((item) => getField(item, field));\n remaining = remaining.slice(field.length);\n }\n }\n return current;\n}\n\nfunction applyExpression(inputs: unknown[], expression: string): unknown[] {\n const segments = splitTopLevel(expression.trim(), \"|\");\n return segments.reduce((current, segment) => applySegment(current, segment.trim()), inputs);\n}\n\nexport function applyJq(data: unknown, expression: string): unknown[] {\n return applyExpression([data], expression).filter((item) => item !== undefined);\n}\n","#!/usr/bin/env node\n\n/**\n * Browser Web Search (BWS) - Turn any website into a CLI command\n * \n * 基于 OpenClaw 浏览器,复用用户登录态访问网站数据\n */\n\nimport { siteCommand } from \"./commands/site.js\";\n\nconst VERSION = \"0.2.0\";\n\nconst HELP_TEXT = `\nBrowser Web Search (BWS) v${VERSION}\n\n把任何网站变成命令行 API,使用 OpenClaw 浏览器 + 用户登录态。\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 元信息\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n bws <name> [args...] 运行 adapter(简写)\n\n示例:\n bws site list # 查看所有可用命令\n bws zhihu/hot # 知乎热榜\n bws xiaohongshu/search \"旅行\" # 小红书搜索\n bws bilibili/popular # B站热门\n\n选项:\n --json 以 JSON 格式输出\n --jq <expr> 对 JSON 输出应用 jq 过滤\n --help, -h 显示帮助信息\n --version, -v 显示版本号\n\n需要 OpenClaw 环境运行。\n`.trim();\n\ninterface ParsedArgs {\n command: string;\n args: string[];\n flags: {\n json?: boolean;\n jq?: string;\n };\n}\n\nfunction parseArgs(args: string[]): ParsedArgs {\n const result: ParsedArgs = {\n command: \"\",\n args: [],\n flags: {},\n };\n\n let skipNext = false;\n\n for (let i = 0; i < args.length; i++) {\n if (skipNext) {\n skipNext = false;\n continue;\n }\n\n const arg = args[i];\n\n if (arg === \"--json\") {\n result.flags.json = true;\n } else if (arg === \"--jq\") {\n skipNext = true;\n const nextIdx = i + 1;\n if (nextIdx < args.length) {\n result.flags.jq = args[nextIdx];\n result.flags.json = true;\n }\n } else if (arg === \"--help\" || arg === \"-h\") {\n console.log(HELP_TEXT);\n process.exit(0);\n } else if (arg === \"--version\" || arg === \"-v\") {\n console.log(VERSION);\n process.exit(0);\n } else if (!result.command) {\n result.command = arg;\n } else {\n result.args.push(arg);\n }\n }\n\n return result;\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (args.length === 0) {\n console.log(HELP_TEXT);\n process.exit(0);\n }\n\n const parsed = parseArgs(args);\n\n switch (parsed.command) {\n case \"site\":\n await siteCommand(parsed.args, {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n break;\n\n default:\n // 如果命令包含 /,当作 site 命令的简写\n if (parsed.command.includes(\"/\")) {\n await siteCommand([parsed.command, ...parsed.args], {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n } else {\n console.error(`[error] 未知命令: ${parsed.command}`);\n console.error(\" 运行 bws --help 查看帮助\");\n process.exit(1);\n }\n break;\n }\n}\n\nmain().catch((error) => {\n console.error(`[error] ${error.message || error}`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/commands/site.ts","../src/openclaw-bridge.ts","../src/openclaw-json.ts","../src/jq.ts","../src/index.ts"],"sourcesContent":["/**\n * site 命令 - 管理和运行网站适配器\n *\n * 用法:\n * bws site list 列出所有可用 adapter\n * bws site search <query> 搜索 adapter\n * bws site info <name> 查看 adapter 详情\n * bws site <name> [args...] 运行 adapter\n *\n * 目录:\n * 内置 adapter:随项目发布\n * ~/.bws/sites/:用户私有 adapter(优先级更高)\n */\n\nimport { readFileSync, readdirSync, existsSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ocGetTabs, ocFindTabByDomain, ocOpenTab, ocEvaluate } from \"../openclaw-bridge.js\";\nimport { applyJq } from \"../jq.js\";\n\nconst BWS_DIR = join(homedir(), \".bws\");\nconst LOCAL_SITES_DIR = join(BWS_DIR, \"sites\"); // 用户私有 adapter(优先级最高)\n\n// 内置 adapter 目录(项目自带)\nimport { fileURLToPath } from \"node:url\";\nimport { dirname } from \"node:path\";\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst BUILTIN_SITES_DIR = join(__dirname, \"../sites\"); // 项目内置 adapter\n\nexport interface SiteOptions {\n json?: boolean;\n jq?: string;\n}\n\n/** Adapter 参数定义 */\ninterface ArgDef {\n required?: boolean;\n description?: string;\n}\n\n/** Adapter 元数据 */\ninterface SiteMeta {\n name: string;\n description: string;\n domain: string;\n args: Record<string, ArgDef>;\n capabilities?: string[];\n readOnly?: boolean;\n example?: string;\n filePath: string;\n source: \"local\" | \"builtin\";\n}\n\nfunction exitJsonError(error: string, extra: Record<string, unknown> = {}): never {\n console.log(JSON.stringify({ success: false, error, ...extra }, null, 2));\n process.exit(1);\n}\n\n/**\n * 从 JS 文件的 /* @meta JSON * / 块解析元数据\n */\nfunction parseSiteMeta(filePath: string, source: \"local\" | \"builtin\"): SiteMeta | null {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n\n // 从文件路径推断默认 name\n const sitesDir = source === \"local\" ? LOCAL_SITES_DIR : BUILTIN_SITES_DIR;\n const relPath = relative(sitesDir, filePath);\n const defaultName = relPath.replace(/\\.js$/, \"\").replace(/\\\\/g, \"/\");\n\n // 解析 /* @meta { ... } */ 块\n const metaMatch = content.match(/\\/\\*\\s*@meta\\s*\\n([\\s\\S]*?)\\*\\//);\n if (metaMatch) {\n try {\n const metaJson = JSON.parse(metaMatch[1]);\n return {\n name: metaJson.name || defaultName,\n description: metaJson.description || \"\",\n domain: metaJson.domain || \"\",\n args: metaJson.args || {},\n capabilities: metaJson.capabilities,\n readOnly: metaJson.readOnly,\n example: metaJson.example,\n filePath,\n source,\n };\n } catch {\n // JSON 解析失败,回退到 @tag 模式\n }\n }\n\n // 回退:解析 // @tag 格式(兼容旧格式)\n const meta: SiteMeta = {\n name: defaultName,\n description: \"\",\n domain: \"\",\n args: {},\n filePath,\n source,\n };\n\n const tagPattern = /\\/\\/\\s*@(\\w+)[ \\t]+(.*)/g;\n let match;\n while ((match = tagPattern.exec(content)) !== null) {\n const [, key, value] = match;\n switch (key) {\n case \"name\": meta.name = value.trim(); break;\n case \"description\": meta.description = value.trim(); break;\n case \"domain\": meta.domain = value.trim(); break;\n case \"args\":\n for (const arg of value.trim().split(/[,\\s]+/).filter(Boolean)) {\n meta.args[arg] = { required: true };\n }\n break;\n case \"example\": meta.example = value.trim(); break;\n }\n }\n\n return meta;\n}\n\n/**\n * 扫描目录下所有 .js 文件\n */\nfunction scanSites(dir: string, source: \"local\" | \"builtin\"): SiteMeta[] {\n if (!existsSync(dir)) return [];\n const sites: SiteMeta[] = [];\n\n function walk(currentDir: string): void {\n let entries;\n try { entries = readdirSync(currentDir, { withFileTypes: true }); } catch { return; }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory() && !entry.name.startsWith(\".\")) {\n walk(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".js\")) {\n const meta = parseSiteMeta(fullPath, source);\n if (meta) sites.push(meta);\n }\n }\n }\n\n walk(dir);\n return sites;\n}\n\n/**\n * 获取所有 adapter(用户私有 > 内置)\n */\nfunction getAllSites(): SiteMeta[] {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n\n const byName = new Map<string, SiteMeta>();\n for (const s of builtin) byName.set(s.name, s);\n for (const s of local) byName.set(s.name, s); // 用户私有覆盖内置\n\n return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));\n}\n\n// ── 子命令 ──────────────────────────────────────────────────────\n\nfunction siteList(options: SiteOptions): void {\n const sites = getAllSites();\n\n if (sites.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(\"未找到任何 site adapter。\");\n console.log(` 内置 adapter 目录: ${BUILTIN_SITES_DIR}`);\n console.log(` 私有 adapter 目录: ${LOCAL_SITES_DIR}`);\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(sites.map(s => ({\n name: s.name, description: s.description, domain: s.domain,\n args: s.args, source: s.source,\n })), null, 2));\n return;\n }\n\n const groups = new Map<string, SiteMeta[]>();\n for (const s of sites) {\n const platform = s.name.split(\"/\")[0];\n if (!groups.has(platform)) groups.set(platform, []);\n groups.get(platform)!.push(s);\n }\n\n for (const [platform, items] of groups) {\n console.log(`\\n${platform}/`);\n for (const s of items) {\n const cmd = s.name.split(\"/\").slice(1).join(\"/\");\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n const desc = s.description ? ` - ${s.description}` : \"\";\n console.log(` ${cmd.padEnd(20)}${desc}${src}`);\n }\n }\n console.log();\n}\n\nfunction siteSearch(query: string, options: SiteOptions): void {\n const sites = getAllSites();\n const q = query.toLowerCase();\n const matches = sites.filter(s =>\n s.name.toLowerCase().includes(q) ||\n s.description.toLowerCase().includes(q) ||\n s.domain.toLowerCase().includes(q)\n );\n\n if (matches.length === 0) {\n if (options.json) {\n console.log(\"[]\");\n return;\n }\n console.log(`未找到匹配 \"${query}\" 的 adapter。`);\n console.log(\" 查看所有: bws site list\");\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(matches.map(s => ({\n name: s.name, description: s.description, domain: s.domain, source: s.source,\n })), null, 2));\n return;\n }\n\n for (const s of matches) {\n const src = s.source === \"local\" ? \" (local)\" : \"\";\n console.log(`${s.name.padEnd(24)} ${s.description}${src}`);\n }\n}\n\nfunction siteInfo_showDirs(options: SiteOptions): void {\n const builtin = scanSites(BUILTIN_SITES_DIR, \"builtin\");\n const local = scanSites(LOCAL_SITES_DIR, \"local\");\n \n if (options.json) {\n console.log(JSON.stringify({\n builtinDir: BUILTIN_SITES_DIR,\n localDir: LOCAL_SITES_DIR,\n builtinCount: builtin.length,\n localCount: local.length,\n }, null, 2));\n return;\n }\n \n console.log(`内置 adapter: ${BUILTIN_SITES_DIR} (${builtin.length} 个)`);\n console.log(`私有 adapter: ${LOCAL_SITES_DIR} (${local.length} 个)`);\n}\n\nfunction findSiteByName(name: string): SiteMeta | undefined {\n return getAllSites().find((site) => site.name === name);\n}\n\nfunction siteInfo(name: string, options: SiteOptions): void {\n const site = findSiteByName(name);\n\n if (!site) {\n if (options.json) {\n exitJsonError(`adapter \"${name}\" not found`, { action: \"bws site list\" });\n }\n console.error(`[error] site info: adapter \"${name}\" not found.`);\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n\n const meta = {\n name: site.name,\n description: site.description,\n domain: site.domain,\n args: site.args,\n example: site.example,\n readOnly: site.readOnly,\n };\n\n if (options.json) {\n console.log(JSON.stringify(meta, null, 2));\n return;\n }\n\n console.log(`${site.name} — ${site.description}`);\n console.log();\n console.log(\"参数:\");\n\n const argEntries = Object.entries(site.args);\n if (argEntries.length === 0) {\n console.log(\" (无)\");\n } else {\n for (const [argName, argDef] of argEntries) {\n const requiredText = argDef.required ? \"必填\" : \"可选\";\n const description = argDef.description || \"\";\n console.log(` ${argName} (${requiredText}) ${description}`.trimEnd());\n }\n }\n\n console.log();\n console.log(\"示例:\");\n console.log(` ${site.example || `bws site ${site.name}`}`);\n console.log();\n console.log(`域名:${site.domain || \"(未声明)\"}`);\n console.log(`只读:${site.readOnly ? \"是\" : \"否\"}`);\n}\n\nasync function siteRun(\n name: string,\n args: string[],\n options: SiteOptions\n): Promise<void> {\n const sites = getAllSites();\n const site = sites.find(s => s.name === name);\n\n if (!site) {\n const fuzzy = sites.filter(s => s.name.includes(name));\n if (options.json) {\n exitJsonError(`site \"${name}\" not found`, {\n suggestions: fuzzy.slice(0, 5).map(s => s.name),\n action: fuzzy.length > 0 ? undefined : \"bws site update\",\n });\n }\n console.error(`[error] site: \"${name}\" not found.`);\n if (fuzzy.length > 0) {\n console.error(\" Did you mean:\");\n for (const s of fuzzy.slice(0, 5)) {\n console.error(` bws site ${s.name}`);\n }\n } else {\n console.error(\" Try: bws site list\");\n console.error(\" Or: bws site update\");\n }\n process.exit(1);\n }\n\n // 解析参数\n const argNames = Object.keys(site.args);\n const argMap: Record<string, string> = {};\n\n // 过滤掉 --flag value 对,收集位置参数\n const positionalArgs: string[] = [];\n for (let i = 0; i < args.length; i++) {\n if (args[i].startsWith(\"--\")) {\n const flagName = args[i].slice(2);\n if (flagName in site.args && args[i + 1]) {\n argMap[flagName] = args[i + 1];\n i++; // 跳过值\n }\n } else {\n positionalArgs.push(args[i]);\n }\n }\n\n // 位置参数按 argNames 顺序填入(跳过已通过 --flag 提供的)\n let posIdx = 0;\n for (const argName of argNames) {\n if (!argMap[argName] && posIdx < positionalArgs.length) {\n argMap[argName] = positionalArgs[posIdx++];\n }\n }\n\n // 只检查 required 参数\n for (const [argName, argDef] of Object.entries(site.args)) {\n if (argDef.required && !argMap[argName]) {\n const usage = argNames.map(a => {\n const def = site.args[a];\n return def.required ? `<${a}>` : `[${a}]`;\n }).join(\" \");\n if (options.json) {\n exitJsonError(`missing required argument \"${argName}\"`, {\n usage: `bws site ${name} ${usage}`,\n example: site.example,\n });\n }\n console.error(`[error] site ${name}: missing required argument \"${argName}\".`);\n console.error(` Usage: bws site ${name} ${usage}`);\n if (site.example) console.error(` Example: ${site.example}`);\n process.exit(1);\n }\n }\n\n // 读取并解析 JS\n const jsContent = readFileSync(site.filePath, \"utf-8\");\n\n // 移除 /* @meta ... */ 块,保留函数体\n const jsBody = jsContent.replace(/\\/\\*\\s*@meta[\\s\\S]*?\\*\\//, \"\").trim();\n\n // 构造执行脚本\n const argsJson = JSON.stringify(argMap);\n\n // OpenClaw 模式执行\n let targetId: string;\n\n if (site.domain) {\n const tabs = ocGetTabs();\n const existing = ocFindTabByDomain(tabs, site.domain);\n if (existing) {\n targetId = existing.targetId;\n } else {\n targetId = ocOpenTab(`https://${site.domain}`);\n await new Promise((resolve) => setTimeout(resolve, 3000));\n }\n } else {\n const tabs = ocGetTabs();\n if (tabs.length === 0) {\n throw new Error(\"No tabs open in OpenClaw browser\");\n }\n targetId = tabs[0].targetId;\n }\n\n const wrappedFn = `async () => { const __fn = ${jsBody}; return await __fn(${argsJson}); }`;\n const parsed = ocEvaluate(targetId, wrappedFn);\n\n if (typeof parsed === \"object\" && parsed !== null && \"error\" in parsed) {\n const errObj = parsed as { error: string; hint?: string };\n const checkText = `${errObj.error} ${errObj.hint || \"\"}`;\n const isAuthError = /401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth/i.test(checkText);\n const loginHint = isAuthError && site.domain\n ? `Please log in to https://${site.domain} in your OpenClaw browser first, then retry.`\n : undefined;\n const hint = loginHint || errObj.hint;\n\n if (options.json) {\n console.log(JSON.stringify({ success: false, error: errObj.error, hint }));\n } else {\n console.error(`[error] site ${name}: ${errObj.error}`);\n if (hint) console.error(` Hint: ${hint}`);\n }\n process.exit(1);\n }\n\n if (options.jq) {\n const expr = options.jq.replace(/^\\.data\\./, '.');\n const results = applyJq(parsed, expr);\n for (const r of results) {\n console.log(typeof r === \"string\" ? r : JSON.stringify(r));\n }\n } else if (options.json) {\n console.log(JSON.stringify({ success: true, data: parsed }));\n } else {\n console.log(JSON.stringify(parsed, null, 2));\n }\n}\n\n// ── 入口 ────────────────────────────────────────────────────────\n\nexport async function siteCommand(\n args: string[],\n options: SiteOptions = {}\n): Promise<void> {\n const subCommand = args[0];\n\n if (!subCommand || subCommand === \"--help\" || subCommand === \"-h\") {\n console.log(`bws site - 网站 CLI 化\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 详情\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n\n示例:\n bws site list\n bws site zhihu/hot\n bws site xiaohongshu/search \"旅行\"\n bws site bilibili/popular`);\n return;\n }\n\n switch (subCommand) {\n case \"list\": siteList(options); break;\n case \"search\":\n if (!args[1]) {\n console.error(\"[error] site search: <query> is required.\");\n console.error(\" Usage: bws site search <query>\");\n process.exit(1);\n }\n siteSearch(args[1], options);\n break;\n case \"info\":\n if (!args[1]) {\n console.error(\"[error] site info: <name> is required.\");\n console.error(\" Usage: bws site info <name>\");\n process.exit(1);\n }\n siteInfo(args[1], options);\n break;\n case \"run\":\n if (!args[1]) {\n console.error(\"[error] site run: <name> is required.\");\n console.error(\" Usage: bws site run <name> [args...]\");\n console.error(\" Try: bws site list\");\n process.exit(1);\n }\n await siteRun(args[1], args.slice(2), options);\n break;\n default:\n if (subCommand.includes(\"/\")) {\n await siteRun(subCommand, args.slice(1), options);\n } else {\n console.error(`[error] site: unknown subcommand \"${subCommand}\".`);\n console.error(\" Available: list, info, search, run\");\n console.error(\" Try: bws site --help\");\n process.exit(1);\n }\n break;\n }\n\n}\n","import { execFileSync } from \"node:child_process\";\nimport { parseOpenClawJson } from \"./openclaw-json.js\";\n\nconst OPENCLAW_EVALUATE_TIMEOUT_MS = 120000;\nconst EXEC_TIMEOUT_BUFFER_MS = 5000;\n\nexport interface OCTab {\n targetId: string;\n url: string;\n title: string;\n type: string;\n}\n\n// Global flags that should be placed after \"browser\" but before subcommand\nconst GLOBAL_FLAGS = new Set([\"--json\", \"--timeout\"]);\n\nexport function buildOpenClawArgs(args: string[], timeout: number): string[] {\n const [subcommand, ...rest] = args;\n if (!subcommand) {\n throw new Error(\"OpenClaw browser command requires a subcommand\");\n }\n\n // Separate global flags from subcommand-specific args\n const globalFlags: string[] = [\"--timeout\", String(timeout)];\n const subcommandArgs: string[] = [];\n\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n if (GLOBAL_FLAGS.has(arg)) {\n globalFlags.push(arg);\n // If this flag takes a value (not --json), include the next arg too\n if (arg !== \"--json\" && i + 1 < rest.length) {\n globalFlags.push(rest[++i]);\n }\n } else {\n subcommandArgs.push(arg);\n }\n }\n\n return [\"openclaw\", \"browser\", ...globalFlags, subcommand, ...subcommandArgs];\n}\n\nexport function getOpenClawExecTimeout(timeout: number): number {\n return timeout + EXEC_TIMEOUT_BUFFER_MS;\n}\n\nfunction runOpenClaw(args: string[], timeout: number): string {\n return execFileSync(\"npx\", buildOpenClawArgs(args, timeout), {\n encoding: \"utf-8\",\n timeout: getOpenClawExecTimeout(timeout),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n}\n\nexport function ocGetTabs(): OCTab[] {\n const raw = runOpenClaw([\"tabs\", \"--json\"], 15000);\n const data = parseOpenClawJson<{ tabs?: OCTab[] }>(raw);\n return (data.tabs || []).filter((tab: OCTab) => tab.type === \"page\");\n}\n\nexport function ocFindTabByDomain(tabs: OCTab[], domain: string): OCTab | undefined {\n return tabs.find((tab) => {\n try {\n const hostname = new URL(tab.url).hostname;\n return hostname === domain || hostname.endsWith(`.${domain}`);\n } catch {\n return false;\n }\n });\n}\n\nexport function ocOpenTab(url: string): string {\n const raw = runOpenClaw([\"open\", url, \"--json\"], 30000);\n const data = parseOpenClawJson<{ id?: string; targetId?: string }>(raw);\n return data.id || data.targetId || \"\";\n}\n\nexport function ocEvaluate(targetId: string, fn: string): unknown {\n const raw = runOpenClaw([\"evaluate\", \"--fn\", fn, \"--target-id\", targetId], OPENCLAW_EVALUATE_TIMEOUT_MS);\n return parseOpenClawJson(raw);\n}\n","interface ParseSuccess<T> {\n ok: true;\n value: T;\n}\n\ninterface ParseFailure {\n ok: false;\n error: Error;\n}\n\nfunction buildPreview(raw: string): string {\n return raw.length > 200 ? `${raw.slice(0, 200)}...` : raw;\n}\n\nfunction tryParseJson<T>(raw: string): ParseSuccess<T> | ParseFailure {\n try {\n return { ok: true, value: JSON.parse(raw) as T };\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\nfunction tryParseLastJsonLineBlock<T>(raw: string): ParseSuccess<T> | null {\n const lines = raw.split(/\\r?\\n/);\n\n // OpenClaw diagnostics are emitted as extra lines around the JSON payload,\n // so prefer the last contiguous block of lines that parses cleanly as JSON.\n for (let end = lines.length; end > 0; end -= 1) {\n for (let start = end - 1; start >= 0; start -= 1) {\n const candidate = lines.slice(start, end).join(\"\\n\").trim();\n if (!candidate) {\n continue;\n }\n\n const parsed = tryParseJson<T>(candidate);\n if (parsed.ok) {\n return parsed;\n }\n }\n }\n\n return null;\n}\n\nexport function parseOpenClawJson<T>(raw: string): T {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new Error(\"OpenClaw returned empty output\");\n }\n\n const direct = tryParseJson<T>(trimmed);\n if (direct.ok) {\n return direct.value;\n }\n\n const lineBlock = tryParseLastJsonLineBlock<T>(trimmed);\n if (lineBlock) {\n return lineBlock.value;\n }\n\n throw new Error(`Failed to parse OpenClaw JSON output: ${direct.error.message}\\nRaw (preview): ${buildPreview(trimmed)}`);\n}\n","function splitTopLevel(input: string, separator: string): string[] {\n const parts: string[] = [];\n let current = \"\";\n let depth = 0;\n let inString = false;\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n const prev = input[i - 1];\n if (char === '\"' && prev !== '\\\\') inString = !inString;\n if (!inString) {\n if (char === '{' || char === '(' || char === '[') depth++;\n if (char === '}' || char === ')' || char === ']') depth--;\n if (depth === 0 && input.slice(i, i + separator.length) === separator) {\n parts.push(current.trim());\n current = \"\";\n i += separator.length - 1;\n continue;\n }\n }\n current += char;\n }\n\n if (current.trim()) parts.push(current.trim());\n return parts;\n}\n\nfunction parseLiteral(value: string): unknown {\n const trimmed = value.trim();\n if (trimmed.startsWith('\"') && trimmed.endsWith('\"')) return JSON.parse(trimmed);\n if (trimmed === \"true\") return true;\n if (trimmed === \"false\") return false;\n if (trimmed === \"null\") return null;\n return Number(trimmed);\n}\n\nfunction getField(value: unknown, field: string): unknown {\n return value !== null && typeof value === \"object\" ? (value as Record<string, unknown>)[field] : undefined;\n}\n\nfunction applySegment(inputs: unknown[], expr: string): unknown[] {\n if (expr === \".\") return inputs;\n if (expr.startsWith(\"select(\")) {\n const match = expr.match(/^select\\((.+?)\\s*(==|>)\\s*(.+)\\)$/);\n if (!match) throw new Error(`不支持的 jq 表达式: ${expr}`);\n const [, leftExpr, op, rightExpr] = match;\n const expected = parseLiteral(rightExpr);\n return inputs.filter((item) => {\n const left = applyExpression([item], leftExpr)[0];\n return op === \"==\" ? left === expected : Number(left) > Number(expected);\n });\n }\n if (expr.startsWith(\"{\") && expr.endsWith(\"}\")) {\n const body = expr.slice(1, -1).trim();\n if (!body) return inputs.map(() => ({}));\n const entries = splitTopLevel(body, \",\");\n return inputs.map((item) => {\n const obj: Record<string, unknown> = {};\n for (const entry of entries) {\n const colon = entry.indexOf(\":\");\n if (colon === -1) {\n const key = entry.trim().replace(/^\\./, \"\");\n obj[key] = applyExpression([item], `.${key}`)[0];\n } else {\n const key = entry.slice(0, colon).trim();\n const valueExpr = entry.slice(colon + 1).trim();\n obj[key] = applyExpression([item], valueExpr)[0];\n }\n }\n return obj;\n });\n }\n if (!expr.startsWith(\".\")) throw new Error(`不支持的 jq 表达式: ${expr}`);\n\n let current = inputs;\n let remaining = expr.slice(1);\n while (remaining.length > 0) {\n if (remaining.startsWith(\"[]\")) {\n current = current.flatMap((item) => Array.isArray(item) ? item : []);\n remaining = remaining.slice(2);\n } else if (remaining.startsWith(\"[\")) {\n const match = remaining.match(/^\\[(-?\\d+)\\]/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const index = Number(match[1]);\n current = current.map((item) => {\n if (!Array.isArray(item)) return undefined;\n return item[index >= 0 ? index : item.length + index];\n });\n remaining = remaining.slice(match[0].length);\n } else if (remaining.startsWith(\".\")) {\n remaining = remaining.slice(1);\n } else {\n const match = remaining.match(/^([A-Za-z_][A-Za-z0-9_]*)/);\n if (!match) throw new Error(`不支持的 jq 表达式: .${remaining}`);\n const field = match[1];\n current = current.map((item) => getField(item, field));\n remaining = remaining.slice(field.length);\n }\n }\n return current;\n}\n\nfunction applyExpression(inputs: unknown[], expression: string): unknown[] {\n const segments = splitTopLevel(expression.trim(), \"|\");\n return segments.reduce((current, segment) => applySegment(current, segment.trim()), inputs);\n}\n\nexport function applyJq(data: unknown, expression: string): unknown[] {\n return applyExpression([data], expression).filter((item) => item !== undefined);\n}\n","#!/usr/bin/env node\n\n/**\n * Browser Web Search (BWS) - Turn any website into a CLI command\n * \n * 基于 OpenClaw 浏览器,复用用户登录态访问网站数据\n */\n\nimport { siteCommand } from \"./commands/site.js\";\n\nconst VERSION = \"0.2.0\";\n\nconst HELP_TEXT = `\nBrowser Web Search (BWS) v${VERSION}\n\n把任何网站变成命令行 API,使用 OpenClaw 浏览器 + 用户登录态。\n\n用法:\n bws site list 列出所有可用 adapter\n bws site info <name> 查看 adapter 元信息\n bws site search <query> 搜索 adapter\n bws site <name> [args...] 运行 adapter\n bws <name> [args...] 运行 adapter(简写)\n\n示例:\n bws site list # 查看所有可用命令\n bws zhihu/hot # 知乎热榜\n bws xiaohongshu/search \"旅行\" # 小红书搜索\n bws bilibili/popular # B站热门\n\n选项:\n --json 以 JSON 格式输出\n --jq <expr> 对 JSON 输出应用 jq 过滤\n --help, -h 显示帮助信息\n --version, -v 显示版本号\n\n需要 OpenClaw 环境运行。\n`.trim();\n\ninterface ParsedArgs {\n command: string;\n args: string[];\n flags: {\n json?: boolean;\n jq?: string;\n };\n}\n\nfunction parseArgs(args: string[]): ParsedArgs {\n const result: ParsedArgs = {\n command: \"\",\n args: [],\n flags: {},\n };\n\n let skipNext = false;\n\n for (let i = 0; i < args.length; i++) {\n if (skipNext) {\n skipNext = false;\n continue;\n }\n\n const arg = args[i];\n\n if (arg === \"--json\") {\n result.flags.json = true;\n } else if (arg === \"--jq\") {\n skipNext = true;\n const nextIdx = i + 1;\n if (nextIdx < args.length) {\n result.flags.jq = args[nextIdx];\n result.flags.json = true;\n }\n } else if (arg === \"--help\" || arg === \"-h\") {\n console.log(HELP_TEXT);\n process.exit(0);\n } else if (arg === \"--version\" || arg === \"-v\") {\n console.log(VERSION);\n process.exit(0);\n } else if (!result.command) {\n result.command = arg;\n } else {\n result.args.push(arg);\n }\n }\n\n return result;\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (args.length === 0) {\n console.log(HELP_TEXT);\n process.exit(0);\n }\n\n const parsed = parseArgs(args);\n\n switch (parsed.command) {\n case \"site\":\n await siteCommand(parsed.args, {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n break;\n\n default:\n // 如果命令包含 /,当作 site 命令的简写\n if (parsed.command.includes(\"/\")) {\n await siteCommand([parsed.command, ...parsed.args], {\n json: parsed.flags.json,\n jq: parsed.flags.jq,\n });\n } else {\n console.error(`[error] 未知命令: ${parsed.command}`);\n console.error(\" 运行 bws --help 查看帮助\");\n process.exit(1);\n }\n break;\n }\n}\n\nmain().catch((error) => {\n console.error(`[error] ${error.message || error}`);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-web-search",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "把任何网站变成命令行 API,专为 OpenClaw 设计,复用浏览器登录态",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site 36kr/newsflash"
11
+ "example": "ping-browser site 36kr/newsflash"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  "count": {"required": false, "description": "Number of results (default 10)"}
9
9
  },
10
10
  "readOnly": true,
11
- "example": "bb-browser site baidu/search \"Claude Code\""
11
+ "example": "ping-browser site baidu/search \"Claude Code\""
12
12
  }
13
13
  */
14
14
 
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "capabilities": ["network"],
13
13
  "readOnly": true,
14
- "example": "bb-browser site bilibili/comments BV1LGwHzrE4A"
14
+ "example": "ping-browser site bilibili/comments BV1LGwHzrE4A"
15
15
  }
16
16
  */
17
17
 
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "capabilities": ["network"],
11
11
  "readOnly": true,
12
- "example": "bb-browser site bilibili/feed"
12
+ "example": "ping-browser site bilibili/feed"
13
13
  }
14
14
  */
15
15
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site bilibili/history 10"
11
+ "example": "ping-browser site bilibili/history 10"
12
12
  }
13
13
  */
14
14
 
@@ -6,7 +6,7 @@
6
6
  "args": {},
7
7
  "capabilities": ["network"],
8
8
  "readOnly": true,
9
- "example": "bb-browser site bilibili/me"
9
+ "example": "ping-browser site bilibili/me"
10
10
  }
11
11
  */
12
12
 
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "capabilities": ["network"],
11
11
  "readOnly": true,
12
- "example": "bb-browser site bilibili/popular 10"
12
+ "example": "ping-browser site bilibili/popular 10"
13
13
  }
14
14
  */
15
15
 
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "capabilities": ["network"],
11
11
  "readOnly": true,
12
- "example": "bb-browser site bilibili/ranking --category 36"
12
+ "example": "ping-browser site bilibili/ranking --category 36"
13
13
  }
14
14
  */
15
15
 
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "capabilities": ["network"],
13
13
  "readOnly": true,
14
- "example": "bb-browser site bilibili/search 编程"
14
+ "example": "ping-browser site bilibili/search 编程"
15
15
  }
16
16
  */
17
17
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site bilibili/trending"
11
+ "example": "ping-browser site bilibili/trending"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site bilibili/video BV1LGwHzrE4A"
11
+ "example": "ping-browser site bilibili/video BV1LGwHzrE4A"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  "count": {"required": false, "description": "Number of results (default 10)"}
9
9
  },
10
10
  "readOnly": true,
11
- "example": "bb-browser site bing/search \"Claude Code\""
11
+ "example": "ping-browser site bing/search \"Claude Code\""
12
12
  }
13
13
  */
14
14
 
@@ -7,7 +7,7 @@
7
7
  "securityId": {"required": true, "description": "Job securityId (from boss/search results)"}
8
8
  },
9
9
  "readOnly": true,
10
- "example": "bb-browser site boss/detail <securityId>"
10
+ "example": "ping-browser site boss/detail <securityId>"
11
11
  }
12
12
  */
13
13
 
@@ -11,7 +11,7 @@
11
11
  "degree": {"required": false, "description": "Degree filter (e.g. 209=高中, 208=大专, 206=本科, 203=硕士, 201=博士)"}
12
12
  },
13
13
  "readOnly": true,
14
- "example": "bb-browser site boss/search \"AI agent\""
14
+ "example": "ping-browser site boss/search \"AI agent\""
15
15
  }
16
16
  */
17
17
 
@@ -8,7 +8,7 @@
8
8
  "page": {"required": false, "description": "Page number (default 1)"}
9
9
  },
10
10
  "readOnly": true,
11
- "example": "bb-browser site cnblogs/search \"Python\""
11
+ "example": "ping-browser site cnblogs/search \"Python\""
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  "page": {"required": false, "description": "Page number (default 1)"}
9
9
  },
10
10
  "readOnly": true,
11
- "example": "bb-browser site csdn/search \"Python\""
11
+ "example": "ping-browser site csdn/search \"Python\""
12
12
  }
13
13
  */
14
14
 
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "capabilities": ["network"],
12
12
  "readOnly": true,
13
- "example": "bb-browser site douban/comments 1292052"
13
+ "example": "ping-browser site douban/comments 1292052"
14
14
  }
15
15
  */
16
16
 
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "capabilities": ["network"],
12
12
  "readOnly": true,
13
- "example": "bb-browser site douban/movie-hot movie 豆瓣高分"
13
+ "example": "ping-browser site douban/movie-hot movie 豆瓣高分"
14
14
  }
15
15
  */
16
16
 
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "capabilities": ["network"],
11
11
  "readOnly": true,
12
- "example": "bb-browser site douban/movie-top 科幻 10"
12
+ "example": "ping-browser site douban/movie-top 科幻 10"
13
13
  }
14
14
  */
15
15
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site douban/movie 1292052"
11
+ "example": "ping-browser site douban/movie 1292052"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site douban/search 三体"
11
+ "example": "ping-browser site douban/search 三体"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site douban/top250"
11
+ "example": "ping-browser site douban/top250"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": false,
11
- "example": "bb-browser site github/fork epiral/bb-sites"
11
+ "example": "ping-browser site github/fork epiral/bb-sites"
12
12
  }
13
13
  */
14
14
 
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "capabilities": ["network"],
12
12
  "readOnly": false,
13
- "example": "bb-browser site github/issue-create epiral/bb-sites --title \"[reddit/me] returns empty\" --body \"Description here\""
13
+ "example": "ping-browser site github/issue-create epiral/bb-sites --title \"[reddit/me] returns empty\" --body \"Description here\""
14
14
  }
15
15
  */
16
16
 
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "capabilities": ["network"],
11
11
  "readOnly": true,
12
- "example": "bb-browser site github/issues epiral/bb-browser"
12
+ "example": "ping-browser site github/issues epiral/ping-browser"
13
13
  }
14
14
  */
15
15
 
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "capabilities": ["network"],
14
14
  "readOnly": false,
15
- "example": "bb-browser site github/pr-create epiral/bb-sites --title \"feat(weibo): add hot adapter\" --head myuser:feat-weibo --body \"Adds weibo/hot.js\""
15
+ "example": "ping-browser site github/pr-create epiral/bb-sites --title \"feat(weibo): add hot adapter\" --head myuser:feat-weibo --body \"Adds weibo/hot.js\""
16
16
  }
17
17
  */
18
18
 
@@ -4,11 +4,11 @@
4
4
  "description": "获取 GitHub 仓库信息",
5
5
  "domain": "github.com",
6
6
  "args": {
7
- "repo": {"required": true, "description": "owner/repo format (e.g. epiral/bb-browser)"}
7
+ "repo": {"required": true, "description": "owner/repo format (e.g. epiral/ping-browser)"}
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site github/repo epiral/bb-browser"
11
+ "example": "ping-browser site github/repo epiral/ping-browser"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  "count": {"required": false, "description": "Number of results (default 10)"}
9
9
  },
10
10
  "readOnly": true,
11
- "example": "bb-browser site google/search \"bb-browser\""
11
+ "example": "ping-browser site google/search \"ping-browser\""
12
12
  }
13
13
  */
14
14
 
@@ -7,7 +7,7 @@
7
7
  "count": {"required": false, "description": "返回条数 (默认 20, 最多 50)"}
8
8
  },
9
9
  "readOnly": true,
10
- "example": "bb-browser site toutiao/hot"
10
+ "example": "ping-browser site toutiao/hot"
11
11
  }
12
12
  */
13
13
 
@@ -44,7 +44,7 @@ async function(args) {
44
44
 
45
45
  async function fallbackFromHomepage(limit) {
46
46
  const homeResp = await fetch('https://www.toutiao.com/', {credentials: 'include'});
47
- if (!homeResp.ok) return {error: 'HTTP ' + homeResp.status, hint: 'Open www.toutiao.com in bb-browser first'};
47
+ if (!homeResp.ok) return {error: 'HTTP ' + homeResp.status, hint: 'Open www.toutiao.com in ping-browser first'};
48
48
 
49
49
  const html = await homeResp.text();
50
50
  const parser = new DOMParser();
@@ -99,7 +99,7 @@ async function(args) {
99
99
  }
100
100
 
101
101
  if (items.length === 0) {
102
- return {error: 'Could not extract hot topics', hint: 'Open www.toutiao.com in bb-browser first and make sure you are logged in'};
102
+ return {error: 'Could not extract hot topics', hint: 'Open www.toutiao.com in ping-browser first and make sure you are logged in'};
103
103
  }
104
104
 
105
105
  return {count: items.length, source: 'homepage_dom', items};
@@ -8,7 +8,7 @@
8
8
  "count": {"required": false, "description": "返回结果数量 (默认 10, 最多 20)"}
9
9
  },
10
10
  "readOnly": true,
11
- "example": "bb-browser site toutiao/search AI"
11
+ "example": "ping-browser site toutiao/search AI"
12
12
  }
13
13
  */
14
14
 
@@ -18,7 +18,7 @@ async function(args) {
18
18
 
19
19
  const url = 'https://so.toutiao.com/search?keyword=' + encodeURIComponent(args.query) + '&pd=information&dvpf=pc';
20
20
  const resp = await fetch(url, {credentials: 'include'});
21
- if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Open so.toutiao.com in bb-browser first'};
21
+ if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Open so.toutiao.com in ping-browser first'};
22
22
 
23
23
  const html = await resp.text();
24
24
  const parser = new DOMParser();
@@ -133,7 +133,7 @@ async function(args) {
133
133
  if (results.length === 0) {
134
134
  return {
135
135
  error: 'No results found',
136
- hint: 'Toutiao may require login or has anti-scraping protection. Try: 1) Open so.toutiao.com in bb-browser first, 2) Log in to toutiao, 3) Use toutiao/hot instead',
136
+ hint: 'Toutiao may require login or has anti-scraping protection. Try: 1) Open so.toutiao.com in ping-browser first, 2) Log in to toutiao, 3) Use toutiao/hot instead',
137
137
  query: args.query
138
138
  };
139
139
  }
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site xiaohongshu/comments 69aa7160000000001b01634d"
11
+ "example": "ping-browser site xiaohongshu/comments 69aa7160000000001b01634d"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site xiaohongshu/note 69aa7160000000001b01634d"
11
+ "example": "ping-browser site xiaohongshu/note 69aa7160000000001b01634d"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site xiaohongshu/search 美食"
11
+ "example": "ping-browser site xiaohongshu/search 美食"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site xiaohongshu/user_posts 5a927d8411be10720ae9e1e4"
11
+ "example": "ping-browser site xiaohongshu/user_posts 5a927d8411be10720ae9e1e4"
12
12
  }
13
13
  */
14
14
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "capabilities": ["network"],
10
10
  "readOnly": true,
11
- "example": "bb-browser site zhihu/hot 10"
11
+ "example": "ping-browser site zhihu/hot 10"
12
12
  }
13
13
  */
14
14
 
package/sites/zhihu/me.js CHANGED
@@ -6,7 +6,7 @@
6
6
  "args": {},
7
7
  "capabilities": ["network"],
8
8
  "readOnly": true,
9
- "example": "bb-browser site zhihu/me"
9
+ "example": "ping-browser site zhihu/me"
10
10
  }
11
11
  */
12
12
 
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "capabilities": ["network"],
11
11
  "readOnly": true,
12
- "example": "bb-browser site zhihu/question 34816524"
12
+ "example": "ping-browser site zhihu/question 34816524"
13
13
  }
14
14
  */
15
15
 
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "capabilities": ["network"],
11
11
  "readOnly": true,
12
- "example": "bb-browser site zhihu/search AI"
12
+ "example": "ping-browser site zhihu/search AI"
13
13
  }
14
14
  */
15
15