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.
- package/README.md +42 -64
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/sites/36kr/newsflash.js +1 -1
- package/sites/baidu/search.js +1 -1
- package/sites/bilibili/comments.js +1 -1
- package/sites/bilibili/feed.js +1 -1
- package/sites/bilibili/history.js +1 -1
- package/sites/bilibili/me.js +1 -1
- package/sites/bilibili/popular.js +1 -1
- package/sites/bilibili/ranking.js +1 -1
- package/sites/bilibili/search.js +1 -1
- package/sites/bilibili/trending.js +1 -1
- package/sites/bilibili/video.js +1 -1
- package/sites/bing/search.js +1 -1
- package/sites/boss/detail.js +1 -1
- package/sites/boss/search.js +1 -1
- package/sites/cnblogs/search.js +1 -1
- package/sites/csdn/search.js +1 -1
- package/sites/douban/comments.js +1 -1
- package/sites/douban/movie-hot.js +1 -1
- package/sites/douban/movie-top.js +1 -1
- package/sites/douban/movie.js +1 -1
- package/sites/douban/search.js +1 -1
- package/sites/douban/top250.js +1 -1
- package/sites/github/fork.js +1 -1
- package/sites/github/issue-create.js +1 -1
- package/sites/github/issues.js +1 -1
- package/sites/github/pr-create.js +1 -1
- package/sites/github/repo.js +2 -2
- package/sites/google/search.js +1 -1
- package/sites/toutiao/hot.js +3 -3
- package/sites/toutiao/search.js +3 -3
- package/sites/xiaohongshu/comments.js +1 -1
- package/sites/xiaohongshu/note.js +1 -1
- package/sites/xiaohongshu/search.js +1 -1
- package/sites/xiaohongshu/user_posts.js +1 -1
- package/sites/zhihu/hot.js +1 -1
- package/sites/zhihu/me.js +1 -1
- package/sites/zhihu/question.js +1 -1
- 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
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
|
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
|
|
24
|
+
bws site zhihu/hot # 知乎热榜
|
|
43
25
|
bws site bilibili/popular # B站热门视频
|
|
44
26
|
```
|
|
45
27
|
|
|
46
|
-
##
|
|
47
|
-
|
|
48
|
-
|
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
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
|
-
#
|
|
65
|
+
# B站热门视频 JSON
|
|
84
66
|
bws site bilibili/popular --json
|
|
85
67
|
|
|
86
|
-
#
|
|
68
|
+
# GitHub 仓库信息
|
|
87
69
|
bws site github/repo facebook/react
|
|
88
70
|
```
|
|
89
71
|
|
|
90
|
-
##
|
|
72
|
+
## 登录态
|
|
91
73
|
|
|
92
|
-
|
|
74
|
+
需要登录的网站,先在 OpenClaw 浏览器中登录:
|
|
93
75
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
openclaw browser open https://xiaohongshu.com
|
|
98
|
-
```
|
|
99
|
-
3. 重试命令
|
|
76
|
+
```bash
|
|
77
|
+
openclaw browser open https://xiaohongshu.com
|
|
78
|
+
```
|
|
100
79
|
|
|
101
|
-
##
|
|
80
|
+
## 自定义 Adapter
|
|
102
81
|
|
|
103
|
-
|
|
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
|
-
|
|
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
package/sites/36kr/newsflash.js
CHANGED
package/sites/baidu/search.js
CHANGED
package/sites/bilibili/feed.js
CHANGED
package/sites/bilibili/me.js
CHANGED
package/sites/bilibili/search.js
CHANGED
package/sites/bilibili/video.js
CHANGED
package/sites/bing/search.js
CHANGED
package/sites/boss/detail.js
CHANGED
package/sites/boss/search.js
CHANGED
|
@@ -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": "
|
|
14
|
+
"example": "ping-browser site boss/search \"AI agent\""
|
|
15
15
|
}
|
|
16
16
|
*/
|
|
17
17
|
|
package/sites/cnblogs/search.js
CHANGED
package/sites/csdn/search.js
CHANGED
package/sites/douban/comments.js
CHANGED
package/sites/douban/movie.js
CHANGED
package/sites/douban/search.js
CHANGED
package/sites/douban/top250.js
CHANGED
package/sites/github/fork.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"capabilities": ["network"],
|
|
12
12
|
"readOnly": false,
|
|
13
|
-
"example": "
|
|
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
|
|
package/sites/github/issues.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"capabilities": ["network"],
|
|
14
14
|
"readOnly": false,
|
|
15
|
-
"example": "
|
|
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
|
|
package/sites/github/repo.js
CHANGED
|
@@ -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/
|
|
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": "
|
|
11
|
+
"example": "ping-browser site github/repo epiral/ping-browser"
|
|
12
12
|
}
|
|
13
13
|
*/
|
|
14
14
|
|
package/sites/google/search.js
CHANGED
package/sites/toutiao/hot.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"count": {"required": false, "description": "返回条数 (默认 20, 最多 50)"}
|
|
8
8
|
},
|
|
9
9
|
"readOnly": true,
|
|
10
|
-
"example": "
|
|
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
|
|
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
|
|
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};
|
package/sites/toutiao/search.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"count": {"required": false, "description": "返回结果数量 (默认 10, 最多 20)"}
|
|
9
9
|
},
|
|
10
10
|
"readOnly": true,
|
|
11
|
-
"example": "
|
|
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
|
|
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
|
|
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
|
}
|
package/sites/zhihu/hot.js
CHANGED
package/sites/zhihu/me.js
CHANGED
package/sites/zhihu/question.js
CHANGED