From 7436910cbec16f63683c2462217b7f451dee2033 Mon Sep 17 00:00:00 2001 From: PathGao <42336971+PathGao@users.noreply.github.com> Date: Wed, 11 Nov 2026 15:57:58 +0800 Subject: [PATCH 1/6] =?UTF-8?q?MCP=EF=BC=9A=E4=B8=80=E4=B8=AA=E6=80=BB?= =?UTF-8?q?=E5=BC=80=E5=85=B3=EF=BC=8C=E7=9B=B4=E6=8E=A5=E6=94=B9=20B?= =?UTF-8?q?=E7=AB=99=E6=94=B6=E8=97=8F=E5=B9=B6=E5=8F=AF=E6=92=A4=E9=94=80?= =?UTF-8?q?=EF=BC=8C=E8=AF=BB=E8=A7=86=E9=A2=91=E6=94=B9=E8=B5=B0=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E5=8A=A0=E5=90=8C=E6=AD=A5=E3=80=81=E6=95=B4?= =?UTF-8?q?=E4=BB=BD=E5=88=97=E8=A1=A8=E3=80=81=E5=B0=81=E9=9D=A2=E6=8B=BC?= =?UTF-8?q?=E5=9B=BE=E5=92=8C=E5=88=A4=E6=96=AD=E6=A0=87=E5=87=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 33 ++ PRIVACY.md | 13 +- README.md | 8 +- docs/AGENT.md | 68 +++- docs/ARCHITECTURE.md | 3 +- docs/DESIGN.md | 4 +- docs/RELEASE.md | 7 + docs/TRIAGE.md | 10 +- extension/agent-bridge.js | 37 +- extension/agent-bridge.selftest.js | 45 +++ extension/agent-collections.js | 5 - extension/agent-collections.selftest.js | 19 +- extension/agent-covers.js | 112 ++++++ extension/agent-covers.selftest.js | 216 +++++++++++ extension/agent-mydata.js | 66 +++- extension/agent-mydata.selftest.js | 45 ++- extension/agent-sync.js | 113 ++++++ extension/agent-video.js | 77 ++++ extension/agent-video.selftest.js | 168 +++++++++ extension/agent-writes.js | 477 +++++++++++++++++++++--- extension/agent-writes.selftest.js | 328 +++++++++++++--- extension/background.js | 25 +- extension/background.selftest.js | 14 + extension/entry-matrix.selftest.js | 3 + extension/limits.js | 7 +- extension/limits.selftest.js | 2 +- extension/load-order.selftest.js | 3 +- extension/manifest.json | 2 +- extension/messages.js | 4 +- extension/options.css | 33 +- extension/options.html | 20 +- extension/options.js | 76 +++- extension/options.selftest.js | 27 +- extension/settings.js | 9 +- extension/sites.js | 12 +- extension/sites.selftest.js | 21 ++ extension/triage/fav-base.js | 17 +- extension/triage/fav-folders.js | 22 +- extension/triage/fav-page.js | 10 +- extension/triage/fav-records.js | 127 +++++++ extension/triage/fav-writes.js | 145 ++----- extension/triage/triage-bg.js | 3 +- extension/triage/triage.html | 1 + extension/triage/triage.selftest.js | 81 +--- mcp/README.md | 168 +++++---- mcp/package.json | 14 + mcp/server.js | 198 ++++++++-- mcp/test/server.test.mjs | 70 +++- skills/moondigest/SKILL.md | 41 +- 49 files changed, 2370 insertions(+), 639 deletions(-) create mode 100644 extension/agent-covers.js create mode 100644 extension/agent-covers.selftest.js create mode 100644 extension/agent-sync.js create mode 100644 extension/agent-video.js create mode 100644 extension/agent-video.selftest.js create mode 100644 extension/triage/fav-records.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b6d9bad..f86749c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,3 +89,36 @@ jobs: run_op -X POST -H "Content-Type: application/zip" --data-binary @"$(ls release/moondigest-*-chrome.zip)" "$api/draft/package" poll="$api/operations" run_op -X POST -H "Content-Type: application/json" -d "$(jq -n --arg notes "$NOTES" '{notes: $notes}')" "$api" + + # The MCP server (mcp/) goes to npm when its package.json version is not there yet, so a release that did not touch + # it publishes nothing. No token: npm trusts this workflow (the package's Trusted Publisher setting names + # PathGao/MoonDigest and release.yml). The first version is published by hand, since that setting needs the package + # to exist; until then this job only says so. + npm: + needs: check + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ inputs.tag || github.ref_name }} + + - uses: actions/setup-node@v5 + with: + node-version: 24 + registry-url: https://registry.npmjs.org + + # Trusted publishing needs npm 11.5.1 or newer. + - run: npm install -g npm@latest + + - name: Publish moondigest-mcp + working-directory: mcp + run: | + version=$(node -p "require('./package.json').version") + if ! npm view moondigest-mcp name >/dev/null 2>&1; then echo "moondigest-mcp is not on npm yet: publish the first version by hand (docs/RELEASE.md)"; exit 0; fi + if npm view "moondigest-mcp@$version" version 2>/dev/null | grep -q .; then echo "moondigest-mcp@$version is already on npm"; exit 0; fi + npm ci + npm test + npm publish --access public diff --git a/PRIVACY.md b/PRIVACY.md index d9a094aa..92ff30c9 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -4,7 +4,12 @@ MoonDigest 的隐私政策。适用于 Chrome 应用商店版和 GitHub 发布 - 设置、分拣结果、备注和对话都存在浏览器本地。API Key 也存在本地,不会出现在任何导出文件里。 - 插件只连这些地方:B站和 YouTube、你配置的 AI 平台、你本机的 Obsidian。允许读取网页以后,侧边栏开着时会在本机读它跟随的每个网页的正文;只有你提问或点 AI 总结时,正文才发给你选的 AI 平台。没有作者的服务器,没有统计。 -- B站 和 YouTube 视频页上的网页脚本(包括你用的 AI agent)可以向插件要这个视频的信息、字幕、章节和热门评论,这些都是网站本来就给这个页面的数据。打开「允许 MCP 连接」后,插件还会连本机 `127.0.0.1:47823` 上的 MCP 服务(`mcp/`),只接受打开 B站 和 YouTube 视频页、在这两个网站搜索视频的请求。再打开它下面的「允许 Agent 读我的收藏、关注和历史」后,还接受读你 B站 的收藏夹和里面的视频、稍后再看、关注和分组、观看历史的请求,连同分拣台的标签、备注和 AI 判断交给本机的 MCP 服务,再由它交给你用的 Agent 。只读,每次返回一页,不改 B站 和插件里的任何东西,不含 API Key 和 AI 对话。见 README 的「给 AI agent 用」。 +- B站 和 YouTube 视频页上的网页脚本(包括你用的 AI agent)可以向插件要这个视频的信息、字幕、章节和热门评论,这些都是网站本来就给这个页面的数据。 +- 设置 → MCP 的「允许 AI 助手使用我的 B站和 YouTube 账号」默认关闭。打开后,插件会连本机 `127.0.0.1:47823` 上的 MCP 服务(`mcp/`),你在本机运行的 AI 工具就能通过它用你的登录读和改下面这些。见 README 的「给 AI agent 用」。 + - 能读:B站 和 YouTube 的视频搜索结果;视频的信息、字幕、章节、热门评论和画面;你 B站 的收藏夹和里面的视频、稍后再看、关注和分组、观看历史,连同分拣台的标签、备注、AI 判断和判断标准;你 YouTube 的稍后观看、赞过的视频、订阅、历史记录和 YouTube 播放列表。 + - 能改:在 B站 移动、复制、取消收藏视频,清理失效视频,新建、改名、删除收藏夹,改收藏夹的公开或私密;把视频加进或移出 B站稍后再看、YouTube 稍后观看和你的 YouTube 播放列表;分拣台的 AI 判断、保留、备注、标签、判断标准,以及勾选哪些收藏夹。 + - 修改直接执行,不逐次问你。对 B站收藏夹和分拣台结果的每次修改都记在浏览器本地(留最近 100 次),可以在设置 → MCP 的「AI 助手最近的修改」或分拣台里撤销;稍后再看和 YouTube 播放列表的修改,由 AI 工具按结果里给出的撤销调用改回。撤销有做不到的情况(如已删除的收藏夹),见 [mcp/README.md](mcp/README.md) 的「安全模型」。 + - 这些数据只交给本机的 MCP 服务,再由它交给你运行的 AI 工具,MoonDigest 不把它们发往别处。AI 工具之后把内容发给哪个模型服务,由那个工具决定。API Key 和 AI 对话不会交出去。开关关着时插件不连 MCP 服务。 - 分拣台只读取你勾选的收藏夹的内容。为了列出可勾选和可移动到的收藏夹,会读收藏夹的名称和数量。 - 分拣台的「关注」读你的关注列表、关注分组、每个人的粉丝数,以及关注的人新发的视频。只在你打开「关注」或点刷新时读。 - **观看进度**不是「关」时,会读你自己的 B站历史记录:只在你打开分拣台或 B站页面时读,最多 10 分钟一次,只读上次之后新增的部分。每个视频只在本地存两个数:看到百分之多少、最后观看时间,留 1 年、最多 5000 个(勾选收藏夹里的视频不删)。这些数据不发往任何地方。选「关」就不读。 @@ -16,11 +21,11 @@ MoonDigest 的隐私政策。适用于 Chrome 应用商店版和 GitHub 发布 | `scripting` | 在视频页按需注入脚本(抓字幕、专注模式、播放器 ✦ AI 总结按钮);在你授权的网页上提取正文 | | `activeTab` | 还没允许读取网页时,你在网页上点插件图标,弹窗和侧边栏就能读这一个网页,直到它跳到别的网址(这项权限不弹提示) | | `sidePanel` | 显示 AI 侧边栏 | -| `cookies` | 读取 B站的 `bili_jct`,分拣台改动收藏(取消收藏、移动、复制、新建收藏夹)和关注(取消或重新关注、特别关注、关注分组)时要用 | +| `cookies` | 读取 B站的 `bili_jct`,分拣台和 MCP 改动收藏(取消收藏、移动、复制,新建、改名、删除收藏夹)以及分拣台改动关注(取消或重新关注、特别关注、关注分组)时要用 | | `declarativeNetRequest` | 给分拣台发往 B站接口的请求加上 B站的 Referer 和 Origin | -| `alarms` | 刷新关注列表时,后台被浏览器回收后从断点接着刷新,刷新完就清除;打开「允许 MCP 连接」后每分钟检查一次本机 MCP 服务,关掉就清除 | +| `alarms` | 刷新关注列表时,后台被浏览器回收后从断点接着刷新,刷新完就清除;打开「允许 AI 助手使用我的 B站和 YouTube 账号」后每分钟检查一次本机 MCP 服务,关掉就清除 | | B站、YouTube 域名 | 读取视频信息、字幕和评论;读写你的收藏夹、稍后再看和关注列表;观看进度打开时读历史记录;在 B站页面显示分拣标记和观看进度 | -| `127.0.0.1`、`localhost` | 连接本机的 Obsidian Local REST API;打开「允许 MCP 连接」后连本机的 MCP 服务(`mcp/`) | +| `127.0.0.1`、`localhost` | 连接本机的 Obsidian Local REST API;打开「允许 AI 助手使用我的 B站和 YouTube 账号」后连本机的 MCP 服务(`mcp/`) | | 可选:任意 http/https 地址 | 你添加 AI 平台时针对那个地址申请;在侧边栏点「允许读取网页」时一次申请所有网站,用来读网页正文 | ## 免责声明 diff --git a/README.md b/README.md index 67e7ea94..49f7240f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ 收藏了却没看的视频、想读的网页,用 AI 帮你读完、整理好,挑出真正要看的。 [![Chrome MV3](https://img.shields.io/badge/Chrome-MV3-4285F4?logo=googlechrome&logoColor=white)](https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3) -[![Version](https://img.shields.io/badge/version-3.7.8-7c6cf2)](extension/manifest.json) +[![Version](https://img.shields.io/badge/version-3.8.0-7c6cf2)](extension/manifest.json) [![Platforms](https://img.shields.io/badge/platforms-Bilibili%20%7C%20YouTube-fb7299)](#功能) [![License](https://img.shields.io/badge/license-GPL--3.0-blue)](LICENSE) @@ -52,7 +52,7 @@ ## 给 AI agent 用 -Agent 碰到视频通常只能看标题和封面。装了 MoonDigest,agent 能借你浏览器的登录状态拿到视频的字幕、章节、简介和热门评论,还能截取任意一秒的画面。用 MCP 时,agent 还能借你的登录搜 B站和 YouTube 视频。不下载视频,不花 AI 额度,读不到你的 Key 和 AI 对话。你的收藏夹、关注、观看历史和分拣台里的标签、备注,只有打开「允许 Agent 读我的收藏、关注和历史」后才能通过 MCP 读;分拣台的 AI 判断、保留、备注和标签,再打开「允许 Agent 改分拣台」后 agent 才能写,取消收藏、移动和复制只能提议,要你在分拣台确认;B站稍后再看、YouTube 稍后观看和 YouTube 播放列表,再打开「允许 Agent 改我的收藏」后 agent 才能改。两种接法: +Agent 碰到视频通常只能看标题和封面。装了 MoonDigest,agent 能借你浏览器的登录状态拿到视频的字幕、章节、简介和热门评论,还能截取任意一秒的画面。不下载视频,不花 AI 额度,读不到你的 Key 和 AI 对话。用 MCP 并在设置 → MCP 打开「允许 AI 助手使用我的 B站和 YouTube 账号」后,agent 还能借你的登录搜 B站和 YouTube 视频,读你的收藏夹、关注、观看历史和分拣台里的标签、备注,并直接修改:在 B站移动、复制、取消收藏视频,新建、改名、删除收藏夹;写分拣台的 AI 判断、保留、备注和标签;改 B站稍后再看、YouTube 稍后观看和 YouTube 播放列表。修改不逐次问你;对 B站收藏夹和分拣台的每次修改都记在本地,在设置 → MCP 或分拣台里可以撤销。两种接法: - **skill**(agent 能操控浏览器,如 ego、Claude in Chrome、Playwright): @@ -61,14 +61,14 @@ Agent 碰到视频通常只能看标题和封面。装了 MoonDigest,agent 能 mkdir -p ~/.claude/skills && cp -r MoonDigest/skills/moondigest ~/.claude/skills/ ``` -- **MCP**(Claude Code、Cursor、Codex 等):在设置 → MCP 打开「允许 MCP 连接」(用时再开,不用时关掉),再装服务(要 Node 20+): +- **MCP**(Claude Code、Cursor、Codex 等):在设置 → MCP 打开「允许 AI 助手使用我的 B站和 YouTube 账号」(用时再开,不用时关掉),再装服务(要 Node 20+): ```sh git clone https://github.com/PathGao/MoonDigest.git cd MoonDigest/mcp && npm install && claude mcp add moondigest -- node "$PWD/server.js" ``` - Cursor、Codex 的配置和注意事项见 [mcp/README.md](mcp/README.md)。插件会自动更新,MCP 服务不会:以前装过的,在 `MoonDigest` 目录里 `git pull` 后重开 agent,才能用上新工具。 + Cursor、Codex 的配置和注意事项见 [mcp/README.md](mcp/README.md),设置 → MCP 里也有各客户端的安装命令。插件会自动更新,MCP 服务不会:以前装过的,在 `MoonDigest` 目录里 `git pull` 后重开 agent,才能用上新工具。 不装 skill 也行,把这段提示词粘给 agent(用 MCP 时把第 1、2 步换成 get_video、get_frame,找视频用 search_videos): diff --git a/docs/AGENT.md b/docs/AGENT.md index 235ef7c1..dd5d6433 100644 --- a/docs/AGENT.md +++ b/docs/AGENT.md @@ -1,6 +1,6 @@ # MoonDigest agent interface v1 -How AI agents read a video through MoonDigest: page messages for agents that drive the browser (the skill, `skills/moondigest/SKILL.md`), and a local MCP server (`mcp/`) for any MCP client, which can also search for videos (§2 `search`). The page messages cannot search. Never API keys or AI conversations. The user's own Bilibili favorites, follows and watch history, with their 分拣 tags, notes and AI verdicts, only through the MCP server and only while the user allows it (§2, `favorites` / `follows` / `history`); likewise the user's collections on other sites (§2 `collection`). Writes only through the MCP server and only while the user allows them: the 分拣台 (§2, `triage` / `propose`: local results directly, Bilibili changes as proposals the user confirms) and the user's collections on other sites (`collectionWrite`). For sites beyond the Bilibili 分拣台 MoonDigest has no pages of its own: the agent is the UI. +How AI agents use MoonDigest: page messages for agents that drive the browser (the skill, `skills/moondigest/SKILL.md`), and a local MCP server (`mcp/`) for any MCP client. The page messages read the video of the page they run in and nothing else: no search, nothing of the user's account, no write. Everything else goes through the MCP server, behind one switch (§2): a video search (`search`), the user's own Bilibili favorites, follows and watch history with their 分拣 tags, notes and AI verdicts (`favorites` / `follows` / `history`), the user's collections on other sites (`collection`), and the writes: Bilibili's favorites (`favWrite`), the 分拣台's local results and 判断标准 (`triage`, `triageSettings`), which folders the 分拣台 keeps a copy of (`sync`), and the collections (`collectionWrite`). No write asks the user first. Safety is undo: every `favWrite`, `triage` and 判断标准 change is journaled and reversed by `undo`, from the tool, the 分拣台 (U) or 设置 → MCP; `collectionWrite` answers with the call that reverses it. Never API keys or AI conversations. For sites beyond the Bilibili 分拣台 MoonDigest has no pages of its own: the agent is the UI. Supported pages: Bilibili video pages (`www.bilibili.com/video/…`, `/bangumi/play/…`, `/list/…`, `/festival/…`) and YouTube watch pages (`www.youtube.com/watch?v=…`). The MCP server reads the sites registered in `extension/agent-sites/` (§5); the page messages only these two. @@ -50,7 +50,9 @@ Errors: `NO_VIDEO` (no `