@streamapp/stream
v0.0.16
Published
Stream — a self-hostable information-flow layer: many sources into one inbox, exposed to AI over MCP
Downloads
2,204
Readme
Stream
把很多信息源(RSS / RSSHub 路由 / 小红书、B站、抖音这类站点 / 网盘 / 播客)汇进一个收件箱, 并把整个收件箱开放给 AI(MCP)的自托管信息流层。所有数据都在你自己机器上。
npx @streamapp/stream # 试一下
npm i -g @streamapp/stream && stream # 长期用需要 Node 20+(node -v 查;没有就去 https://nodejs.org 装 LTS,Windows 上
winget install OpenJS.NodeJS.LTS 也行)。除了 Node 别的什么都不用装——不需要 git、不需要
Docker、不需要 Python 或编译器(原生依赖有预编译包)。
开箱这一次是 96 个包 / 230MB / 十几秒。
装完打开 http://127.0.0.1:8900。
stream [--port <n>] [--data <dir>]
--port, -p <n> 监听端口(默认 8900)
--data <dir> 数据目录(默认 ~/.stream)- 数据全在
~/.stream:删掉它就是重置;卸载 = 删掉它 +npm rm -g @streamapp/stream。 - 只有一个端口:前端、
/api/*、/ws、插件全走 8900 这一扇门。 - 大件用到才装,不占开箱体积:RSSHub 在第一次跑到 RSSHub 源时装(约 400MB,43~147 秒, 看机器和网络)。
上手:五步,每步都有判据
下面每一步都给了一条能跑的命令和一个该看到的答案。判据不过就别往下走——这条链路上每一种 坏法都是安静的(采到游客态数据、流不排班、能力显示可用但一跑就失败),往下走只会把一个静默的 失败带到更远的地方。
1. 把 Chrome 扩展装上 —— 不装的后果是静默的
采集借你自己浏览器的登录态(Stream 不自带浏览器,也不碰你的密码)。没有扩展,小红书 / B 站 / 抖音这类站点只能拿到游客看得见的东西——不报错,只是采得少、采得浅。
打开 http://127.0.0.1:8900 首次会引导你装。手动装:
curl -s -X POST 127.0.0.1:8900/api/extension/materialize # → {"dir":"…/.stream/extension"}拿那个 dir 去 Chrome:chrome://extensions → 打开右上角开发者模式 → 加载已解压的扩展程序
→ 选那个目录。(Chrome 从 137 起移除了 --load-extension,命令行装不了,GUI 是唯一的路。)
扩展连后端之前要经一个本机小程序(host agent)拿一把钥匙。后端启动时自己把它登记进 Chrome 并拉起,
不用你装别的;启动日志里有一行 [stream-computer-use] host-agent → <路径> 就是登记成功。今天这个小程序只有
Windows 版(mac / Linux 上后端会记一行 warn,扩展配不上,采集只有游客态)。
判据(唯一可信的那个,别看 Chrome 里的图标):
curl -s 127.0.0.1:8900/api/browser-capability # → {"state":"ready","connected":true,…}"never-seen"= 从没连上过 → 还没装,或者装在了另一个 Chrome 上。"disconnected"= 装过、现在没连 → 点一下扩展图标叫醒它(Chrome 会让它休眠,后端重启后尤其)。
2. 订第一条流 —— 记得给它一个频道
先找源:
curl -s -G 127.0.0.1:8900/api/sources --data-urlencode "q=播客"members[].source 填它回的 id(真正必须对的是这一个),plugin 填它的 adapter
(rsshub / replay / builtin …):
curl -s -X POST 127.0.0.1:8900/api/streams -H 'content-type: application/json' -d '{
"id": "my-podcast",
"label": "故事FM",
"strategy": "fanout",
"cadence_seconds": 86400,
"options": {},
"channel_id": "default-audio",
"members": [{ "plugin": "replay", "source": "@streamapp/lizhi/lizhi-user",
"params": { "id": "2657184879512415276" } }]
}'channel_id 不是可选的讲究:一条不属于任何频道的流,这次会话在调度里、重启之后就没了
(开机只装载被某个频道引用的流)。要绑就在建流这一句里绑,别建完再补。频道用
curl -s 127.0.0.1:8900/api/channels 看,开箱自带四个(default-timeline / default-audio /
default-video / default-tasks)。
判据:
curl -s -X POST 127.0.0.1:8900/api/streams/my-podcast/refresh # → {"fetched":961,"written":961}fetched: 0 不是"没有新内容"。常见成因:那个 source 在本机解析不到、参数不对、缺登录态、
或者那条源坏了。去 curl -s "127.0.0.1:8900/api/debug/log?channel=harvest" 看这一轮的分阶段
结论——日志里连一条这个源的记录都没有,就是根本没跑到它,而不是跑了没结果。
3. 打开需要钥匙的能力(转写 / 认字 / 摘要)
先问它现在缺什么:
curl -s 127.0.0.1:8900/api/conversion-kinds
# extract 那一行的 branches: {"stt":false,"ocr":true,"article":true} ← stt 缺钥匙要语音转文字就配一把 Groq 的 key。Stream 可以替你去申请——它在你自己的 Chrome 里打开厂商 控制台、用你已经登录的账号建一把新 key,写进本地配置(不经过任何第三方)。
前提:先在那个 Chrome 里登录 https://console.groq.com(Groq 支持用 Google 账号登录,免费额度 够用)。这一步用的就是你现成的登录态——没登录的话它会停在登录页,这不是失败,是缺前提。
curl -s -X POST 127.0.0.1:8900/api/source-runtime-config/provision \
-H 'content-type: application/json' \
-d '{"pluginId":"builtin","sourceId":"groq-whisper","params":{"name":"stream-auto-7f3a"}}'
# → secrets.apiKey.configured: true 实测约 17 秒自己去 https://console.groq.com/keys 拿一把、用 PUT /api/source-runtime-config 填进去也行。
配完不用重启,下一次问就变了:
curl -s 127.0.0.1:8900/api/conversion-kinds # branches.stt → true转写一集:
curl -s -X POST 127.0.0.1:8900/api/conversions -H 'content-type: application/json' \
-d '{"kind":"extract","item":"<item id>"}' # → {"id":"cv_…","status":"running"}
curl -s 127.0.0.1:8900/api/conversions/cv_… # 轮到 status:"done",result.text 就是文字稿一小时的播客约 4~5 分钟(取媒体和重编码占大头,真正的识别只要几十秒)。
4. 接一个对话宿主(可选)
Stream 自己没有对话,用你已有的 agent。三步,第三步才是可选的:
一、装 Stream。 第 1 步已经做完了(npm i -g @streamapp/stream && stream)。
装了它就有电脑操作——用户自己那个已登录的 Chrome,加上原生桌面窗口。
二、宿主那边配一行,指向 Stream。
claude mcp add stream -- stream mcp # Codex 是 config.toml 的 mcp_servers 一行stream mcp 是一层 stdio 壳:它探一次本机的后端,在场就整面转发到 /api/mcp,不在场就先把
后端拉起来再转发。这一行此后不用再改——往 Stream 里加多少能力,工具都从同一个口出去。
三、想要更多能力就往里加:
stream add @streamapp/netdisk # 网盘:验分享 / 转存 / 直链 / 跳转
stream add @streamapp/meituan # 美团:领券 / 到店团购搜索 / 下单
stream remove @streamapp/netdisk # 不想要了能力包是 Stream 包的一格槽位(package.json#stream.capability),装进 <dataDir>/recipes/
后由后端在自己进程里挂上——登录态不出这个进程。界面上的「组件」页里点装是同一条路。
装完要重启后端才生效(安装那一刻只落盘),装上了没有的话先重启再排查。
再往下:第 5 节把 Stream 的 skill 装给你的 agent(MCP 给工具,skill 给"什么时候用哪个")。
DSH 用户多一样:Stream UI bundle
DSH 装上它之后整张脸就是 Stream(内容流 + 对话)。它也读第 5 节装的那批 skill(同一个
~/.agents/skills/ 目录):
npm i -g @deepseek-ai/[email protected] # 引擎版本要对得上:这份 bundle 按 0.1.2 的客户端模块表构建
# 装进已经带 web 界面的那个 profile($DSH_HOME/profiles/web)——bundle 关掉的是 web-app 的整页壳,
# 新建的空 profile 里没有它可关。想单独留一个 profile 给 Stream:先 cp -r profiles/web profiles/stream,再把下面的 web 换成 stream。
dsh plugin --profile web add @streamapp/dsh-plugin-stream-ui
dsh web # 单独的 profile 就是 dsh --profile stream这份 bundle 是唯一要装进 DSH profile 的东西;能力包一律装进 Stream,DSH 那边一个字都不用改。
Stream 后端照常跑着就行(stream),那张页在本机哪个口上都不用告诉它——本机来源默认可信。
后端和 DSH 不在同一台机器时才需要登记:STREAM_TRUSTED_ORIGINS=http://<那台机器>:<口> stream。
模型是你自己的:在 DSH 的「设置 - 模型」页配 provider;Stream 不参与。 前面三步都不依赖它——收集、采集、转写都不用模型。
接给别的 AI 客户端(MCP)
不走 stream mcp 也行:后端跑着就直接用 HTTP 这一档 http://127.0.0.1:8900/api/mcp
(设了 api_token 才需要 Authorization: Bearer …)。不想让后端常驻就用 stdio 那一档,
客户端按需拉起进程。三条路的工具集一样。
5. 把 Stream 的 skill 装进你自己的 agent(Claude Code / Codex)
上一步给的是工具,这一步给的是手艺——什么时候用哪个、按什么顺序、什么算数。
curl -s -X POST 127.0.0.1:8900/api/skills/install它把随包出货的 skill 刷进 ~/.stream/skills/,再从 ~/.claude/skills/(Claude Code)和
~/.agents/skills/(Codex)链接过去。是链接不是拷贝——升级之后两边同时变新。
判据:
curl -s 127.0.0.1:8900/api/skills # hosts[].landings[].mode 都是 "link"Claude Code 里敲 /stream- 看得到它们,Codex 里是 $stream-。名字一律带 stream- 前缀,
不会顶掉你自己的同名 skill;那个位置已经有别的东西就跳过并说明。撤销用
POST /api/skills/uninstall(只删它自己建的那些)。mode 报 "copy" 说明这台机器建不出
符号链接——功能一样,但升级后要再跑一次 install。
出问题时先跑这几句
以副作用为准,不要以"应该好了"为准——每一种坏法都是安静的。
| 问 | 命令 | 绿的样子 |
|---|---|---|
| 后端活着吗 | curl -s 127.0.0.1:8900/api/health | {"ok":true} |
| 采集的手在不在 | curl -s 127.0.0.1:8900/api/browser-capability | "state":"ready" |
| 这条流在调度里吗 | curl -s 127.0.0.1:8900/api/streams | 看得到你建的那个 id |
| 它真采到东西了吗 | POST /api/streams/<id>/refresh | fetched > 0 且 written > 0 |
| 这个能力现在能用吗 | curl -s 127.0.0.1:8900/api/conversion-kinds | 那条的 available / branches.* 为 true |
| 我自己写的 recipe 装载了吗 | curl -s 127.0.0.1:8900/api/recipes/local | ok: true(dir 就是该往哪写;没装载会带 error 原文) |
三个最容易误判的地方:
fetched: 0不等于「没有新内容」(见第 2 步)。- 建流不带
channel_id:这次能跑,重启后消失。 - 「配好了」不等于「能用了」:判据是那条能力自己的自述(
/api/conversion-kinds), 不是配置那一格的configured。
MIT · 数据全在本机,不上传任何地方。
