OmniHub
One hub for every source your agent needs.
配置驱动、按平台组织的 Agent 互联网信息中枢:把任意网页与平台统一成
search / read 结果,交给 Agent 直接消费。
当前完成 M4 并进入 M5:M0 契约层(目录发现、manifest 校验、两级
优先级路由)+ M1 执行层(HTTP/CLI/OpenCLI 适配器、web 公共网页平台)+
M2 平台批次一(github、v2ex、bilibili)+ M3 有状态工具与按需抓取
(kimi-webbridge daemon 适配器、search --enrich N)+ M4 插件校验与
信任、explain 来源明细、SKILL.md 生成、平台批次二(reddit、
youtube)+ M5 平台自声明的 check live 回归与统一限流/冷却 + 平台批次三
(twitter、xueqiu、xiaohongshu、xiaoyuzhou、linkedin,浏览器
browse 复合动作)+ 工具型平台(deepwiki 公开 MCP、notebooklm 本地
CLI)+ 微信公众号(wechat:Exa 搜索、Sogou 浏览器兜底、登录态浏览器
读取)。CLI 已支持 platforms、doctor、explain、plugin、
skill、check、cooldowns、search 和 read。剩余平台、MCP 适配器详见
docs/implementation.md。
快速开始
cd services/omnihub
python -m pytest -q
# 列出发现的平台(JSON 是默认输出)
python -m omnihub.cli platforms
# 解释一次请求会走哪些平台
python -m omnihub.cli explain --platform web --action search
python -m omnihub.cli explain --url https://www.example.com/a --action read
# 搜索与按需读取
python -m omnihub.cli search "yt-dlp github" --platform web --limit 3
python -m omnihub.cli read https://example.com --platform web
# 搜索后自动补正文(失败隔离,失败项进 meta.enrich_errors)
python -m omnihub.cli search "example domain" --limit 3 --enrich 2
# 站点平台
python -m omnihub.cli search "flask" --platform github --limit 3
python -m omnihub.cli read https://github.com/pallets/flask/issues/42 --text
python -m omnihub.cli search "python 限流" --platform v2ex --limit 3
python -m omnihub.cli read https://www.v2ex.com/t/1 --text
python -m omnihub.cli search "rust 教程" --platform bilibili --limit 3
python -m omnihub.cli read https://www.bilibili.com/video/BV1S1dPBYEG9 --text
python -m omnihub.cli search "asyncio" --platform reddit --limit 3
python -m omnihub.cli read https://www.reddit.com/r/Python/comments/j531n0/an_introduction_to_async_programming_python/ --text
python -m omnihub.cli search "asyncio tutorial" --platform youtube --limit 3
python -m omnihub.cli read https://www.youtube.com/watch?v=oAkLSJNr5zY --text
# 登录态平台(需要本机 kimi-webbridge daemon 与浏览器登录)
python -m omnihub.cli read https://www.bilibili.com/video/BV1S1dPBYEG9 --text
python -m omnihub.cli search "openai" --platform twitter --limit 3
python -m omnihub.cli read https://x.com/user/status/1 --text
python -m omnihub.cli search "茅台" --platform xueqiu --limit 3
python -m omnihub.cli search "咖啡" --platform xiaohongshu --limit 3
python -m omnihub.cli search "科技" --platform xiaoyuzhou --limit 3
python -m omnihub.cli transcribe https://www.xiaoyuzhoufm.com/episode/6adcced4c96c8942ec11a649 --text
python -m omnihub.cli search "python" --platform linkedin --limit 3
python -m omnihub.cli read https://www.linkedin.com/jobs/view/4379639599/ --text
python -m omnihub.cli search "人工智能" --platform wechat --limit 3
python -m omnihub.cli read https://mp.weixin.qq.com/s/nN5HH8JGVH9IQvuM5m-bIw --text
# 工具型平台
python -m omnihub.cli read facebook/react --platform deepwiki --text
python -m omnihub.cli search "asyncio" --platform notebooklm --limit 3
python -m omnihub.cli read nbb0987654321 --platform notebooklm --text
# 检查工具、适配器、凭据与登录提示(不发起网络请求)
python -m omnihub.cli doctor --text
# 平台自声明的 live 回归(--dry-run 只列出计划,不联网)
python -m omnihub.cli check --dry-run --text
python -m omnihub.cli check --platform github --platform reddit --text
# 查看 / 清除本地冷却账本(显式操作,不会自动绕过限流)
python -m omnihub.cli cooldowns --text
python -m omnihub.cli cooldowns reset --platform github --text
# 第三方插件:只校验、不执行;生成给 Agent 用的 SKILL.md
python -m omnihub.cli plugin validate ~/.omnihub/plugins
python -m omnihub.cli plugin list --text
python -m omnihub.cli skill --output SKILL.md
# 离线契约示例平台
python -m omnihub.cli search hello --platform noop --limit 2 --text
# 安装后也可用 omnihub 命令
pip install -e .
omnihub platforms --text
小红书 kimi-webbridge CLI
除了平台化的 search/read,仓库还提供了一个可独立使用的小红书 CLI。它在
用户已登录的真实浏览器里执行搜索、捕获笔记的 xsec_token、再读取正文,
并提供作者主页兜底与 token 缓存:
# 安装后使用 xiaohongshu-cli;未安装则用 python -m
python -m omnihub.platforms.xiaohongshu.scripts.xiaohongshu_cli login-status
python -m omnihub.platforms.xiaohongshu.scripts.xiaohongshu_cli search "西安 红叶" --limit 5
python -m omnihub.platforms.xiaohongshu.scripts.xiaohongshu_cli read 6addcd87000000002c00cb5a
python -m omnihub.platforms.xiaohongshu.scripts.xiaohongshu_cli read <note-id> --author-url "<作者主页 URL>"
输出统一为 {"ok": true, "data": ...},错误时为
{"ok": false, "error": {"code", "message"}} 并以非零码退出。token 缓存在
$XDG_CACHE_HOME/xiaohongshu-cli/notes.json(0600,不保存查询词)。平台技能入口
见 omnihub/platforms/xiaohongshu/SKILL.md,
CLI 的完整参数与错误码见
references/xiaohongshu-cli.md。
web 是公共网页兜底平台:搜索走 Exa hosted MCP(无需 Key),读取依次
尝试 Jina Reader、kimi-webbridge 真实浏览器(daemon 可用时)、Exa。它的
默认优先级是 10,因此普通 search 默认走公共网页;站点平台(github
100、bilibili 110、v2ex 120、reddit 130、youtube 140、twitter
150、xueqiu 160、xiaohongshu 170、xiaoyuzhou 180、linkedin 190、
deepwiki 200、notebooklm 210、wechat 220)通过 --platform,或
routing 里自定义的 intents 使用。read 按 URL 域名
先选站点平台,再自动把 web 作为最后一个来源。
github:REST 搜索仓库;读取仓库(含 README)、issue、PR。设置GITHUB_TOKEN或GH_TOKEN可提高限额并访问私有仓库。v2ex:SOV2EX 搜索主题,V2EX 公共 API 读取主题和第一页回复。bilibili:Exasite:bilibili.com搜索;read通过 kimi-webbridge 在 已登录浏览器里解析视频cid和字幕签名地址,再下载 AI/CC 字幕 JSON, 输出带标题、UP 主、发布时间和逐条字幕正文的 item(存在多语言轨道时每条 轨道一个 item)。字幕默认以.txt+.srt写入当前目录的_raw/(可用OMNIHUB_RAW_DIR覆盖),文件路径记录在 item 的raw.saved_files。不需要BILIBILI_COOKIE;daemon 或登录态缺失时read记为SKIP,search不受影响。reddit:Exasite:reddit.com搜索;Exa fetch 读取公开帖子与高赞评论, daemon 可用时可用真实浏览器兜底(Reddit 的匿名 JSON 与 Jina 都返回 403)。youtube:Exasite:youtube.com搜索;Exa fetch 读取页面正文/字幕文本, daemon 可用时可用真实浏览器兜底。twitter/xueqiu/xiaohongshu/linkedin:搜索与读取都通过 kimi-webbridge 在用户已登录的真实浏览器里执行站点 JavaScript。平台目录 只保存选择器与提取逻辑,browse复合动作负责导航、等待选择器、登录/风控 标记和结果限流。daemon 不可用时这些平台记为degraded,不会让doctor失败。Twitter 另有 Exasite:x.com搜索兜底。- 标签页生命周期:每次
read/browse使用独立 session,结束后(成功、 失败或超时)都会关闭该 session 的全部标签页;并发查询不会互相关闭对方的 标签,超时恢复也只接管omnihub:分组内的标签,不会动用户自己打开的页面。 xiaoyuzhou:Exasite:xiaoyuzhoufm.com搜索;读取优先 Jina,daemon 可用时用真实浏览器兜底,最后再走 Exa fetch。transcribe从单集页面 提取公开音频地址,经 ffmpeg 压缩切片后调用 Whisper 兼容接口(Groq / OpenAI)转文字,--polish再让 Groq Llama 补中文标点。需要ffmpeg与GROQ_API_KEY或OPENAI_API_KEY(环境变量或~/.omnihub/secrets.yaml);密钥缺失时返回NEED_SETUP,不会影响search/read。- 小红书的读取链接必须带搜索结果里的
xsec_token;token 会过期,因此check只对搜索做 live 回归。 wechat:搜索优先走 Exasite:mp.weixin.qq.com,直接返回文章原文链接; Exa 不可用时回退到 Sogou 微信搜索的浏览器页面(结果为 Sogou 跳转链接, 只在执行搜索的浏览器 session 内可解析)。read通过 kimi-webbridge 打开 文章页:先在一个轻量标签页上应用移动端微信 UA(Emulation.setUserAgentOverride), 再加载目标链接,避免微信对桌面/自动化 UA 返回“环境异常”验证页;随后提取#js_content、标题、公众号名和发布时间。文章 URL 可能随时被删除,因此check只对搜索做 live 回归。deepwiki:读取公开仓库的 DeepWiki 文档。直接 POST 官方 MCP 端点read_wiki_contents,无需 Node 或 API Key;接受owner/repo、GitHub 仓库 URL 或 deepwiki.com URL。未索引的仓库返回NOT_FOUND并提示先在 deepwiki.com 打开一次。notebooklm:搜索与读取本机 Google NotebookLM。search用notebooklm list --json列出并按标题过滤,read用notebooklm summary --json --topics返回摘要和建议问题。需要本机安装notebooklmCLI(pip install notebooklm-py)并已完成notebooklm login;CLI 缺失时平台标记为degraded,来源自动SKIP。
目录
omnihub/
cli.py # platforms / doctor / explain / check / cooldowns / search / read / transcribe
transcribe.py # ffmpeg 压缩切片 + Groq/OpenAI Whisper 音频转写
core/
config.py # 分层 routing 配置
context.py # 子进程、HTTP、secret;超时/输出上限
doctor.py # 工具/适配器/凭据/登录态体检(不联网)
normalize.py # item 归一、URL 规范化、聚合去重
pipeline.py # 路由 → 来源降级 → 解析 → envelope
policy.py # 超时、输出上限、URL 安全、错误分类
registry.py # 扫描/校验/索引 platform.yaml
result.py # Item / envelope / error codes(复用 Agent-Reach)
router.py # 显式平台 / URL / intent / priority
schema/ # JSON Schema
template.py # ${param} / ${secret:NAME} / 过滤器模板
enrich.py # search --enrich N:并发补正文、失败隔离
plugins.py # 第三方插件校验(不执行代码)
skill.py # 由 registry 生成 SKILL.md
adapters/
base.py # Adapter / ToolCall / RawResult 协议
builtin.py # 平台目录内可信 Python hooks
http.py / cli.py / opencli.py
daemon.py # kimi-webbridge:/status、session、cursor 分页、browse 站点脚本
parse/
generic.py # JSON / YAML / OpenCLI / text 解析链
web.py # 共享 Exa / Jina 解析器
daemon.py # 浏览器读取结果 → 统一 item
platforms/noop/ # 最小离线平台(契约测试/示例)
platforms/web/ # Exa + Jina 公共网页平台
platforms/github/ # GitHub 搜索、仓库/issue/PR 读取
platforms/v2ex/ # SOV2EX 搜索、主题与回复读取
platforms/bilibili/ # Exa 搜索、浏览器解析 + 字幕 JSON 读取
platforms/reddit/ # Exa 搜索与帖子读取、浏览器兜底
platforms/youtube/ # Exa 搜索与视频页/字幕读取、浏览器兜底
platforms/twitter/ # 登录态浏览器搜索与推文读取(Exa 搜索兜底)
platforms/xueqiu/ # 登录态浏览器讨论搜索与状态读取
platforms/xiaohongshu/ # 登录态浏览器笔记搜索与读取;scripts/xiaohongshu_cli.py + references/
platforms/xiaoyuzhou/ # Exa 搜索、Jina/浏览器/Exa 读取、Whisper 转写
platforms/linkedin/ # 浏览器内 guest API 职位搜索与职位详情
platforms/wechat/ # Exa/Sogou 搜索、移动 UA 浏览器读取公众号文章
platforms/deepwiki/ # 公开 MCP 读取仓库 DeepWiki 文档
platforms/notebooklm/ # 本地 notebooklm CLI:笔记本搜索与摘要
utils/ # URL、文本脱敏、子进程环境(复用 Agent-Reach)
平台即插件
每个平台是一个目录,platform.yaml 是唯一必需文件。删除目录即下线,
不需要改核心代码。platform.yaml 只放配置,不放实现:
- 需要代码时,实现文件放在平台的
scripts/下,并由 YAML 显式引用:builtin来源写script: scripts/search.py,daemon的browse来源写script: scripts/search.js,自定义解析器写顶层parse_script: scripts/parse.py。 - 脚本路径必须是
scripts/下的相对路径;..、绝对路径、反斜杠和符号 链接逃逸在加载期即报错。 - 每个平台都有一个手写
SKILL.md,封装本平台的触发场景、命令、参数、 来源顺序与错误码。平台因此既能单独作为 Skill 使用,也能通过 OmniHub 与其他平台协作完成多源搜索与内容提取。 - 未声明
script的第三方插件仍按旧约定回退到平台根目录的同名文件; 内置平台全部使用scripts/布局,并由测试禁止内联 JavaScript。
id: example
description: Example platform
level: default # default | optional | experimental
priority: 20 # 数字越小越优先
domains: [example.com]
actions:
search:
params:
query: {type: str, required: true}
limit: {type: int, default: 5}
sources:
- adapter: cli
name: example-cli
tool: example
call: search
args: {query: "${query}", limit: "${limit}"}
parse: json_items
priority: 10
来源按 priority 升序尝试;disabled: true 可单独关闭一个来源。
平台级和来源级优先级互相独立。first_success 遇到空结果或非终态错误会
继续下一个来源;NEED_LOGIN、RATE_LIMITED、PLATFORM_BLOCKED、
BAD_INPUT 等终态错误会立即停止。
可选来源用 when: secret:NAME、when: tool:NAME 或
when: adapter:NAME 声明:条件不满足时记为 SKIP,不会让整个平台失败。
adapter:daemon 表示“该适配器的健康探针通过才运行”,例如 web 的
kimi-webbridge 读取来源。平台的 auth.secret 表示“可选登录凭据”,缺失时
doctor 提示但仍使用公共来源;requires.secrets 则表示必需凭据。
第三方插件
插件放在 $OMNIHUB_PLUGIN_PATH、~/.omnihub/plugins/<id>/ 或
./.omnihub/plugins/<id>/,同名覆盖内置平台。纯清单插件直接加载;
带 Python/JavaScript 实现的插件(scripts/*.py、scripts/*.js,或遗留的
根目录 search.py / read.py / parse.py / transcribe.py)是在用户权限下执行,
因此必须声明 capabilities(http / exec / browser,纯计算写 [])
并且显式信任:
python -m omnihub.cli plugin validate ~/.omnihub/plugins
python -m omnihub.cli search "query" --trust-plugin my-plugin
# 或 OMNIHUB_TRUSTED_PLUGINS=my-plugin
未信任时插件仍会出现在 platforms 里,但运行其代码会返回 NEED_SETUP;
plugin validate 会给出提醒。explain 会列出每个平台的来源顺序、
adapter、when 与 parse,skill 从 registry 生成文档,两者都不会
输出凭据或来源 args。
限流与冷却
失败会写入跨进程的冷却账本,下一次请求在真正发出网络/命令调用前就会跳过:
| 错误码 | 范围 | 默认时长 |
|---|---|---|
RATE_LIMITED |
整个平台(所有来源与动作) | 300s,优先使用 Retry-After,上限 6h |
PLATFORM_BLOCKED |
整个平台 | 900s |
TIMEOUT / UPSTREAM_BROKEN / NEED_SETUP |
单个来源 | 60s / 120s / 60s |
PLATFORM_BLOCKED 表示验证码或风控,RATE_LIMITED 是终止码:不会切到
下一个平台。单来源暂停只跳过该来源,同平台其他来源和其他平台仍可继续。
被跳过的尝试在 meta.attempts[] 里带 result: SKIP、reason 和
retry_after_seconds;最终错误也会在 error.retry_after_seconds 中给出
剩余秒数。成功调用会清除该来源的暂停;平台冷却只会自然过期。
账本位于 ~/.omnihub/state/rate_limits.json(目录 0700、文件 0600),
可用 OMNIHUB_STATE_DIR 改写位置。文件只保存平台/来源名与时间戳,不保存
查询、URL 或凭据;损坏或不可写时直接忽略,不会阻塞请求。
doctor 会在对应平台下输出 cooling 行;cooldowns 命令列出全部未过期
条目,cooldowns reset [--platform ID] 在确认上游已恢复后显式清除。重置
不影响 min_interval 的 pacing 时间戳,也不会自动绕过限流。check 把只被
账本跳过(没有真正发起调用)的项标为 cooling,不计入失败;网络返回的
429 仍记为失败。
平台可在来源上声明 min_interval(秒),用于连续调用同一来源时的最小
间隔;核心把它限制在 10s 以内,超出的部分只会等待,不会报错:
sources:
- adapter: http
name: exa
min_interval: 1.5
路由配置
按以下顺序合并,后者覆盖前者:
- 内置默认值
~/.omnihub/config/routing.yaml./.omnihub/config/routing.yaml$OMNIHUB_ROUTING--routing FILE
version: 1
mode: first_success # first_success | aggregate
intents:
code: [github, web]
platforms:
github: {enabled: true, priority: 5}
aggregate:
top: 3
per_platform_limit: 5
dedupe: true
安全策略(超时上限、输出上限、URL 白名单与 DNS 解析校验)不在 routing
配置里,平台不能通过 YAML 放宽这些限制。发起 HTTP 前会拒绝解析到
loopback/私网地址的公网域名,重定向后同样复检。凭据通过 ${secret:NAME} 从环境变量或
~/.omnihub/secrets.yaml 注入,不写进 manifest 输出。
设计文档
docs/implementation.md— 完整设计、契约、迁移计划docs/migration.md— 从旧 channel 迁移、新增/删除平台docs/release.md— 0.1.0 发布验收记录与已知限制THIRD_PARTY.md— 复用代码与许可
License
MIT。复用的上游代码版权与许可见 LICENSE 和 THIRD_PARTY.md。
Metadata
Release files for omnihub 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| omnihub-0.1.0.tar.gz | 232.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| omnihub-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 427.4 kB
Release files / omnihub-0.1.0.tar.gz
| Download URL | omnihub-0.1.0.tar.gz |
|---|---|
| Size | 232.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9af07be369a53d522003a55a1da3834c25bd30032f672ceb5203ba09dad81130
|
|
BLAKE2b-256 checksum How to use checksums |
c8a907b53f0ae89ee2de70ef6a9da8c43d65263741e59b8b76db17fbd031a5f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / omnihub-0.1.0-py3-none-any.whl
| Download URL | omnihub-0.1.0-py3-none-any.whl |
|---|---|
| Size | 195.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df964ea5dbe4539f60abf1ff9c8a78ff3088d8f8a8d888c2b21fa022c8d7f597
|
|
BLAKE2b-256 checksum How to use checksums |
6b68534ced03e797c73672a27fefe887910dfde3907ba5734b17d6b6a0197f4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for omnihub 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.