聚合课表作业通知,一句话查遍校园各系统
54.7/100
综合分
71
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/11/4 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查未通过:缺少 main/exports/bin 入口声明;engines.node 要求 >=24.18.0 <25,不满足 Node 22.19.0;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Anchorite-no/dsh-zju-assistant该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · platform
装得上吗
作者未把包发布到 npm,只能用源码安装
插件本身没问题,是分发方式不同
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 0 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装缺少 main/exports/bin 入口声明;engines.node 要求 >=24.18.0 <25,不满足 Node 22.19.0;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-zju-assistant-workspace(未发布到 npm,仅可源码安装)
✗Node 引擎要求 >=24.18.0 <25 · 基线 Node 22.19 不满足
✓dsh CLI 依赖要求 0.2.1-rc.5 · 最新 0.2.1-rc.5 兼容
✗入口文件缺少入口声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/4 06:33:53
🔴 安全扫描发现高危能力自动 · 静态分析源码
v2.7.0🔴 扫描:高危尚无人工评估
静态扫描发现安装期即执行代码,或命令参数由运行时变量拼接(参数来源无法静态判定,从严处理)。
安装时即执行脚本可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 69 处,展示前 69 条)
- 执行外部命令来自配置/变量scripts/setup.mjs:22
const child = spawn(process.execPath, args, { cwd: path.join(root, core), stdio: 'inherit', windowsHide: true }) - 读取本地文件core/dingtalk/lib/cli.ts:131
if (existsSync(membersPath) || op === 'members') { - 读取本地文件core/dingtalk/lib/database.ts:246
return readdirSync(dir, { withFileTypes: true }).some(entry => entry.isDirectory() && (entry.name === 'globalStorage' || isAccountName(entry.name))) - 读取本地文件core/dingtalk/lib/database.ts:278
if (isAccountName(path.basename(at)) && existsSync(path.join(at, 'DBFiles'))) return path.dirname(at) - 读取本地文件core/dingtalk/lib/database.ts:306
entries = readdirSync(base, { withFileTypes: true }) - 读取本地文件core/dingtalk/lib/database.ts:351
const decoded = strictBase64(readFileSync(file)) - 执行外部命令来自配置/变量core/wechat/lib/chat-media.ts:56
const child = spawn(ffmpeg!, ['-v', 'error', '-f', 'hevc', '-i', 'pipe:0', '-frames:v', '1', '-n', preview], { windowsHide: true, stdio: ['pipe', 'ign - 执行外部命令固定命令core/wechat/lib/macos.mjs:70
const result = spawnSync('/usr/bin/pgrep', ['-x', 'WeChat'], { encoding: 'utf8', timeout: 5000 }) - 执行外部命令来自配置/变量core/wechat/lib/macos.mjs:83
const child = spawn(command, args, { stdio: ['pipe', 'pipe', 'pipe'] }) - 发起网络请求core/weread/lib/article.ts:87
const response = await fetch(url, { headers: { referer: article.url, 'user-agent': 'Mozilla/5.0' }, signal: AbortSignal.timeout(30000) }) - 发起网络请求core/weread/lib/web.ts:15
const response = await runtime.request(target, { method: extra.body ? 'POST' : 'GET', body: extra.body ? JSON.stringify(extra.body) : undefined, - 发起网络请求plugin/client/api.ts:4
const response = await fetch('/api/zju-campus', { - 发起网络请求plugin/host/affairs.mjs:252
run = this.fetch(source, status.owner).finally(() => { this.running.delete(id) }) - 发起网络请求plugin/host/affairs.mjs:259
async fetch(source, owner) { - 执行外部命令来自配置/变量plugin/host/browser.mjs:47
try { return execFileSync(command, args, { encoding: 'utf8', windowsHide: true, timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }) } catch { return - 请求外部主机dshplugin/host/campus.test.mjs:47
const response = await routes.get('/api/zju-campus').fetch(new Request('http://dsh/api/zju-campus', { method: 'POST', body: JSON.stringify({ action, i - 发起网络请求plugin/host/campus.test.mjs:47
const response = await routes.get('/api/zju-campus').fetch(new Request('http://dsh/api/zju-campus', { method: 'POST', body: JSON.stringify({ action, i - 请求外部主机weread.qq.complugin/host/campus.test.mjs:135
const response = await bridge.request({ url: 'https://weread.qq.com/web/shelf/sync', headers: {} }) - 发起网络请求plugin/host/campus.test.mjs:135
const response = await bridge.request({ url: 'https://weread.qq.com/web/shelf/sync', headers: {} }) - 发起网络请求plugin/host/campus.test.mjs:139
assert.throws(() => bridge.request({ url: 'https://example.com/' }), /其他站点/) - 发起网络请求plugin/host/cc98-feed.mjs:147
if (due) added.push(...await this.fetch(name)) - 发起网络请求plugin/host/cc98-feed.mjs:155
async fetch(name) { - 发起网络请求plugin/host/chaoxing.mjs:181
async fetch(status, args, read, before, place, { system = SYSTEM, timeoutMs = 120_000 } = {}) { - 发起网络请求plugin/host/chaoxing.mjs:209
await this.fetch(status, ['chaoxing', 'course', 'list'], data => ({ items: (data?.courses ?? []).flatMap(courseOf) }), before, (cache, entry) => ({ .. - 发起网络请求plugin/host/chaoxing.mjs:273
await this.fetch(status, ['chaoxing', 'chapter', 'list', `--course=${course.link}`, ...(force ? ['--fresh'] : [])], data => ({ - 发起网络请求plugin/host/chaoxing.mjs:290
await this.fetch(status, ['lab', 'timetable'], data => ({ - 执行外部命令来自配置/变量plugin/host/chat-watch.mjs:231
const child = spawn( - 执行外部命令固定命令plugin/host/chat-watch.mjs:252
const killer = spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], { - 执行外部命令来自配置/变量plugin/host/core.mjs:46
return spawn(process.execPath, [script, ...args], { ...options, env: nodeEnv(env), windowsHide: true }) - 执行外部命令固定命令plugin/host/documents.mjs:10
execFile('powershell.exe', ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', command], { windowsHide: true, timeout: 10_000 }, (error, stdout) - 执行外部命令固定命令plugin/host/downloads.mjs:11
execFile('powershell.exe', ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', command], { windowsHide: true, timeout: 10_000 }, (error, stdout) - 发起网络请求plugin/host/feed.mjs:231
if (this.status().state === 'ready' && due(this.load(), FRESH_MS)) await this.fetch() - 发起网络请求plugin/host/feed.mjs:256
async fetch() { - 发起网络请求plugin/host/holidays.mjs:163
const response = await this.fetch(MIRROR(year), { signal: AbortSignal.timeout(15_000) }) - 发起网络请求plugin/host/index.mjs:137
fetch: request => this.request(request), - 发起网络请求plugin/host/learn.mjs:85
const response = await fetch(url, { signal: AbortSignal.timeout(10 * 60_000) }) - 执行外部命令固定命令plugin/host/learn.mjs:125
const child = batch ? spawn('cmd.exe', ['/d', '/s', '/c', command, ...args], { cwd, env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) - 执行外部命令来自配置/变量plugin/host/learn.mjs:126
: spawn(command, args, { cwd, env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) - 发起网络请求plugin/host/learning.mjs:134
async fetch(status, args, read, before, place, { timeoutMs = 120_000, retries = 0 } = {}) { - 发起网络请求plugin/host/learning.mjs:242
await this.fetch(status, ['zhiyun', 'search', 'courses', '--scope', 'mine', '--limit', '200'], - 发起网络请求plugin/host/learning.mjs:255
await this.fetch(status, ['zhiyun', 'search', 'lessons', '--course', id], - 发起网络请求plugin/host/learning.mjs:334
await this.fetch(status, ['zhiyun', 'search', 'resources', '--course', course.id, '--lesson', String(Number(number))], (data) => { - 发起网络请求plugin/host/mail-feed.test.mjs:35
async *fetch(range, query, options) { - 发起网络请求plugin/host/mail-feed.test.mjs:123
mail.imap = () => ({ on() {}, close() {}, async connect() {}, async mailboxOpen() { return { uidValidity: 1n } }, async search() { return [] }, async - 发起网络请求plugin/host/mail.mjs:174
if (page.length) for await (const item of client.fetch(page.join(','), { uid: true, envelope: true, flags: true, size: true }, { uid: true })) { - 发起网络请求plugin/host/mail.mjs:200
if (page.length) for await (const item of client.fetch(page.join(','), { uid: true, envelope: true, internalDate: true }, { uid: true })) { - 发起网络请求plugin/host/mail.test.mjs:24
async *fetch(range, query, options) { assert.equal(options.uid, true); for (const uid of range.split(',').map(Number)) yield { uid, size: 40, envelope - 删除本地文件plugin/host/model.mjs:24
const forget = () => { for (const attachment of saved) { try { const file = attachments.imageHostPath?.(attachment); if (file) rmSync(file, { force: t - 发起网络请求plugin/host/pta-verification.test.mjs:90
const ctx={http:{jar:new CookieJar(),userAgent:DEFAULT_UA},close(){},async fetch(url,options={}){ - 执行外部命令来自配置/变量plugin/host/ts-loader.test.mjs:25
const run = (env) => spawnSync(process.execPath, [path.join(pkg, 'main.ts')], { encoding: 'utf8', env: { ...process.env, ...env } }) - 执行外部命令来自配置/变量plugin/host/wechat.mjs:47
const child = spawn(process.execPath, [path.join(CORE_ROOT, 'weread', 'scripts', 'weread.ts'), ...args], { - 发起网络请求plugin/host/weread-bridge.mjs:107
async fetch(url, { method = 'GET', headers = {}, body } = {}) { - 发起网络请求plugin/host/weread-bridge.mjs:117
return this.fetch(url, { method, headers, body }) - 发起网络请求plugin/host/weread-bridge.mjs:156
const response = await this.fetch(`${ORIGIN}/web/shelf/sync?userVid=&synckey=0&lectureSynckey=0`, { headers: { accept: 'application/json', referer: `$ - 发起网络请求plugin/host/weread-bridge.mjs:197
const { status, body } = await this.fetch(url, { headers: { referer: `${ORIGIN}/`, ...(proof ? { 'x-wr-ticket': proof.ticket, 'x-wr-randstr': proof.ra - 发起网络请求plugin/host/weread-bridge.mjs:244
const actions = onlyPta ? { ptaCaptcha:(input,call)=>this.ptaCaptcha(input,call) } : { cookie: name => this.cookie(name), request: input => this.reque - 发起网络请求plugin/host/xuezai.mjs:162
async fetch(status, args, read, before, place) { - 发起网络请求plugin/host/xuezai.mjs:220
await this.fetch(status, ['xuezai', 'course', 'list'], data => (data?.courses ?? []).flatMap(courseOf), before, (cache, entry) => ({ ...cache, courses - 发起网络请求plugin/host/xuezai.mjs:228
await this.fetch(status, ['xuezai', 'material', 'list', `--course=${found.link}`], data => (data?.materials ?? []).map(fileOf), before, - 执行外部命令固定命令plugin/host/zju-tool.mjs:27
const killer = spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' }) - 发起网络请求plugin/host/zju-tool.mjs:82
const outcome = await approval.request({ - 执行外部命令来自配置/变量scripts/check-package.mjs:5
const result = spawnSync(process.execPath, [process.env.npm_execpath, 'pack', '--dry-run', '--ignore-scripts', '--json'], { encoding: 'utf8', windowsH - 执行外部命令固定命令scripts/check-security.mjs:10
const result = spawnSync('git', args, { cwd: root, encoding: 'utf8', windowsHide: true }) - 执行外部命令固定命令scripts/check-security.mjs:21
const configured = spawnSync('git', ['config', '--local', '--get', 'security.gitleaksPath'], { cwd: root, encoding: 'utf8', windowsHide: true }).stdou - 执行外部命令来自配置/变量scripts/check-security.mjs:24
const result = spawnSync(binary, ['git', root, '--redact=100', '--no-banner', ...(staged ? ['--pre-commit', '--staged'] : ['--log-opts=--all'])], { cw - 读取本地文件scripts/private-paths.cjs:39
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - 读取本地文件scripts/private-paths.cjs:54
const stat = fs.lstatSync(from) - 写入本地文件scripts/private-paths.cjs:56
if (!stat.isDirectory()) { fs.mkdirSync(path.dirname(to), { recursive: true }); fs.copyFileSync(from, to); return } - 读取本地文件scripts/private-paths.cjs:57
for (const entry of fs.readdirSync(from)) {
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库
涉及外部主机:dsh、weread.qq.com
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-slots给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成ZJU All-in-One Assistant
浙大学生的一站式校园 AI 助手,以 DeepSeek Harness 插件形式提供,支持 Windows 和 macOS。
把散在各处的校园信息串成你自己的日常。教务网、学在浙大、智云课堂、学习通、PTA、学校和学院的通知、公众号,再加上你选定的钉钉群和微信群,接到一起,按课程整理成一份个人档案。问一句话,它去各个网站查、比对,把结果交给你。
这是学生个人项目,不是浙江大学的官方产品,和学校各系统的运营方没有关系。
现在的状况(2026 年 11 月)
| 形态 | 给谁用 | 状态 |
| DeepSeek Harness 插件(dsh-zju-assistant) | 装了 DeepSeek Harness 的同学,有界面 | 桌面版的功能已经搬过来,在 DeepSeek Harness 0.2.1-rc.5 上测过;已发到 npm(dsh-zju-assistant),在插件页填包名就能装 |
| ZJU Skill | 用 Codex、Claude Code 这类命令行助手的同学 | 照常维护,另一个仓库 |
插件和 Skill 底下是同一套校园命令,区别在有没有界面。
为什么改成插件
最想要的是用起来没感觉。平时用 AI 是什么流程,查课表、交作业也是那个流程,不用为了校园这点事再单独装一个桌面应用。
展开完整文档(README 剩余 1 节)
ZJU All-in-One Assistant 浙大学生的一站式校园 AI 助手,以 DeepSeek Harness 插件形式提供,支持 Windows 和 macOS。 把散在各处的校园信息串成你自己的日常。教务网、学在浙大、智云课堂、学习通、PTA、学校和学院的通知、公众号,再加上你选定的钉钉群和微信群,接到一起,按课程整理成一份个人档案。问一句话,它去各个网站查、比对,把结果交给你。 这是学生个人项目,不是浙江大学的官方产品,和学校各系统的运营方没有关系。 现在的状况(2026 年 11 月) | 形态 | 给谁用 | 状态 | | DeepSeek Harness 插件(dsh-zju-assistant) | 装了 DeepSeek Harness 的同学,有界面 | 桌面版的功能已经搬过来,在 DeepSeek Harness 0.2.1-rc.5 上测过;已发到 npm(dsh-zju-assistant),在插件页填包名就能装 | | ZJU Skill | 用 Codex、Claude Code 这类命令行助手的同学 | 照常维护,另一个仓库 | 插件和 Skill 底下是同一套校园命令,区别在有没有界面。 为什么改成插件 最想要的是用起来没感觉。平时用 AI 是什么流程,查课表、交作业也是那个流程,不用为了校园这点事再单独装一个桌面应用。插件装在 DeepSeek Harness 里,和你平时写东西、问问题用的是同一个助手、同一套设置。 另一个原因是维护。桌面版本来就是 DeepSeek Harness 的二开。引擎、对话、设置都是 dsh 的,我们自己写的是一层 Electron 壳加一个界面包。官方现在有自己的桌面版和插件页,这层壳就成了重复的东西:窗口、托盘、自动更新、安装程序、Mac 的玻璃效果都得自己维护,引擎每升一次还要跟着改壳(0.2.1 就把好几个界面包从依赖里拿掉了)。 多端也维护不过来。Windows 一个 exe,Mac 按芯片两个 dmg,都没有代码签名,Windows 要过 SmartScreen,Mac 第一次打开要去系统设置里放行。手头没有 Mac,0.1.7 的 Mac 版发出去以后才知道一打开就插件加载失败,是 PR #5 的作者先发现的。 改成插件以后,Windows、macOS 的桌面版和网页版都由官方维护,安装和升级走 dsh 自己的插件页,这个仓库只管校园功能。比赛时为了展示加的东西这次没有搬,包括品牌、窗口玻璃效果、托盘、ChatGPT 登录、执行过程右栏、对话后自动整理记忆,界面上讲功能怎么用的灰色说明小字也删了。 能做什么 | 方面 | 内容 | | 课程和作业 | 课表、成绩、课程信息;学在浙大和 PTA 的作业、截止时间;学在浙大、学习通的课件下载,智云课堂回放的转写 | | 通知 | 教务网、学工部、研工部、团委、综合服务网和本人学院官网的通知公告;二课、三课的活动和本人记点 | | 校园生活 | 体艺场馆的空场、预约须知和放号时间;CC98 帖子;图书馆电子书在哪个平台有全文;知网检索和下载 | | 消息 | 浙大邮箱收信和写信;微信公众号文章;你选定的微信群和钉钉群(只读本机已有的聊天记录) | | 学习 | 个人档案记下你学到哪、喜欢怎么听讲;把资料讲成带画面的讲解 | | 文档 | PDF、Word、PPT、Excel 取文字,PDF 找页、切页、取图,照片和 Word 转 PDF | 会改动你账号状态的操作,比如场馆预约、选课、交作业、二课报名、发邮件,只在你明确要它做时才执行,插件里执行前还要过一道 dsh 的批准(弹不弹窗问你,看你在 dsh 里设的权限模式)。发邮件之前会先给你看完整的草稿。 研究生课在研究生院的教务系统(研究生教务网)里,本科教务网查不到。日程表两边都读:研究生的课从研究生教务网来,选了研究生课的本科生两边的课排在同一张表上。研究生的考试安排还读不到。 插件版 装好以后有什么 - 侧栏多三页。「日程表」把一周的课(本科和研究生教务网的)、考试、作业截止、带时间的备忘和自己加的日程排在一张表上,右上角的「+」加日程,加过的点一下就能改;「校园事务」是作业、备忘录、校园通知和个人消息;「学习」是智云、学在浙大和超星。 - 设置里多一页「个人档案」,放基础信息和记忆。 - 插件页「ZJU All-in-One Assistant」下面有十个子插件,每个都能单独开关,关掉的那一项页面和命令一起停。 - 这几页上的校内链接(学校的网站和 CC98)在插件用默认浏览器单独开的窗口里打开,直接带着插件已经登录好的统一身份认证和 WebVPN,不用再登一遍;在校外时自动经 WebVPN 走,链接本身还是原来的地址。CC98 自己的账号在这个窗口里登一次,以后都记得。别的网站用系统的默认浏览器打开。 | 子插件 | 管的系统 | 默认 | | 校园账号 | 统一身份认证、WebVPN;其余各项都靠它 | 开 | | 课程与学习 | 教务网、研究生教务网、学在浙大、PTA、大物实验课、智云课堂、学习通、电子书、知网 | 开 | | 课程讲解 | 把一次课或一份资料讲成带画面的讲解;打开时下载动画用的库(约 6 MB) | 关 | | 活动与场馆 | 素质拓展平台、体育场馆 | 开 | | 校园社区 | CC98、查老师 | 开 | | 校园通知 | 学校部门和学院官网的通知 | 开 | | 浙大邮箱 | 浙大邮箱 | 开 | | 微信聊天 | 本机微信的聊天记录,Windows 和 macOS(Mac 上的条件见下面);数据挪出了默认位置时在配置里选文件夹 | 关 | | 钉钉聊天 | 本机钉钉的聊天记录(v2、v3 账号库都认),Windows 和 macOS;数据不在默认位置时在配置里选文件夹 | 关 | | 微信公众号 | 微信读书里加了书架的公众号 | 关 | 助手那边只多一个 zju-campus 技能,不进输入框的 / 菜单,也不改 dsh 原来的系统提示。用 dsh 写代码时它不会跑出来。 安装 1. 到 DeepSeek Harness 官网 下载安装官方桌面版,0.2.1-rc.3 或更新的版本;也可以不装桌面版,用 npx @deepseek-ai/dsh web 在浏览器里开。 2. 打开侧栏「插件」,点「添加插件」,填 dsh-zju-assistant,装好后点「立即启用」。 3. 在插件页「ZJU All-in-One Assistant」的「校园账号」里填统一身份认证的学号和密码、WebVPN 的校园网账号;PTA、实验课、学习通、校园卡查询密码、CC98 在各自子插件的配置里填。 4. 模型在 dsh 的设置里配,登录 DeepSeek 账号或者填 API Key。 5. 扫码登录微信读书、过微信读书和 PTA 的验证码,插件会用你电脑上的默认浏览器单独开一个窗口来做,用的是插件自己的一份浏览器资料,不碰你平时的登录和记录;默认浏览器不是 Chrome、Edge 这类 Chromium 内核的(比如 Firefox、Safari)时,改用电脑上装着的 Chrome 或 Edge。PTA 滑块在本地识别并自动拖动,不需要模型;微信读书验证码由你在 dsh 里选的视觉模型自动作答。识别失败或遇到不支持的题型时,停在窗口里等你自己做完。不用另装别的插件。 6. 聊天里助手给的链接(比如 CC98 的帖子)开在哪里由 dsh 决定,默认开在 dsh 右侧栏的浏览器里,这个浏览器关掉 dsh 就忘了登录。想用默认浏览器打开,在 dsh 的「设置 → 通用设置 → 网页链接默认打开方式」里选「默认浏览器」;不改设置时,按住 Ctrl 或 Shift(Mac 上也可以按 Cmd)点链接也会用默认浏览器打开。 在 macOS 上 插件装在 macOS 版的 DeepSeek Harness 里也能用,校园命令、页面和技能两边是同一套。有几处不一样: - 微信聊天要微信一直开着、登录着(Windows 上能离线查,Mac 上不能)。第一次用时会用系统的 clang 编一个只读的小程序去读微信进程,要先装 Xcode 命令行工具(终端里运行 xcode-select --install),系统可能弹出开发者工具授权。系统完整性保护(SIP)开着时,系统多半不让读官方微信的进程,这时会报「macOS 不让读取微信进程」;插件不会去改 SIP 或微信。Mac 后端来自 weread-skill,原作者是在关了 SIP 的 Mac 上测的。 - 钉钉聊天读 ~/Library/Application Support/DingTalkMac(App Store 版在 ~/Library/Containers 下的沙盒里),不用装别的。原作者在 Apple 芯片、钉钉 9.0.2 上测过,Intel Mac 和别的版本没测过。 - 课程讲解要的 TeX 不能自动装,要自己装 MacTeX,或者小一些的 BasicTeX 再用 tlmgr 装上 dvisvgm;检查版面用「应用程序」文件夹里的 Chrome 或 Edge。 两个聊天的 Mac 支持是 2026-11-02 从 weread-skill、dingtalk-skill 迁过来的,我们手上没有 Mac,迁过来以后只用合成数据测过。 运行方式 - 插件数据放在 ~/.dsh/zju-campus;指定了 DSH_HOME 时跟随该目录。账号在 DSH 插件页填写。 - 没有托盘。dsh 开着时才会在后台检查通知、邮件和聊天,新消息在侧栏「校园事务」上出一个圆点,允许过的话也弹浏览器通知。 - 课程讲解暂时没有配音。 - 讲解在新标签页里播放,讲解里的脚本放在沙箱里跑,碰不到 dsh 的接口。 - FFmpeg 第一次用到时才下载,下完核对校验值。 数据和隐私 这个项目没有自己的服务器,数据都在你的电脑上。 - 校园账号和密码加密保存在 ~/.dsh/zju-campus/profile/accounts.json,密钥由 Windows 凭据管理器或 macOS 钥匙串保管,旧明文在更新后的插件首次读取时自动迁移;个人资料、记忆、备忘录也在这个文件夹里,是一条一个的 Markdown 文件。 - 各系统的登录状态和缓存在 ~/.dsh/zju-campus/zju 和 cache。 - 浙大邮箱的客户端专用密码先经同样的系统凭据库保护,再将密文存入 dsh 的凭据服务。dsh 自己的模型 API Key 仍由宿主管理。详见 凭据与本地数据。 - 下载的课件、转写和讲解在「文档」文件夹的 CampusAgent 下(macOS 上是 ~/Documents/CampusAgent),在页面上点「下载」存的文件在「下载」文件夹的 CampusAgent 下。 对话内容会发给你在 dsh 里选的模型服务,助手查到的校园数据也会随对话一起发过去。 从源码运行 需要 Node.js 24.x(24.18 以上)和 npm 11。 npm ci npm run verify npm run verify 检查 DSH 插件及共用核心的类型和测试。npm run build 构建插件页面,npm run security 检查凭据与发布范围。 插件在 plugin/(实现包 dsh-zju-campus)和 plugin/bundle/(插件页上那张「ZJU All-in-One Assistant」卡片,组合包 dsh-zju-assistant)。开发时装进本机的 dsh 用链接安装,改了代码不用重装: 1. node plugin/build.mjs 打包页面那一半; 2. 在 plugin/bundle 里跑一次 pnpm install,建出指向实现包的链接; 3. dsh plugin --profile <名字> add <仓库的绝对路径>\plugin\bundle。装进官方桌面版时先完全退出桌面版,用它自带的 resources\runtime\cli\bin\dsh.cmd,profile 名是 desktop。 改了引擎侧的代码重开 dsh 生效,改了页面先重新跑第 1 步。 项目结构 | 位置 | 内容 | | plugin/ | 插件的实现:host/ 是引擎侧(档案、账号、zju 工具、各子插件),client/ 是页面,skill/ 是 zju-campus 技能,rows.mjs 是十个子插件的清单 | | plugin/bundle/ | 组合包:插件页上的卡片「ZJU All-in-One Assistant」和十行的 patch | | core/zju/ | ZJU Core:校园系统的命令和参考文档,插件和 ZJU Skill 用的是同一套命令 | | core/learn/ | Learning Core:把资料讲成带画面的讲解 | | core/weread/ | WeRead Core:从微信读书找公众号文章,读正文、存成 Markdown | | core/chat/、core/wechat/、core/dingtalk/ | 本机微信、钉钉聊天记录的只读查询,Windows 和 macOS 版客户端 | | core/initialize/、core/skills/ | 初始化和几条工作流:选课、考前复习、课程学习、我的一周、交作业 | 引擎是 DeepSeek Harness(@deepseek-ai/dsh)。第三方代码和素材的许可见 第三方声明。 致谢 钉钉 v2 账号库的支持来自 @YidanPan 的 #4,Mac 版插件加载失败的问题是 @Silhouette-my 在 #5 里先发现的,Mac 上读微信、钉钉聊天的做法来自 @spdc-elm 给 weread-skill 和 dingtalk-skill 做的 macOS 支持。 许可证 项目自有代码采用 GNU GPL v3(仅限第 3 版),SPDX 标识为 GPL-3.0-only。允许使用、修改和再分发;分发本项目或其衍生程序时,须按 GPL 提供相应源码并保留许可。 第三方代码、依赖和素材沿用各自许可,见 第三方声明。
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Anchorite-no/dsh-zju-assistant
聚合课表作业通知,一句话查遍校园各系统
相关插件 · 平台集成
智能体工程课walkinglabs/learn-harness-engineering
一门项目制课程,教你搭建环境、管理状态、做验证、加控制机制,让 AI 编程智能体稳定干活。
★ 21,155已汉化DSH
71.7
视频翻译配音liuzhao1225/YouDub-webui
开源视频本地化工作台:导入视频,识别、翻译并逐句克隆原声,生成字幕和配音成片。
★ 5,792已汉化Cordis
71.4
失败经验库Ikalus1988/MisakaNet
📚 零依赖、由 git 支撑的微课程库,让 AI 智能体异步分享并检索经过验证的调试经验。| https://misakanet.org
★ 550已汉化DSH
71.2
IM 机器人接入xmanrui/dsh-im
让 DeepSeek Harness 触手可及 Connecting DeepSeek Harness
★ 3,170已汉化DeepSeek 客户端
71
桌面智能体工作区freestylefly/wesight
WeSight 是开源桌面控制台,面向本地 AI 智能体。它帮你安装或复用 Claude…
★ 933已汉化MCP
71
中国平台研究引擎Jesseovo/last30days-skill-cn
给 AI Agent 的中文互联网研究 Skill:检索 8 大平台最近 30…
★ 2,041已汉化DSH
70.7
这是你的仓库?认领条目