dsh-zju-lecture-note
Manifest validTurns Zhejiang University classroom replays (classroom.zju.edu.cn) into Markdown study notes. Its scraping core comes from nzw806/zju-lecture-summerizer- (a standalone Python tool, not a DSH plugin),
浙大智云课堂讲义插件(ZJU Lecture Agent Plugin)
面向 DeepSeek Harness(dsh) 与任意 MCP 宿主:把浙大智云课堂(
classroom.zju.edu.cn)的课程回放 变成一份可复习、结构化、能正常渲染的讲义。 语音字幕 + 本地课件是默认主链路;课堂投影 PPT 截图是可选模块,需要时在插件界面一键打开。 字幕接口公开免登录;核心只用 Python 标准库(PDF 课件另需可选的pypdf)。隐私优先:登录 Cookie 只进入本地进程内存,工具不收、不回显、不落盘、不写日志; 本地文件工具拒绝凭据与浏览器数据。细则见
SECURITY.md。
本仓库既是插件的能力核心,也是 dsh 原生插件包的发布源:Python 核心、MCP 服务器、
讲义 Skill,以及 dsh 用的 package.json / cordis.patch.yml / lib/ 都在这里。
npm 包名 dsh-zju-lecture-note;两种形态随包携带同一份 Python 实现,
工具名、参数、返回值完全一致,不会各自漂移。
这是什么
上游项目 nzw806/zju-lecture-summerizer- 用
Python 脚本 + 本地网页 + 写死的 DeepSeek key 完成"链接 → 讲义"。本插件保留它的抓取内核,
把模型调度交给宿主智能体(DeepSeek Harness / MiniMax Code / Claude Code / Cursor …),
并把整套流程封装成标准 MCP Server + Skill:
| 维度 | 上游(CLI + 本地 Web) | 本插件(Agent 插件 / MCP) |
|---|---|---|
| 交互 | 固定单次生成网页,无法追问 | 原生 Tool / Skill,可多轮单点答疑、生成自测题 |
| 模型 | 脚本内硬编码 DeepSeek API | 宿主智能体直接感知材料,统一上下文与计费 |
| 生态 | 孤立工具 | dsh 原生插件 / 标准 MCP,可与 Git、知识库、文档插件协同 |
| 依赖 | 常驻后台 Web 服务 | 按需 stdio 进程,零 pip 依赖 |
| 隐私 | 需要把 Cookie 交给脚本 | Cookie 只在本地内存中解析;工具不回显、不落盘 |
复用与新增的具体边界见 ATTRIBUTION.md。
两种形态,一套内核
| 形态 | 载体 | 适合谁 |
|---|---|---|
| dsh 原生插件 | 本仓库根目录的 npm 包(package.json + cordis.patch.yml + lib/),随包携带 Python 核心 | DeepSeek Harness 用户 |
| 标准 MCP + Skill | server.mjs 拉起一个 stdio MCP Server,附带 skills/ 里的讲义 SOP | 任意 MCP 宿主(MiniMax Code、Claude Code、Cursor、Cline…) |
- dsh:默认注册 7 个工具(字幕主链路 + 登录辅助);在插件设置里打开
「启用 PPT 投影抓取」 后额外注册
zju_fetch_slides,共 8 个工具。 - MCP:未设置
ZJU_ENABLE_SLIDES时保持旧行为(8 个);设0关闭,仓库自带的zju-lecture.mcp.json默认就是0(7 个)。
功能分层:字幕主链路 vs 可选投影抓取
v1.5.1 起,原本「仅依赖字幕、已实测跑通」的链路与「依赖投影 PPT 抓取」的链路彻底解耦:
| 分层 | 工具 | 依赖 | 默认 |
|---|---|---|---|
| 字幕主链路 | zju_parse_url / zju_fetch_transcript / zju_extract_courseware / zju_check_note / zju_write_note | 公开字幕接口 + 本地文件,无需登录态 | 始终开启 |
| 登录辅助 | zju_get_video_url / zju_read_browser_cookie | 本机 Edge 或 ZJU_COOKIE / ZJU_COOKIE_FILE | 可用,但不会主动读取 |
| 投影抓取(可选) | zju_fetch_slides | 登录态 + 截图下载 + PDF 合成 | 关闭,需显式开启 |
- dsh 插件设置项
enableSlides(默认false)是投影模块的总开关;MCP 宿主用ZJU_ENABLE_SLIDES(未设置时保持旧版行为,设为0即关闭)。 - 关闭时:不注册
zju_fetch_slides、不加载zju_lecture.slides模块、不读浏览器 Cookie、 Skill 不追加投影附录;系统提示会明确告诉 Agent「投影抓取已关闭」。 - 开启后才会追加
skills/zju-lecture-summarizer/SLIDES.md的投影下载与插图规范, 讲义插图子开关embedSlideImages(默认false)才生效。
隐私与安全(先看)
- Cookie 不是工具参数:
zju_fetch_slides/zju_get_video_url不接受 cookie 参数,zju_read_browser_cookie也没有参数;登录态只能由本地配置(ZJU_COOKIE/ZJU_COOKIE_FILE) 或本机 Edge 提供,模型无法把它写进参数或从返回值里读出来。 - Cookie 不回显:
zju_read_browser_cookie只返回名称、数量、长度与来源类别; 所有 stdout / stderr / 错误消息还会再经过一次 redactor,注册过的 Cookie 值统一替换为[REDACTED]。 - Cookie 不落盘:读 Edge 时优先只读打开数据库;必须复制时用 0600 私有临时文件,读完覆写并删除。 服务端缓存只存在于 Python 子进程内存,进程退出即消失。
- TLS 不降级,也不靠运气:证书校验永远不会被关闭。信任库按
certifi→ 随包cacert.pem→ 系统根证书 三级回落——dsh 自带的 Python 解释器不含 certifi,这份随包 Mozilla 根证书快照让"能不能抓字幕"不再取决于 用户碰巧 pip 装了什么。三级都不够时才失败,并把原因 + 下一步一起返回 (当前信任库、加载了几张 CA、HTTPS_PROXY是什么、该装certifi还是把域名加进NO_PROXY), 而不是丢一句<urlopen error ...>让人猜。 - 瞬时故障有重试:建连与正文读取都在重试范围内(
IncompleteRead/SSLEOFError/ 超时 / 5xx, 共 3 次、指数退避 + 抖动);证书校验失败不重试——那需要修,不需要重跑。 - 跨域重定向被拒绝:避免
urllib在 302 时把Cookie头重发到第三方域。 - 本地文件有边界:
zju_extract_courseware/zju_check_note只接受课件与讲义扩展名, 并拒绝.env、凭据、私钥、浏览器 Cookie/Login Data 等敏感文件与目录;凭据词按分隔符整词匹配 (client_secret.json仍拒绝,secret sharing 第5章.md放行),通用文件名config.json不再误伤。 - 不写用户目录:插件把随包 Skill 注册到 dsh 技能表,而不是复制进
$DSH_HOME;无遥测、无后台服务。 - 可机器验证:
python tools/privacy_scan.py扫描工作区,--history扫描 git 历史,--tarball扫描 npm 发布产物。
完整威胁模型与已知边界见 SECURITY.md。
工具清单
| 工具 | 功能 | 鉴权 | 隐私处理 |
|---|---|---|---|
zju_parse_url | 解析回放链接,取 sub_id / course_id / tenant_code | 公开 | 纯字符串解析 |
zju_fetch_transcript | 抓整节课或指定时间段的语音字幕,支持按分钟/逐句/分块/JSON 四种视图与字符上限 | 公开免登录 | 无 Cookie |
zju_fetch_slides | (可选模块,默认关闭) 抓取课堂投影 PPT 截图清单;可选下载截图并合成 PDF(接口不返回文字层/OCR);可开关是否把截图引用进讲义 | 需登录态(Cookie 需含 _token) | 不收/不回显 Cookie |
zju_extract_courseware | 提取本地 PPTX / DOCX / TXT / MD / CSV / JSON / PDF 文本 | 本地文件 | 拒绝敏感路径 |
zju_check_note | 检查讲义 Markdown 的公式规范与渲染(一键修复机械问题;报告按问题类型聚合) | 本地文件 | 只接受 .md / .markdown |
zju_write_note | 写入讲义:文件名由 Agent 按「上课日期 + 本节内容」命名(如 知识总结-2026-10-28-复变函数-保角映射.md)、拒绝静默覆盖、写前修正 + 写后校验 | 本地文件 | 只写工作区内的 .md |
zju_get_video_url | 解析回放 MP4 直链(供下载或 Whisper 重识别) | 需登录态(服务端本地解析) | 直链可能带签名,勿外传 |
zju_read_browser_cookie | 读取本机 Edge 登录态并缓存到服务端内存 | 本机读取 | 只返回名称与长度 |
智云投影 PPT 是截图,不是文字:
search-ppt返回的是投影画面截图(pptimgurl)。zju_fetch_slides默认只给截图清单与页数;传download=true时会把截图下载到<工作区>/ppt-assets/<sub_id>/images/,并合成PPT-<sub_id>.pdf(JPEG 直嵌、PNG 解码, 连续字节相同的截图折叠为一页)。只传纯sub_id时必须同时给course_id; 鉴权使用 Cookie 父域.zju.edu.cn中的_token(Authorization: Bearer)。
启用投影抓取与讲义插图(可选,默认关闭)
投影模块有两层开关,先开总开关,再决定要不要插图:
enableSlides(dsh)/ZJU_ENABLE_SLIDES(MCP)——总开关,默认关闭。 打开后才会注册zju_fetch_slides,并加载截图下载/PDF 合成代码;关闭时整个投影模块 不存在,字幕主链路不受任何影响。embedSlideImages(dsh)/ZJU_EMBED_SLIDES(MCP)——插图子开关,默认关闭。 仅在总开关打开时有意义;打开后 Agent 会把截图下载到ppt-assets/<sub_id>/images/, 并从中挑 3–8 张关键页,以 Markdown 相对路径插进讲义对应小节。
总开关关闭(默认)时只走字幕,不下载截图、不读浏览器 Cookie、不追加投影 Skill 附录,成本最低。
-
dsh:打开 设置,在左侧导航选择 「浙大智云课堂讲义」(独立一栏,不占通用设置), 打开「启用 PPT 投影抓取」(
enableSlides);需要插图再打开「把截图引用进讲义」 (embedSlideImages)。设置通过 dsh 的configForms写回 profile 配置,保存后插件会按 新配置重新加载。首次安装或更新后如果看不到这一栏,请完全重启 DeepSeek Harness (不是只刷新页面):dsh 在启动时缓存每个包的dsh.client元数据,新增的浏览器半边 只有在重启后才会进入 Client 模块图。 -
MCP / MiniMax Code:在 server env 中设
ZJU_ENABLE_SLIDES=1,需要插图时再加ZJU_EMBED_SLIDES=1(仓库自带的zju-lecture.mcp.json两个都是0):"env": { "ZJU_ENABLE_SLIDES": "1", "ZJU_EMBED_SLIDES": "1" } -
CLI:
python -m zju_lecture.cli slides <url> --download只负责下载截图与 PDF; 是否把图片写进讲义由 Agent 按上面的开关决定。
开启后如果宿主模型不能查看本地图片,Agent 只会在讲义末尾列「课堂投影截图索引」(页码 + 相对路径),
不会编造截图内容。图片路径按讲义所在目录调整:讲义在工作区根目录用 ppt-assets/...,
在 notes/ 里用 ../ppt-assets/...。这些相对路径只在工作区内有效;要离线分享或换机器,
直接发 PPT-<sub_id>.pdf 更稳妥。
插件本身只负责下载、索引与相对路径,不提供 OCR;模型能否「看懂」截图取决于宿主是否支持本地图片输入。 不支持时,Agent 会退化为截图索引 + 字幕/课件总结,不会把截图当成已识别的文字。
安装
1. DeepSeek Harness(dsh)
dsh 插件是 pnpm 包:dsh plugin 会把参数原样转给 profile 目录里的 pnpm,
所以来源写法与 pnpm 一致(npm 包名 / github: / file:)。
# 从 GitHub 仓库(就是本仓库)—— 当前可用的安装方式
dsh plugin --profile desktop add github:chenqianyu486-sys/dsh-zju-lecture-note
# 从 npm registry(包发布到 npm 之后才可用,包名 dsh-zju-lecture-note)
# dsh plugin --profile desktop add dsh-zju-lecture-note
# 从本地路径(开发 / 验证)
dsh plugin --profile desktop add file:/absolute/path/to/zju-lecture-agent-plugin
本包目前还没发布到 npm registry(
dsh-zju-lecture-note在 npm 上查无此包), 请用上面的github:写法安装;等发布后再换回包名写法。
desktopprofile 由 DeepSeek Harness 桌面端持有,管理插件时请使用 DeepSeek Harness 自带的 dsh 命令(resources/runtime/cli/bin/dsh.cmd), 而不是 npm 全局安装的dsh;web/headless等 profile 用哪个都可以。
不要只把 GitHub 的 zip 解压出来让 dsh 加载。 插件是 Node ESM 包,
lib/index.js 里的 @deepseek-ai/* 全部是 peerDependencies(与 dshmarket 一致),
由 pnpm 在 dsh plugin add 时装进 profile 的 node_modules(当前是 @deepseek-ai/schemastery 3.18.x);
解压出来的目录本身没有 node_modules,启用时会报:
Cannot find package '@deepseek-ai/schemastery' imported from ...\lib\index.js
若一定要用本地目录,先给它补一个能被解析的 schemastery(其余 @deepseek-ai/dsh-*
仍由 dsh 运行时提供),再让 dsh 加载:
cd /absolute/path/to/zju-lecture-agent-plugin
npm install --no-save --no-package-lock @deepseek-ai/schemastery@^3.18.1
dsh plugin --profile desktop add file:/absolute/path/to/zju-lecture-agent-plugin
--no-save --no-package-lock 让这次安装只落进目录的 node_modules,不改 package.json
(@deepseek-ai/dsh-* 是 dsh 提供的 peer,不要在这里重复安装)。
装完后在 dsh 里直接说:
帮我把这节课整理成讲义:https://classroom.zju.edu.cn/livingroom?course_id=58133&sub_id=123456
版本要求:dsh 会按
peerDependencies中的@deepseek-ai/dsh-*范围校验运行时版本; 本插件声明>=0.2.0-rc.2 <0.3.0-0,不匹配时需要显式的版本豁免。 Python 探测顺序:插件配置项 → 环境变量ZJU_PYTHON→ DeepSeek Harness 自带 Python → PATH 上的python3/python/py -3。 投影抓取开关:dsh 设置项enableSlides(默认false,投影模块总开关); MCP 宿主用环境变量ZJU_ENABLE_SLIDES=1。需要把截图插进讲义时,再打开子开关embedSlideImages/ZJU_EMBED_SLIDES=1。关闭时只走字幕,不注册投影工具、不读登录态。
插件市场(收录审核中):dsh 应用内市场读取
awesome-dsh-plugin 的条目列表。
本仓库的收录 PR #11087
目前仍是 open(held: author,等维护者复核描述);合并前在 market 里搜不到是预期行为,
合并后站点自动重建,届时可直接搜 zju-lecture。在那之前请用上面的 github: 命令安装。
登录态(可选,仅投影 PPT / 视频直链需要):优先自动读本机 Edge;Edge 正在运行时数据库被锁,
且 Edge 127+ 若启用了应用绑定加密(ABE),退出 Edge 也无法在本地解密其 v20 Cookie,这时会直接
提示改用 ZJU_COOKIE_FILE。可在宿主配置里设置环境变量,不要把 Cookie 粘贴到对话里:
ZJU_COOKIE="name=value; name2=value2" # 直接给 Cookie 头
ZJU_COOKIE_FILE="/absolute/path/cookie.txt" # 或从本机文件读(一行,权限建议 600)
智云接口用 Cookie 会话中的 _token 做 Authorization: Bearer 鉴权;这个会话 Cookie 通常设在父域
.zju.edu.cn。插件从 Edge 读课堂域 Cookie 时会一并读取该父域 Cookie;手工配置 ZJU_COOKIE /
ZJU_COOKIE_FILE 时请确认里面包含它,否则 PPT 接口可能返回 401/403。
工作区与备份位置(可选):
ZJU_WORKSPACE="/absolute/path/to/workspace" # 相对路径(如 知识总结.md)按它解析;dsh 插件会自动传入
ZJU_BACKUP_DIR="/absolute/path/to/backups" # zju_check_note / zju_write_note 的备份目录,默认系统临时目录
相对路径的解析顺序是:绝对路径 → ZJU_WORKSPACE → PWD → 服务进程自己的 cwd;都找不到时,
错误信息会列出尝试过的绝对路径,而不是只回一句"找不到"。
2. 任意 MCP 宿主(MiniMax Code / Claude Code / Cursor …)
本仓库自带 stdio MCP Server,配置里只要一个 PATH 上的 node:
{
"mcpServers": {
"zju-lecture": {
"type": "stdio",
"command": "node",
"args": ["/绝对路径/dsh-zju-lecture-note/server.mjs"],
"timeout": 120000
}
}
}
server.mjs 会在运行时解析 Python 解释器(依次尝试 ZJU_PYTHON → python3 / python / py -3,要求 3.9+),
因此配置里只需要一个在 PATH 上必然存在的 node。需要登录态时,把 ZJU_COOKIE 或
ZJU_COOKIE_FILE 放进该服务器的 env(部分宿主不支持 env,可用系统环境变量)。
Claude Code 一行接入:
claude mcp add zju-lecture node /绝对路径/dsh-zju-lecture-note/server.mjs
或把本仓库作为 Claude Code 插件目录使用(清单见
adapters/claude_code/plugin.json)。
3. MiniMax Code(MCode)插件包
python tools/build_plugin.py --install # 校验 + 安装到 ~/.minimax/plugins/zju-lecture
python tools/build_plugin.py --install --zip # 同时产出 dist/zju-lecture-<version>.zip
安装后 MCode 的插件自动扫描会识别该包,启用后即可直接说:
帮我把这节课整理成讲义:https://classroom.zju.edu.cn/livingroom?course_id=58133&sub_id=123456
卸载:删除 ~/.minimax/plugins/zju-lecture 目录。
4. 命令行
python -m zju_lecture.cli parse "https://classroom.zju.edu.cn/livingroom?course_id=58133&sub_id=123456"
python -m zju_lecture.cli fetch 123456 --format min -o 字幕.txt
python -m zju_lecture.cli fetch 123456 --start 20 --end 45
python -m zju_lecture.cli slides "https://classroom.zju.edu.cn/livingroom?course_id=58133&sub_id=123456" --download
python -m zju_lecture.cli courseware ./课件.pptx
python -m zju_lecture.cli cookie # 默认只显示名称与长度
CLI 的
cookie子命令默认不回显明文;确实需要时加--reveal(会进入终端历史)。slides子命令优先用--cookie-file/ZJU_COOKIE;--cookie仍可用,但会出现在进程列表里。slides --download输出到<工作区>/ppt-assets/<sub_id>/(可用--out-dir改),并生成PPT-<sub_id>.pdf;--no-dedup关闭连续重复截图折叠。
目录结构
dsh-zju-lecture-note/
├── package.json # npm / dsh 插件包清单(包名 dsh-zju-lecture-note)
├── cordis.patch.yml # dsh bundle patch(把插件插进 cordis)
├── lib/ # dsh 插件实现(ESM)
│ ├── index.js # 插件入口:注册 8 个 dsh 工具 + 内存注册随包 Skill
│ ├── bridge.js # 拉起随包 Python 核心(JSON-RPC over stdio)
│ ├── tools.js # 生成物:dsh 工具定义
│ └── tools.schema.json # 生成物:工具定义快照(供测试比对)
├── .minimax-plugin/plugin.json # MiniMax Code(MCode)插件清单
├── zju-lecture.mcp.json # MCP 服务器定义(stdio → node ./server.mjs)
├── server.mjs # 启动器:解析 Python 解释器并拉起 MCP 服务
├── mcp_server.py # MCP stdio 服务器(JSON-RPC 2.0,隐私边界在这里)
├── zju_lecture/ # 核心库(纯标准库)
│ ├── client.py # 智云课堂 API 客户端(TLS 校验 + 同域重定向)
│ ├── cookies.py # Edge Cookie 读取与安全清理
│ ├── security.py # Cookie 校验/redactor/本地文件边界
│ ├── courseware.py # 课件文本提取(带路径校验)
│ ├── note_lint.py # 讲义 Markdown 公式规范检查 / 自动修复
│ ├── formatter.py # 时间戳 / 分钟聚合 / 分块
│ └── cli.py # 命令行入口
├── skills/zju-lecture-summarizer/SKILL.md # 讲义生成 SOP(五段式模板 + 公式规范)
├── skills/zju-lecture-summarizer.manifest.json # Skill 文件清单(sha256 审计)
├── SECURITY.md # 威胁模型 / Cookie 生命周期 / 已知边界
├── adapters/ # 其它宿主的接入示例(不进入插件包)
├── tests/ # 回归测试(含隐私扫描与插件形态测试)
├── tools/ # 打包、schema 生成、隐私扫描、冒烟测试
├── icon.png / icon-dark.png # 插件图标
├── LICENSE / ATTRIBUTION.md
└── README.md
隐私设计(工作原理)
Cookie 从哪里来、到哪里去
- 来源只有本地:内存缓存 →
ZJU_COOKIE→ZJU_COOKIE_FILE→ 本机 Edge; - 校验:拒绝换行(HTTP 头注入)、空 name、超长;不合法只返回原因,不回显内容;
- 使用:只放进发往
classroom.zju.edu.cn的请求头;跨域重定向拒绝; - 缓存:仅 Python 子进程内存;
- 回显:没有;工具 schema 中没有 cookie / reveal / domain 参数;
- 日志:所有输出经过
SecretRedactor; - 临时文件:只读打开优先;必须复制时连同
-wal/-shm一起复制(刚登录的行可能还在 WAL 里), 权限 0600,读完覆写并删除。
本地文件
zju_extract_courseware 和 zju_check_note 只接受指定扩展名,并拒绝敏感文件名/目录
(.env*、*credential*、*.pem、Cookies、Login Data、.ssh、.dsh 等)。
凭据类单词按分隔符整词匹配(my-secret.txt 拒绝、secret sharing 第5章.md 放行),
config.json 这类通用名不再被拒。符号链接会先解析真实路径再判定。
确有必要时可用 ZJU_ALLOW_SENSITIVE_PATHS=1 显式关闭保护。
数据会离开本机的部分
字幕与课件文本会作为工具结果进入宿主模型上下文,由你选择的模型提供方处理——这是使用大模型 整理讲义的固有代价。插件本身不收集、不额外上传、不写遥测。
讲义不进仓库
讲义是给自己复习用的本机学习材料,不是代码。开发者自己在这个仓库里跑插件时也一样,
默认就不会被提交:本仓库的 .gitignore 已经挡掉任意层级的 知识总结*.md 与 /notes/
(git check-ignore -v 知识总结-2026-10-28-复变函数-保角映射.md 可以自查)。
在你自己的项目仓库里用这个插件时,建议把这几行加进该仓库的 .gitignore:
# 智云课堂讲义(本机产物,不入库)
知识总结*.md
notes/
Agent 侧也写明了:不要 git add / 提交讲义,需要归档就把文件换个名字留在本地——
zju_write_note 的同名拒绝覆盖已经保证新讲义不会吃掉旧讲义。
讲义的公式规范
生成的讲义要能渲染,而不只是"公式写对了"。插件把这件事做成了两道闸门:
- 规范写进 Skill(
skills/zju-lecture-summarizer/SKILL.md§4④)——行内$...$/ 独立$$...$$、 分式\frac{分子}{分母}(分子分母都带花括号)、绝对值\lvert ... \rvert(不写裸|)、 虚数单位\mathrm{i}、中文不塞进\text{}、<details>写成多行。 - 产出后自检——
zju_check_note静态扫描讲义,捕获"表格列被裸竖线截断""行内公式没闭合" "块级公式没独立成行""\frac缺花括号""单行<details>"这类会让整篇散架的问题。
python tools/check_note.py 讲义.md # 只检查
python tools/check_note.py --fix 讲义.md # 自动修正机械问题(单行 $$ 块、$$ 前后缺空行、
# \dfrac、\frac 补花括号、表格裸竖线、单行 <details>)
自动修复只做语义无歧义的改写,且只作用于围栏代码块与行内代码之外(讲义里"这样写是错的"示例
不会被改坏);回写前会把原文件备份到备份目录(ZJU_BACKUP_DIR,默认系统临时目录下的
zju-lecture-backups/,报告里给出备份的绝对路径),再原子替换——备份不会再落在讲义同目录污染
git status。需要判断的(未闭合的 $、\text{} 里的中文、裸虚数单位)保持原样并继续报出来。
检查报告按问题类型聚合:59 处同类错误只打印一次 [错误] display_not_isolated × 59,附行号列表与
少量示例(max_samples 可调),长讲义不会再把工具返回刷成一屏。
开发与测试
python -m unittest discover -s tests -v # 全部回归测试(含插件形态与隐私测试)
python tools/build_plugin.py --validate # 按插件规范校验包结构
python tools/check_note.py 讲义.md # 讲义渲染规范检查
python tools/privacy_scan.py # 工作区隐私扫描(Cookie/密钥/个人路径/邮箱)
python tools/privacy_scan.py --history # 连同 git 历史一起扫
python tools/export_schemas.py # 改动 TOOLS 后重新生成适配器 schema
python tools/export_dsh_tools.py # 改动 TOOLS 后重新生成 dsh 工具定义
python tools/build_skill_manifest.py # 改动 Skill 后重新生成 sha256 清单
node tests/bridge_smoke.mjs # 端到端:拉起 Python 核心并调用一次工具
node tests/bridge_error_smoke.mjs # 端到端:确认 MCP 的 isError 会冒泡成异常
node tests/bridge_workspace_smoke.mjs # 端到端:相对路径按 session workspace 解析
python tools/refresh_ca_bundle.py --check # 检查随包 CA 是否完整(≥50 张根证书)
python tools/smoke_test.py --sub-id <真实 sub_id> # 对已安装包跑端到端冒烟(--dir 可指定包目录)
测试覆盖:URL 解析、时间戳格式化、分块时间区间回归、课件提取、工具 schema 健全性、
MCP 协议往返(initialize / tools/list / tools/call / 通知不响应)、
FIPS-197 AES-128 已知向量、Edge Cookie 失败原因分类、Cookie 主机精确匹配、Cookie 不回显与错误 redaction、
敏感本地文件拒绝、TLS 不降级与三级信任库回落、瞬时故障重试与证书失败不重试、isError 冒泡、
跨域重定向拒绝、讲义公式规范检查与自动修复、隐私扫描器自测、review 批次回归
(行内代码不被改写、修复前 .bak 备份、DOCX 段落保留、chunked/json 截断、切片时长、
Cookie WAL 回放、ABE 探测、PDF 报错带解释器路径、bridge 超时语义、系统提示适用范围)。
测试中的
subprocess/ 管道 / 临时文件在受限沙箱里可能被拒绝;这是环境限制,不是插件缺陷。 在普通终端或给予足够的文件/进程权限后运行即可。
发布 dsh 插件包
本仓库根目录就是 npm 包(包名 dsh-zju-lecture-note),发布即发布这个仓库:
npm login # 首次发布前登录(交互式,需本人操作)
npm pack --dry-run # 先看一眼会打进包里的文件清单
python tools/privacy_scan.py --tarball <生成的 .tgz> # 发布前隐私闸门
npm publish # 发布到 npm registry
files 字段是逐文件枚举的运行时清单(lib/、mcp_server.py、zju_lecture/*.py、skills/、
SECURITY.md……),不要改成整目录——否则 __pycache__/*.pyc 会混进包里
(files 的优先级高于 .npmignore)。
dsh 按
peerDependencies校验运行时版本。若升级 dsh 后插件被跳过,请检查@deepseek-ai/dsh-*的 peer 范围,或按 dsh 提示使用dsh plugin allow-version做精确版本豁免。
1.5.1 字幕主链路与投影抓取解耦
- 新增总开关:dsh 插件设置
enableSlides(默认false)与 MCP 环境变量ZJU_ENABLE_SLIDES。关闭时zju_fetch_slides不注册、不读登录态、不下载截图; 字幕主链路(zju_fetch_transcript等)完全不受影响。MCP 宿主未设置该变量时保持旧行为 (开启),设0即关闭;仓库自带的zju-lecture.mcp.json默认0。 - 代码层解耦:
zju_lecture/__init__.py用 PEP 562 懒加载download_slides/build_slides_pdf/SlideError;mcp_server.py只在进入zju_fetch_slides分支时才导入zju_lecture.slides。纯字幕进程的sys.modules里不会出现截图/PDF 模块。 - 协议层解耦:
tools/list与initialize.instructions按开关动态输出;关闭时即使客户端 直接调用zju_fetch_slides,也会得到可执行的「投影抓取未启用 + 如何开启」错误。 - Skill 拆分:
SKILL.md只保留字幕主链路;投影下载与插图规范移到skills/zju-lecture-summarizer/SLIDES.md,dsh 插件仅在enableSlides=true时追加到内存 Skill。 关闭时 Agent 不会看到投影工具的操作指南。 - 两层次开关:
enableSlides是总开关,embedSlideImages是「截图是否入讲义」子开关; 总开关关闭时子开关无效,避免出现「开了插图却抓不到投影」的误解。 - 图形化设置:dsh 不会自动渲染插件
Config。新增浏览器半边lib/client.js,通过settings.section在设置左侧导航注册独立的 「浙大智云课堂讲义」 一栏(不塞进通用设置);enableSlides/embedSlideImages标记为 Schemastery.volatile(),通过共享configForms服务读写并带 revision 校验。package.json增加dsh.client(./client导出、platform: web)。 - 新增
tests/test_feature_decoupling_v151.py:覆盖环境变量、工具过滤、RPC 错误、 字幕链路在关闭状态下可用、纯字幕进程不加载 slides 模块、Skill/清单/MCP 配置一致性; 另含浏览器独立设置栏的注册冒烟(tests/client_settings_smoke.mjs)。
1.5.0 智云投影 PPT 读取与讲义插图
zju_fetch_slides按真实接口修正:从 Cookie 父域.zju.edu.cn会话中解析_token并加Authorization: Bearer;按course_id+page/per_page分页并按截图 URL 去重;正确解包content.pptimgurl(以前把 JSON 字符串当文字输出)。- 新增
download/output_dir/dedup参数与 CLIslides --download:截图存到ppt-assets/<sub_id>/images/,用纯标准库合成PPT-<sub_id>.pdf(JPEGDCTDecode、 PNGFlateDecode),连续字节完全相同的截图折叠为一页。 - 明确「接口返回截图,不是文字层/OCR」:Skill 不再暗示能读取幻灯片文字;模型不能看图时只用字幕与本地课件,
需要幻灯片原文时让用户提供原始
.pptx走zju_extract_courseware。 - 安全:截图只允许
https://*.zju.edu.cn;ZJU 的http://自动升级 HTTPS;Cookie 只发给 ZJU 图片主机、 不发送 Bearer;跨主机重定向剥离凭据;写盘限制在工作区,最多 2000 张、单张 ≤32 MiB。 - 新增 dsh 设置
embedSlideImages(默认false)与 MCP 环境变量ZJU_EMBED_SLIDES=1: 开启后 Agent 会下载投影截图,并挑 3–8 张关键页以 Markdown 相对路径插进讲义; 关闭时只走字幕,避免额外下载与视觉 token。 zju_fetch_slides:省略download时跟随上述开关;输出新增「讲义插图模式」、每页工作区相对路径 与可直接粘贴的 Markdown 引用示例;下载结果带workspace_rel/note_example。- Skill 新增「投影截图入讲义」工作流:模型能看图时挑关键页内联,不能看图时只在文末列截图索引, 不臆造内容;图片路径按讲义所在目录调整。
- 参考
8eoyw/zju-learning-cli的公开实现思路(MIT), 详见ATTRIBUTION.md;其基于 numpy/Pillow 的近似笔画去重未移植,保持零第三方依赖。 - 完善致谢:补充
PeiPei233/zju-learning-assistant、8eoyw/zju-learning-cli及其上游参考项目的逐项说明。 - dsh 单独把
zju_fetch_slides的超时提高到 600s(下载整节课截图用),其他工具仍用配置值。 - 新增
tests/test_slides_v150.py,覆盖 token / 分页 / 父域 Cookie / 截图下载 / PDF / 工作区边界 / 插图开关与相对路径引用。
1.4.2 修正
@deepseek-ai/schemastery从dependencies改为peerDependencies(^3.18.1), 与插件市场的推荐写法、dshmarket保持一致:profile 里不会出现第二份运行时, peer 由 pnpm 在dsh plugin add时自动装好;本地目录安装只需补这一颗依赖。- 文档口径统一到 8 个工具:
zju_write_note在 1.4.1 加入后,README.md、ATTRIBUTION.md与lib/index.js的注释还写着 7。
1.4.1 修复了哪些问题
针对 实地运行反馈 的 8 项问题(真实一节课 sub_id=1988057 跑下来):
| # | 修复 |
|---|---|
| P1-1 | 相对路径按会话工作区解析(ZJU_WORKSPACE → PWD → 进程 cwd);找不到时回显已尝试的绝对路径。dsh 插件把 session workspace 传给 Python 子进程(同时作为子进程 cwd)。 |
| P1-2 | fix=true 新增块级 $$ 前后补空行修复器:区分开块/闭块(不往公式体里插空行)、跳过代码块、保留列表缩进。 |
| P2-1 | 检查报告按 code 聚合:[错误] display_not_isolated × 59 + 有界行号列表 + 少量示例,新增 max_samples(默认 3)。 |
| P2-2 | PPT / 视频直链失败分支改为单一结论 + 单一动作(reason 枚举 abe / locked / no_rows …),去掉错乱的编号与"退出 Edge 后重试;但 ABE 退出也无效"的自相矛盾。 |
| P2-3 | 新增 zju_write_note:文件名由 Agent 按「上课日期 + 本节内容」起名(name 参数,如 知识总结-2026-10-28-复变函数-保角映射.md),缺省才回退 知识总结-<sub_id>-<课程简称>.md;同名拒绝静默覆盖(返回 exists,要覆盖须显式 overwrite=true)。 |
| P3-1 | 备份改写到备份目录(ZJU_BACKUP_DIR,默认系统临时目录),报告给出绝对路径;不再污染 git status。 |
| P3-2 | Skill 清单新增 contentHash;插件启动告警打印两边的 sha256;Skill 增加「版本与副本核对」章节;新增防回归测试禁止"让用户提供 cookie"这类过期文案。 |
| P3-3 | 字幕返回 ASR 可靠度(噪声比 / 碎句比 / 平均句长 / 高频口头禅 + 处置建议),chunked / json 视图给 quality 对象;Skill 要求可靠度低时复算校核并在讲义顶部写明复核点。 |
1.4.0 修复了哪些问题
针对 issue #1 的 11 项审查:
| # | 修复 |
|---|---|
| 1 | fix=true 不再改写行内代码/围栏代码里的示例(讲义里"这样写是错的"不会被改坏) |
| 2 | 回写前先备份 <文件名>.bak,再原子替换;工具卡类型从 read 改为 edit(1.4.1 起备份写到 ZJU_BACKUP_DIR,默认系统临时目录) |
| 3 | DOCX 按 token 流解析,恢复段落、制表符、<w:br/> 与 XML 实体 |
| 4 | chunked / json 遵守 max_chars,超限时给 truncated / next_start_minute |
| 5 | 切片时同时报告"课程总时长"和"本次区间",不再把片段时长当整课 |
| 6 | 检测 app_bound_encrypted_key(ABE),命中时直接引导 ZJU_COOKIE_FILE |
| 7 | Cookie 兜底复制带上 -wal / -shm 并回放 WAL,不再误报"未登录" |
| 8 | config.json 移出敏感名单;凭据词改整词匹配,学术文件名不再误伤 |
| 9 | PDF 解析失败时汇总每种库的失败原因,并给出当前解释器的绝对路径 |
| 10 | isError 已能冒泡(1.3.3);超时仅在无其他在途调用时才回收 Python 子进程 |
| 11 | 系统提示段落声明适用范围,只对智云课堂相关任务生效 |
已知限制
- 投影抓取默认关闭:dsh 的
enableSlides默认false,所以升级后默认看不到zju_fetch_slides;需要投影时在插件设置里打开,或给 MCP server 设ZJU_ENABLE_SLIDES=1(仓库自带的zju-lecture.mcp.json保持0)。字幕主链路不受影响。 - 只解压 zip 就启用 →
Cannot find package '@deepseek-ai/schemastery': 它是 peer 依赖,由 pnpm 在dsh plugin add时装进 profile 的node_modules, 解压出来的目录里没有,所以直接加载会失败。 用dsh plugin --profile desktop add github:chenqianyu486-sys/dsh-zju-lecture-note安装, 或在本地目录先npm install --no-save --no-package-lock @deepseek-ai/schemastery@^3.18.1再dsh plugin ... add file:<路径>。 插件现在会把这类错误翻译成「缺少依赖 + 该怎么装」,不再只抛裸的模块找不到。 - Edge 登录态的两条死路:Chromium 会对 Cookie 数据库加独占锁;Edge 127+ 若启用应用绑定加密(ABE),
v20 Cookie 连 DPAPI 也解不开。插件会读
Local State里的app_bound_encrypted_key判定 ABE, 并在错误信息里直接指向ZJU_COOKIE_FILE(而不是反复让你退出 Edge);读库兜底复制会带上-wal/-shm并让 SQLite 回放 WAL,避免"刚登录却被判未登录"。 - PDF 课件需要可选依赖
pypdf(pip install pypdf);其余格式零依赖。 - SSL / 代理:信任库按
certifi→ 随包cacert.pem→ 系统根证书 回落,所以 dsh 自带的无 certifi 解释器也能正常握手;真正会失败的是代理做了 HTTPS 解密或系统时间偏差过大。错误消息会给出当前 信任库、CA 张数、代理环境变量与具体修法(把classroom.zju.edu.cn设为直连或放进NO_PROXY)。 插件不会为了"能跑"而关闭证书校验。瞬时抖动(截断读包、握手 EOF、超时、5xx)已内置 3 次退避重试。 - 同名 Skill 的磁盘副本会抢名字:若
$DSH_HOME/skills/zju-lecture-summarizer/下还留着一份旧副本, 一个名字只保留一份、重复的仅打 warning,Agent 可能一直读那份过期 SOP。SKILL.md 的 front matter 里 没有版本字段,所以只能比内容:清单里的contentHash(sha256:...)是权威值,插件启动时会在 stderr 打印插件内置版与磁盘副本的 sha256,并给出删除路径。 - 讲义文件名不再固定:1.4.1 起用
zju_write_note,文件名由 Agent 按「上课日期 + 本节内容」起名 (知识总结-2026-10-28-复变函数-保角映射.md),缺省才回退知识总结-<sub_id>-<课程简称>.md。 固定的知识总结.md会让下一节课静默覆盖上一节,而讲义通常被.gitignore忽略、覆盖后无法恢复。 - git 历史:历史中已有的个人路径/密钥不会因为当前文件干净而消失;
用
python tools/privacy_scan.py --history检查,必要时重写历史并轮换凭证。 - 形态别选错:
tools/build_plugin.py产出的是 MCode 插件包;装进 dsh 用的是本仓库根目录的 npm 包(dsh plugin ... add)。两者是两套安装方式,不要混。 - 字幕接口公开,但请遵守平台条款:一次一节课,不要批量抓取。
许可与致谢
本插件以 MIT 许可证发布,改编自 nzw806/zju-lecture-summerizer-(MIT),
原项目的版权声明保留在 LICENSE。
特别感谢这些项目与同学:
PeiPei233/zju-learning-assistant(ZLA,MIT): 智云课堂 / 学在浙大 API 流程的原始实现,本插件与参考 CLI 的共同上游;它的桌面端经验让很多边界问题有据可查。8eoyw/zju-learning-cli(MIT):v1.5.0 投影 PPT 管线的直接参考——_token→Authorization: Bearer、search-ppt分页去重、截图合成 PDF、ZJUhttp://升级 HTTPS。 本仓库按零第三方依赖和更严格的下载边界重新实现,没有拷贝其代码;详见ATTRIBUTION.md。certifi与 Mozilla CA Certificate Program: 随包cacert.pem的来源,保证 dsh 自带的无 certifi 解释器也能正常 TLS 握手。8eoyw/zju-learning-cli自身致谢的eWloYW8/ZJU-course-material-download、Kcalb35/Tronclass-pdf-downloaderforChrome、fish-can/TronClass-PDF-Downloader、xzzd-pro/xzzd-pro:课件/PDF 下载思路的上游参考。 本插件目前没有使用这些下载端点,列出它们是为了保持致谢链完整。- 以及为本插件提交 issue、做实地运行反馈、审查代码与文档的同学——你们的复现步骤和原始输出是这些修复的起点。
逐文件来源、第三方许可证与「参考但未拷贝」的边界见 ATTRIBUTION.md。
仅供个人学习使用:只抓取本人有权限访问的回放,Cookie 属个人凭证,不要外传。
Comments
Loading…
Similar plugins
by HaoKuo
dsh-notebook-studio 是 DeepSeek Harness(dsh)插件:上传多篇 PDF,用提示词做文献综述、关键信息提取与研究设计,并生成带页码引用的报告(DOCX/PDF)或演示文稿(PPTX/PDF)。 | dsh-notebook-studio is a DeepSeek Harness (dsh) plugin: upload PDF papers and use prompts for a literature review, key-finding extraction and a study design, then generate page-cited reports (DOCX/PDF) or slide decks (PPTX/PDF).
★ 0
MIT
JavaScript
Sep 28, 2026
dsh plugin --profile web add dsh-notebook-studioby wjx-ai
DSH Markdown 阅读插件:点击会话中的 MD/图片链接,在浏览器右侧以真三栏布局(侧边栏 | 会话区 | 文档)直接阅读 —— 目录 TOC、自动跟随磁盘变更、图片内联预览、复制原文、字号缩放。dsh plugin --profile web add github:wjx-ai/dsh-md-reader
★ 0
MIT
JavaScript
Sep 22, 2026
dsh plugin --profile web add dsh-md-readerby a1141171521
DSH 自动记忆系统:本地优先、文本可读、失败可见的分层记忆。
★ 3
MIT
JavaScript
Oct 31, 2026
dsh plugin --profile web add dsh-memfuseby literaf
Paper → academic talk for DeepSeek Harness: reads the real full text, budgets the slot per venue (组会汇报/会议报告/答辩/开题/文献汇报), builds the deck around the figures and writes the speaker notes as the script.
★ 1
MIT
TypeScript
Aug 17, 2026
dsh plugin --profile web add dsh-paper-slidesby timestatic
Notes taken from DeepSeek Harness conversations: quotes whole replies or selected passages, keeps highlights rendered at their original position in the session, attaches editable Markdown notes, tags
★ 0
Apache-2.0
JavaScript
Oct 18, 2026
dsh plugin --profile web add @timestatic/dsh-session-notebookby copylee711
Academic tools in TypeScript, no Python or MCP server: the Zotero library through its local API or zotero.org (search, annotations, full text, citation export; add by DOI / arXiv id, notes and tags wh
★ 4
↓ 1.4k/wk
MIT
TypeScript
Oct 22, 2026
dsh plugin --profile web add @copylee/dsh-academic