# openworkbuddy **Repository Path**: kdkq/openworkbuddy ## Basic Information - **Project Name**: openworkbuddy - **Description**: No description available - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-27 - **Last Updated**: 2026-08-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # OpenWorkBuddy **腾讯 WorkBuddy 的开源复刻版。** 一句话下任务,AI 自己规划、拆解、动手,最后交给你**能打开验收的成果文件**——PPT、Word、Excel、网页、调研报告、公众号推文。 跑在你自己的电脑或服务器上,数据和 API Key 都不出本机。不绑定任何一家大模型:DeepSeek / 通义 Qwen / 智谱 GLM / Kimi / OpenRouter / Ollama 本地模型,界面里点一下就切。 > 作者:开发者猫叔 · 个人与非商业用途免费,商业使用需要单独授权(见 [LICENSE](LICENSE) 与 [COMMERCIAL-LICENSE.md](COMMERCIAL-LICENSE.md)) **目录**:[能干什么](#能干什么) · [为什么用它](#为什么用它) · [安装启动](#安装启动从零开始) · [配置模型](#配置模型) · [多人一起用](#多人一起用多租户) · [扩展它](#扩展它技能--连接器--插件) · [手机远程指挥](#手机远程指挥im) · [定时任务](#定时任务) · [安全](#安全这个-agent-手里有-shell) · [部署到服务器](#部署到服务器) · [参与贡献](#参与贡献) · [实现细节](#实现细节想深入的再看) --- ## 能干什么 给它一句话,它自己干完并交付文件。几个真实用法: - 「帮我出一份 Q3 复盘 PPT,数据用这个 Excel」 → 读表 → 算 → 生成 `.pptx` - 「调研一下国内 AI 陪伴产品,出一份报告」 → 联网搜 → 逐个打开读 → 出 Markdown / Word - 「把这份材料做成一个能在手机上看的网页」 → 写 HTML → 本机起服务 → 手机扫码看 - 「每天早上 9 点抓行业新闻,做成晨报发我飞书」 → 定时任务 + IM 推送 | | | |---|---| | 🤖 **Agent 自主执行** | 自然语言 → 规划 → 工具循环 → 交付文件,每一步在工作台实时可见 | | ⚡ **多任务并行** | 每个对话独立跑任务,互不排队;任务运行中发消息默认「插队」并入当前任务;刷新页面 / 断网 / 电脑睡醒都会自动断点续流接回直播;后台任务完成、等待审批都有通知 | | 🗂️ **成果按对话归档** | 默认工作空间下每个对话一个成果子文件夹(`任务_月日_标题/`),相对路径读写、脚本 cwd、图像视频下载全部落在里面,根目录不再越堆越乱;找不到的文件自动回退到工作空间根(读旧成果 / 共享素材无感);自选项目目录保持原地读写不变 | | 🎚️ **Ask / Plan / Goal / Craft** | 只问答 / 只出计划 / 目标驱动 / 完整执行,输入框旁一键切换 | | 🎯 **Goal 目标模式** | 说一个目标,自动拆成可验收的标准清单;每轮跑完由「验收员」逐条核对——不光看汇报,还对成果文件做机器实测(JS 语法 / JSON 解析 / HTML 截断检查),没达成的自动补跑(最多 3 轮),目标卡实时打勾 | | 🎛️ **按对话选模型** | 每个对话可以指定自己的模型(输入框旁模型按钮),互不影响;不选就跟随全局默认;可开启「新对话沿用上次选的模型」 | | 📊 **模型健康账本** | 每次任务按模型记成败(滚动近 20 次),选模型菜单里直接看「近 N 次任务 X 成」,连挂 ≥2 标红——坏渠道一眼看出来 | | 🧪 **智能体评测** | 侧边栏「更多 → 评测」把整个 agent 当黑盒考:15 道分层任务(L1 基础 / L2 进阶陷阱 / L3 高难多约束),每题可重复跑 k 次——**pass@1 均值**看能不能、**k 次全过**看稳不稳(τ-bench 的 pass^k 思路),时过时不过的题标 ⚡;失败自动归成确定性败因码(超时/死循环/没交产物/内容不对…);**AI 评委**逐条质量维度只判 是/否(不打会漂移的印象分);可「📌 设为基线」,之后每轮自动逐题对比、退步点名。设计依据见 [docs/评测方法论.md](docs/评测方法论.md)。命令行同款 `npm run eval -- --repeat 3 --judge Y --save-baseline` | | 🎨 **文本→专业图** | `gen_diagram` 一个工具画四类图:mermaid(流程/时序/甘特)、Graphviz dot(架构图)、ECharts(数据图表)、PlantUML(UML),本机离线渲染出 SVG + 2x 高清 PNG;写文档、做 PPT、发飞书文档的配图全走它,不再让模型手搓 SVG | | 🔁 **长任务自动续跑** | 任务撞到步数/时长上限但还没做完,可按设置的轮数自动重置预算接着跑:靠工作目录的 PROGRESS.md 进度档从断点继续,绝不重做已完成的部分(默认关闭,设置 → 智能体设置开启;手动停止不续跑) | | 🧩 **专家 · 技能 · 连接器** | 三合一广场:召唤专家、装技能、接 MCP 连接器 | | 👥 **专家与专家团** | 12 位内置专家 + 4 个专家团;也可以自己建:头像、职称、说明、绑技能、默认提示词 | | 📦 **技能系统** | 一个 Markdown 文件就是一个技能,**改完下一条任务就生效**,不用重启 | | 🔌 **MCP 连接器** | 标准 Model Context Protocol(stdio / Streamable HTTP),接进来的工具自动注入 agent | | 🧷 **Agent Plugins** | 支持 [Agent Plugins 1.0.0](https://agent-plugins.org) 开放插件标准,一个包同时带技能和 MCP,粘个 GitHub 地址就装 | | 🤝 **助理模式** | 侧栏一个入口进聊天页:直接对话(走本地通道,任务在本机执行),飞书/微信里 @机器人 的对话也流进同一条历史,重启不丢 | | 📱 **IM 远程指挥** | 飞书、QQ、企业微信、微信(iLink 扫码 / 公众号)、钉钉、通用 Webhook | | 📂 **项目** | 每个项目独立工作目录 + 自带指令 + 挂载专家/技能/连接器——这些都会真的进系统提示词,不是摆设 | | 🧠 **双层记忆** | 手写区(全局共享、界面直接编辑)+ 条目区(agent 用 remember 自己记,按账号隔离、可去重可删、超量丢最旧) | | 🎛️ **权限档位** | 只看不动 / 每步都问 / 自动改文件 / 全自动,输入框旁一键切换,参考 Claude Code 的权限设计 | | ⏰ **定时任务** | cron 定时跑(每天 9 点出晨报这种),结果推到 IM,错过了会补跑 | | 🔐 **安全中心** | 命令审批闸门、黑白名单、删文件保护、URL 白名单、审计日志 | | 👤 **账号与用量** | 多用户、按人分任务历史、tokens 用量统计、可选的积分额度 | | 📚 **资料库** | 把参考资料丢进 `data/library/`,界面里可管理;agent 干活时用 `library_list` / `library_read` 自己查,不用每次贴进对话 | | 📚 **参考模板库** | 提示词范例,不知道怎么开口的时候抄一份改 | | 🌐 **本地部署预览** | 做完网页一键起本机服务,相对路径 / fetch / localStorage 才是真的能用;可放开给手机看 | | 🐾 **桌面宠物** | 桌面角落一只透明小挂件,实时显示 agent 在干什么:敲键盘 = 在干活,跳起来 + 系统通知 = **有问题要问你**(主窗口被盖住时最容易漏掉的就是这个),撒花 = 完成,掉汗 = 出错。点它开关主窗口,拖动换位置,右键可「先别烦我」。**默认不存在**——说一句「把这张图做成桌面宠物」并传张照片,它就现场用你的图做一只(照片只存本机)。仅桌面版 `npm run app` | **内置技能**:`deep-research` 深度调研 · `html-page` 网页生成 · `ppt-design` PPT · `docx` Word · `excel-report` Excel 报表 · `data-viz` 数据可视化 · `weekly-report` 周报 · `wechat-article` 公众号推文(排版 + 推草稿箱) · `xhs-cards` 小红书图文卡片 · `video-compose` 图文成片(配音+字幕+ffmpeg 拼装)· `feishu-doc` 飞书文档(官方 convert API:原生表格 / 加粗 / 行内代码 / 插图上传)· `lark-cli` 飞书全家桶命令行 · `skill-creator` 让它自己写技能 **内置工具**:`run_node` `run_shell` `write_file` `edit_file` `read_file` `list_files` `search_files` `fetch_url` `render_page` `check_page` `web_search` `gen_diagram` `feishu_doc_create` `remember` / `forget` `use_skill` `save_skill` `library_list` `library_read` `generate_image` `generate_video` `html_to_image` `text_to_speech` `desktop_pet` --- ## 为什么用它 - **东西都在你自己手里。** 自托管,会话、文件、API Key 全在本机;默认只监听 `127.0.0.1`,不主动往外传任何东西。 - **不被一家模型绑死。** 换服务商就是界面上点一下,配置热生效不用重启。想彻底不花钱、不出网,接 Ollama 跑本地模型。 - **交付的是文件,不是聊天记录。** PPT / Word / Excel / 网页都是真生成,能直接打开验收。声称写了文件却不在磁盘上,会被拦下来重做。 - **扩展成本低到离谱。** 加一个技能 = 写一个 Markdown 文件放进 `skills/`,存盘后下一条任务就生效。不用改代码、不用重启、不用打包。 - **走开放标准。** MCP 连接器 + [Agent Plugins 1.0.0](https://agent-plugins.org),别人做的插件包直接能用,不是自成一派的私有格式。 - **自带一个真浏览器。** 桌面版跑在 Electron 里,抓不到内容的动态页面会用内置 Chrome 真渲染一遍再读,单页应用也读得到。 - **代码是给人读的。** 没有构建步骤、没有前端框架、agent 主循环是手写的——想学 agent 到底怎么转,clone 下来就能顺着读,改完刷新即见效。 - **不怕崩。** 会话、账本、定时任务表都是原子写 + `.bak` 兜底,断电最多丢最后几秒,不会剩半个 JSON 把数据毁掉。 - **手机上也能使唤。** 接飞书 / QQ / 企微 / 微信,在外面下任务,干完推回来。 - **它手里有 shell,所以有闸门。** 命令审批、文件黑名单、网络白名单、审计日志,都是真拦,不是摆着看的开关。 - **测试是真跑的。** `npm test` 用模拟 LLM,不需要 API Key 就能全绿;前端那部分在真 Chromium 里跑。 --- ## 安装启动(从零开始) ### 0. 先准备 | 要什么 | 说明 | |---|---| | **Node.js 18 或更高** | [nodejs.org](https://nodejs.org) 下载即可;macOS 有 Homebrew 的话 `brew install node` | | **git** | 用来拉代码 | | **一个大模型的 API Key** | DeepSeek / 通义 / 智谱 / Kimi / OpenRouter 都行,哪个都只要一个。不想花钱就装 [Ollama](https://ollama.com) 跑本地模型,不用 Key | 检查一下 Node 装好没: ```bash node -v # 要 v18.0.0 以上 ``` ### 1. 拿代码、装依赖 ```bash git clone https://github.com/CatCatUncle/openworkbuddy.git cd openworkbuddy npm install ``` > 国内装 Electron 卡住的话,先设个镜像再 `npm install`: > `export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/` **嫌麻烦?** macOS / Linux 上有一键脚本,上面这些它全包了(查环境 → 装依赖 → 生成配置 → 起服务): ```bash bash install.sh ``` ### 2. 启动 三种跑法,挑一个: ```bash npm run app # 桌面版(Electron 窗口)—— 自己用推荐这个 npm start # 纯服务端,浏览器打开 http://localhost:3800 npm run cli -- "帮我写一份本周周报" # 命令行,适合脚本里调 ``` 桌面版和 `npm start` 的区别只有一层外壳:桌面版多了独立窗口、全局快捷键和内置浏览器渲染,服务端和界面是同一套。 macOS 想双击图标打开(不碰终端)的话,跑一次 `bash scripts/make-mac-app.sh`,会在 `~/Applications` 生成 OpenWorkBuddy.app——它启动的永远是本仓库的最新代码,改完代码重开 App 就生效;重复打开会聚焦已有窗口,不会叠第二个实例。 Windows 也能直接跑:装好 Node.js 后同样 `npm install` + `npm run app` 即可。`run_shell` 会自动走 cmd(macOS/Linux 走 zsh/bash),数据备份用的是系统自带 tar(Windows 10 1803+ 就有),图表/图文卡片渲染走 Electron 内置浏览器,都不用额外装东西。 端口被占了就换一个:`PORT=3801 npm start`(Windows cmd 写法:`set PORT=3801 && npm start`)。 ### 3. 第一次打开,两步就能用 1. **注册第一个账号。** 界面会让你注册——**第一个注册的账号自动是管理员**。注册完之后默认就不许别人自己注册了,别人要用得管理员去开(见[多人一起用](#多人一起用多租户))。 2. **填 API Key。** 登录后会弹引导页,选服务商、粘 Key。它会**当场发一条真实请求验活**,通过了才保存——不会让你等到发第一条消息才发现 Key 是错的。 Key 不用手动写进配置文件。以后想改,去 **设置 → 模型**,保存即生效。 ### 4. 下第一条任务 在输入框里说人话就行,比如「帮我做一份介绍 OpenWorkBuddy 的 PPT」。左边能看到它一步步在干什么,做完的文件在右边「成果文件」里点开预览。 **文件存在哪**:默认是项目目录下的 `workspace/`。想换地方,去 **设置 → 工作目录**,或者在左上角「项目」里建一个新项目并指定目录——每个项目一个目录,切项目就是切目录。 ### 启动时容易卡住的几件事 | 现象 | 原因和解法 | |---|---| | `npm install` 卡在 electron | 设镜像:`export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/` | | `EADDRINUSE` 端口被占 | `PORT=3801 npm start`;桌面版遇到这个会直接连已经在跑的那个实例 | | 发消息报上游错误 | Key 或 base_url 不对,去 设置 → 模型 里改,那里会当场验活 | | 界面一直转圈(服务器部署) | 反代没关缓冲,SSE 流被攒住了。nginx 加 `proxy_buffering off` | | 生成的 Word/PPT 中文变方块 | 缺中文字体(自己精简过 Docker 镜像的话补 `fonts-noto-cjk`) | | 改了技能要不要重启 | 不用。`skills/` 每次任务重读磁盘。改 `config.json` 才要重启(界面里改是热生效的) | --- ## 配置模型 界面里 **设置 → 模型** 直接改,保存即热生效。新增模型时表单顶部有**渠道预设**下拉:选一个(OpenAI / Anthropic / OpenRouter / 火山方舟 / 阿里云百炼 / DeepSeek / 智谱 / Kimi / Ollama),接口地址和协议自动填好,只差粘 Key。手动改 `config.json` 也行: ```jsonc { "models": [ { "name": "DeepSeek", // 界面上显示的名字 "provider": "openai", // openai 兼容 / anthropic "base_url": "https://api.deepseek.com/v1", "api_key": "sk-...", "model": "deepseek-chat" } ], "active_model": "DeepSeek" } ``` | 服务商 | base_url | model 示例 | |---|---|---| | OpenRouter(一个 Key 通吃) | `https://openrouter.ai/api/v1` | `deepseek/deepseek-chat` | | OpenAI | `https://api.openai.com/v1` | `gpt-5.2` | | Anthropic Claude | 留空(官方端点) | `claude-sonnet-5`(`provider` 填 `anthropic`) | | 火山方舟(豆包) | `https://ark.cn-beijing.volces.com/api/v3` | `doubao-seed-1-6-250615` | | DeepSeek | `https://api.deepseek.com/v1` | `deepseek-chat` | | 通义 Qwen | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen-max` | | 智谱 GLM | `https://open.bigmodel.cn/api/paas/v4` | `glm-4-plus` | | Kimi | `https://api.moonshot.cn/v1` | `moonshot-v1-32k` | | Ollama 本地 | `http://localhost:11434/v1` | `qwen3:14b` | 模型条目还能带 `extra_body`,透传厂商私有参数(比如 OpenRouter 的 `reasoning`)。 `config.json` 是唯一存着所有 API Key 的文件,已经在 `.gitignore` 里,**别手滑提交**。 --- ## 多人一起用(多租户) 先说清楚这里的「多租户」是什么,免得期待错位: > **一套程序、一台机器、一份配置,多个账号共用。** 每个人有自己的账号、自己的任务历史、自己的用量账单;但模型 Key、技能、连接器、成果文件目录是**全组共用的一份**。 > 它**不是**"一人一套独立环境"——这个 agent 手里有 `run_shell`,能读写这台机器的文件,应用层挡不住一个成员去读另一个人的产出。真要硬隔离,见本节最后一段。 适合:自己一个人用(默认就是这样,什么都不用配)、家里 / 小团队几个人共用一个 Key、给同事开个号让他也能用。 ### 角色:第一个注册的是管理员 | | 管理员 | 成员 | |---|---|---| | 下任务、用技能连接器、看成果文件 | ✅ | ✅ | | 自己的任务历史、改自己的昵称/头像/登录名/密码 | ✅ | ✅ | | 看用量流水 | 看**全员**的 | 只看**自己**的 | | 开关「允许别人自己注册」 | ✅ | ❌ | | 开关「积分限额」、给人充值 | ✅ | ❌ | 第一个注册的账号自动是 `admin`,之后注册的都是 `member`。角色记在 `data/users.json` 里。 ### 怎么把人加进来 程序里没有"管理员直接建号"的入口,加人是这么两步: 1. 管理员点左下角头像 →「账号 · 用量」→ 勾上 **「允许别人自己注册账号」**; 2. 对方打开地址,自己注册一个(注册入口这时候才出现),完事后管理员**把开关关回去**。 关着的时候登录页连注册入口都不给。**挂在公网上又长期开着注册 = 谁进来都能拿你的 Key 跑任务,还能用 `run_shell` 碰这台机器**,所以用完请关。 ### 各自看到什么、共用什么 **每个人自己的:** - **任务历史**。侧边栏的任务列表按账号分开存(浏览器本地 `wb_sessions:<用户名>`),服务端的会话文件也记着归属。换个账号登录,看到的是他自己的列表。 - **用量账单**。每次任务的 tokens、耗时、模型、来源(网页/CLI/IM/定时任务)都记进 `data/usage.json`,成员在「账号 · 用量」里看自己的今日 / 本月 / 近 7 天 / 最近 50 条。 - **身份**。昵称、头像、登录名、密码,各改各的。 **全组共用的(任何登录用户都能看、能改):** - 模型和 API Key、搜索服务的 Key(`config.json`) - 技能 `skills/`、专家 `experts.json`、MCP 连接器、已装插件 - 安全中心的所有闸门设置、审计日志 - 定时任务表 - **工作目录和成果文件**——所有人的产出落在同一个目录里,互相看得见 > 需要说明白的一点:任务列表是**按人分列表**,不是权限墙。会话回放接口没有做归属校验,知道会话 ID 的人仍然能调出来看。这在"一台机器几个自己人"的场景够用,公开对外则不够。 ### IM 和定时任务算谁的 飞书/QQ/微信那些渠道进来的消息、以及 cron 定时任务,背后没有登录态,**统一记到管理员(第一个账号)名下**。 ### 想给成员定额度:积分限额(默认关着) **一个人本地部署,别开它。** 它拦不住任何真实开销——Key 是你自己的,账单在服务商那边,这本账只是个自己写给自己的数字。开着的唯一效果是:干到一半余额见底,任务被自己的应用掐了,然后你还得进后台给自己充值。 真正用得上的场景只有一个:**多人共用你的一个 Key,你要给成员定额度。** 管理员到「账号 · 用量」勾上「开启积分限额」,才开始按**每 1000 tokens 扣 1 积分**(每次任务至少 1 积分)算,余额 0 时拒跑;管理员可以给任何人充值。开关即时生效,不用重启,网页 / CLI / IM / 定时任务四个入口共用同一本账。 关着的时候整套积分 UI 都不出现——余额、充值、结果下面那行「扣 X 积分」,一个不显示。**用量流水跟这个开关无关,永远照记**:那是给你看花了多少 tokens 的账,不是闸。 ### 名字、头像、登录名都能改 - 头像菜单 →「个人资料」:改**自己**的昵称和头像;设置 →「个性化」:改**助理**的名字和头像。两边都支持 emoji 或上传图片(自动裁方、压到 128px,硬上限 256KB)。 - 助理改名不只是换个界面标题:名字会同时进系统提示词,所以你喊它「小秘」它自己也认。 - **登录名也能改**(个人资料 → 登录名,要输一次当前密码,毕竟改的是身份本身)。挂在旧名字底下的东西会一起搬走:历史会话的归属、用量流水、充值记录里的「谁充的」、还有你手上那个登录令牌——所以改完不会被踢下线,也不会有一堆任务突然没了主。 ### 几条默认就这么定的安全规矩 - **有了第一个账号之后,默认不许别人自己注册。**(上面说过,开了记得关回去) - **登录有闸**:同一个账号连错 8 次、或同一个 IP 连错 30 次,停 15 分钟。只记在内存里,重启就清,这是防连打不是封号。 - **改完密码,别处的会话立刻下线**,只留你当前这一个。密码泄露了才改的密码,旧 cookie 还能用就等于没改。 - 走 https 进来时,会话 cookie 自动带 `Secure`。 - 账本 `data/users.json` 坏了会**直接报错停下**,不会当成「没有用户」——后者的下场是下次写盘把所有账号覆盖成空的,而且下一个注册的人自动变管理员。 ### 真要硬隔离怎么办 **一人一个实例**:各自一份 `data/` 和 `config.json`,用不同端口跑,或者一人一个容器。这是唯一靠谱的隔离方式——因为 agent 有 shell,任何应用层的"权限"在它面前都不算数。 --- ## 扩展它(技能 / 连接器 / 插件) ### 技能:一个 Markdown 文件 `skills/<名称>/skill.md`: ```markdown --- name: my-skill description: 一句话说清什么时候该用它(agent 靠这句判断) --- ## 操作步骤 1. …… ``` 启动时只把 `description` 注入系统提示;agent 觉得用得上,才用 `use_skill` 加载正文——所以技能可以写得很长,不占上下文。同目录下放 `scripts/`、`templates/` 都行,agent 能用到。 **技能是热的**:每次任务重读磁盘,改完存盘,下一条任务就是新的。不想自己写?让它写:内置的 `skill-creator` 技能就是干这个的。 ### MCP 连接器 `config.json` 的 `mcp_servers`,本地进程和远程服务两种都能接: ```json "mcp_servers": [ { "name": "filesystem", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/你的/数据目录"] }, { "name": "notion", "url": "https://mcp.example.com/mcp", "headers": { "Authorization": "Bearer 你的令牌" } } ] ``` - 填 `command` 就是本地 stdio;填 `url` 就是远程 Streamable HTTP,界面上也有对应的两个选项。 - 请求头里多半是令牌,所以 `GET /api/mcp` 只回**头的名字**不回值;在界面上改别的字段存回去时,原来的令牌自动沿用。 - 带请求头又走明文 `http://` 的远程地址会被拒(本机 `localhost` 除外)——令牌不该在路上裸奔。 服务器暴露的工具会以 `mcp__服务器名__工具名` 的形式自动注入。界面 **专家 · 技能 · 连接器 → 连接器** 页也能管。 ### Agent Plugins(开放插件标准) 支持 [**Agent Plugins 1.0.0**](https://agent-plugins.org)——不绑定任何客户端的插件格式。**一个包同时带技能和 MCP 连接器,装一次两样都进来**,在别的支持这个标准的客户端里也能用同一个包。 界面 **专家 · 技能 · 连接器 → 插件** 页,粘一个 GitHub 地址点安装: ``` https://github.com/owner/repo 仓库根就是插件 https://github.com/owner/repo/tree/main/plugins/xxx 仓库里的某个子目录 ``` 装完立刻生效、卸载立刻停,不用重启。也可以直接把目录拷进 `plugins/`,重启即生效。
插件目录长什么样、实现到哪一步(展开) 一个目录,根上放一份 `plugin.json`: ``` my-plugin/ ├── plugin.json 必需,插件清单 ├── skills/ Agent Skills,每个子目录一份 SKILL.md │ └── my-skill/SKILL.md ├── mcp.json MCP 服务器声明 └── com.example.client/ 别家客户端的私有扩展(反向域名命名,我们原样忽略) ``` `plugin.json` 最小可用形态: ```json { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "my-plugin", "version": "1.0.0", "description": "一句话说清它是干什么的", "license": "MIT" } ``` `mcp.json` 和 MCP 官方配置一个样,多了两个可以在 `args` / `env` 值 / `cwd` 里展开的变量: ```json { "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", "mcpServers": { "notes": { "type": "stdio", "command": "node", "args": ["${PLUGIN_ROOT}/bin/server.js", "--data", "${PLUGIN_DATA}"] } } } ``` `${PLUGIN_ROOT}` 是插件装在哪,`${PLUGIN_DATA}` 是给它的可写数据目录——落在 `data/plugin-data/<插件名>`,**卸载插件不会删它**,重装数据还在(规范要求跨升级保留)。 **安装机制**:走稀疏浅克隆(`--depth 1 --filter=blob:none --sparse`),只拉那一个子目录;**先在临时目录验一遍清单,不合规就不落盘**,免得 `plugins/` 里堆一堆装不上的垃圾。卸载是先停进程再删目录(顺序反了就查不出它带过哪些服务器,子进程会一直挂着)。 **更新**:从地址装的插件卡片上有「更新」按钮,按当初那个地址重拉。安装来源记在 `data/plugin-sources.json`(记在插件目录**外面**,不然重装时正好被自己删掉)。版本号没变也照拉——上游经常只改内容不动版本。 **一致性范围**: | | | |---|---| | 规范版本 | 1.0.0(`$schema` 按本地已知常量校验,**加载时不联网取 schema**,规范明令禁止) | | 组件类型 | `skills` + `mcp.json` 两类都实现 | | MCP 传输 | `stdio`、`streamable-http`。`sse`(遗留 HTTP+SSE)规范里是可选项,**没实现**,遇到会跳过那一条并报出来 | | 客户端扩展 | `extensions` 字段和 `com.*/` 目录一律不解读、不校验——那是别家客户端的地盘 | | 技能发现 | 只认 `skills/` 的直接子目录里名字**正好是 `SKILL.md`** 的文件,不递归 | | HTTP 安全 | `redirect: "manual"`——配置里的 `headers` 绝不会跟着跳转发到别的域 | **坏零件不连坐**(规范定的五级失败边界,照着实现):清单不合规 → 整个插件不加载;`mcp.json` 顶层坏了 → 只关掉这个插件的 MCP,技能照常用;某个技能目录坏了 → 只跳过那一个;某条 MCP 条目坏了 → 只跳过那一条;路径想往插件目录外跑 → 直接拒绝。被跳过的零件会在插件卡片上列出来(「⚠️ 有零件被跳过」),不闷声吞掉。 插件带来的技能和连接器在界面上是**只读**的,要去掉就卸载整个插件。
### 专家与专家团 `experts.json` 定义专家——每个专家就是一份独立系统提示的子智能体,和主 Agent 共享工作目录。主 Agent 拿到复杂任务会自己拆(调研 → 分析 → 写作 → 做 PPT),用 `delegate_to_expert` 逐段委派。 **专家团**是打包好的多人协作阵型(调研出报告、汇报三件套、网页交付组、内容发布组),`delegate_to_team` 一次派整团。界面上可以自己建专家:头像、职称、一句话说明、绑哪些技能、默认提示词。 --- ## 手机远程指挥(IM) 在手机上给它下任务,干完推回来。 侧栏的**助理模式**就是这些渠道的总控台:顶部列出已连接的通道,中间是所有渠道汇成一条的对话历史(落盘保存,重启不丢),底部输入框可以直接跟助理对话——和在飞书里 @它 一样,任务在这台电脑上执行。 | 渠道 | 要不要公网 | 说明 | |---|---|---| | 飞书 | 不要 | 长连接,填 `app_id` / `app_secret` 就行;另可**扫码**授权你本人的身份(见下) | | QQ | 不要 | 官方机器人 WebSocket 长连接 | | 微信 iLink | 不要 | 扫码登录 + 长轮询 | | 企业微信自建应用 | 要 | 回调地址得能被访问到 | | 微信公众号 | 要 | 同上 | | 企业微信群机器人 / 钉钉 | 不要 | 只推结果,不收指令 | | 通用 Webhook | 看你怎么接 | 桥接任何东西 | 通用 Webhook 长这样,同步返回 `{ reply, files }`,iOS 快捷指令、自动化平台、你自己的脚本都能接: ```bash curl -X POST http://localhost:3800/im/task \ -H "Content-Type: application/json" \ -d '{"message": "帮我生成本周周报", "secret": "你配的密钥"}' ``` **飞书扫码授权**:设置 → 助理设置 → 飞书 → 扫码授权,用飞书 App 扫一下,AI 就能以**你本人**的身份读日历、翻云文档、查邮件、写多维表格(走飞书官方设备码流程,密码不经过 OpenWorkBuddy)。依赖本机的 [lark-cli](https://github.com/larksuite/cli)(MIT):`npx @larksuite/cli@latest install`。 > 免得误会:机器人**收消息**必须有应用的 `app_id` + `app_secret`,这是飞书的设计,扫码替代不了。扫码解决的是另一半——用户身份的 API 调用。两者互不冲突,可以只配一个。 --- ## 定时任务 界面的**自动化**页管这一摊:定时任务、运行记录、批量开关/删除、从模板一键添加。加任务在 **自动化 → 定时任务**,或者调 API: ```bash curl -X POST http://localhost:3800/api/schedules \ -H "Content-Type: application/json" \ -d '{"name": "每日晨报", "cron": "0 9 * * 1-5", "task": "抓今天的 AI 行业新闻,生成晨报 markdown"}' ``` cron 5 字段(分 时 日 月 周),支持 `*` `,` `-` `*/n` `1-30/5` `5/10`;周里 `0` 和 `7` 都是周日。日和周同时写了具体值时按标准 cron 取**或**(`0 9 1 * 1` = 每月 1 号或每周一)。越界(`70 * * * *`)、写反(`5-1`)、步长 0 一律当场报错——收下不报的后果是任务永不触发,界面上却一切正常。 **错过了会补跑**(每个任务可关)。这是台式应用,合上盖子睡一夜、或者应用压根没开着,到点那次就没人执行。下次醒来会把该跑没跑的补一次: - 一段时间里错过好几次也**只补一次**,补的是最近该跑的那次——补的是「这件事还没做」,不是把闹钟按次数重放 - 最多往回补 24 小时,更早的按过期丢掉(关机一个月,不该开机就把一个月的晨报全补一遍) - 装好后第一次启动不补跑,否则一上来就会把历史全部重放 - 同一个任务不叠着跑:上一次还没跑完,这次就跳过 --- ## 安全(这个 agent 手里有 shell) 先选档位(输入框旁一键切,参考 Claude Code):**只看不动**(只读,不写文件不跑命令)→ **每步都问** → **自动改文件**(默认:工作目录随便改,删除、sudo 这类命令照样问)→ **全自动**(只剩文件黑名单和审计兜底)。 设置 → 安全中心。每一项都是真闸门:文件黑名单、命令审批、网络白名单、运行时开关、审计日志(`data/audit.json`,环形 1000 条)。审批弹在输入框上方,批准 / 拒绝都行;超时(默认 120 秒)或者点停止都按拒绝算。 命令闸拆命令的时候,下面这些都算**同一条命令里的一段**,会逐段核对: ```bash rm -rf ~/x # 直接写 echo hi; rm -rf ~/x # ; && || | & 串起来 echo hi rm -rf ~/x # 换行——agent 写的多行脚本 echo $(rm -rf ~/x) # 命令替换 echo `rm -rf ~/x` # 反引号 ( rm -rf ~/x ) # 子 shell FOO=1 rm -rf ~/x # 前面挂环境变量 /bin/rm -rf ~/x # 写全路径 nohup rm -rf ~/x # 套个壳 find . -name "*.log" -delete ``` 引号里的分隔符不当分隔符(`grep "a|b"` 不会被拆开),`echo "记得 rm 掉旧文件"` 也不会误弹审批。 两条跟直觉不太一样、但故意这么定的: - **文件黑名单排在命令放行名单前面。** 黑名单是「永远拦」——不能因为你放行了 `cat `,`cat ~/.ssh/id_rsa` 就跟着过去。拦得住的是「顺手」,不是「刻意绕」——**别把这当沙箱**。 - **`run_node` 的代码也过闸。** 只守 `run_shell` 那扇门是守不住的,一句 `require("child_process").execSync(...)` 就从旁边过去了。 --- ## 部署到服务器 ```bash git clone https://github.com/CatCatUncle/openworkbuddy.git openworkbuddy && cd openworkbuddy cp config.example.json config.json # compose 是按文件挂的,必须先有 mkdir -p data workspace docker compose up -d docker compose logs -f openworkbuddy # 看它起来没 ``` 默认只把端口绑在 `127.0.0.1:3800`,外面自己套 nginx / Caddy 反代 + HTTPS。不用 Docker 的话可以 PM2:`HOST=127.0.0.1 PORT=3800 pm2 start server.js --name openworkbuddy`。 反代配置样例、PM2 细节、**以及安全须知**都在 [deploy/README.md](deploy/README.md)。 > ⚠️ 这个 agent 手里有 `run_shell`。**把它挂到公网 = 把这台机器的 shell 挂到公网。** > 起来之后**第一件事就是注册管理员账号**(空库对外挂着,等于谁先访问谁是管理员), > 然后关掉开放注册、打开安全中心的命令审批、用低权限账号跑。别裸奔。 --- ## 参与贡献 欢迎提 issue 和 PR。这个项目没有构建步骤、没有前端框架,上手成本很低。 ### 开发环境 ```bash git clone https://github.com/CatCatUncle/openworkbuddy.git cd openworkbuddy npm install npm test # 全套测试,用模拟 LLM,不需要 API Key npm start # 改前端就直接刷新浏览器;改后端重启这条命令 ``` 前端就是一个手写的 `public/index.html`,改完刷新即可,没有热更新也不需要打包。 ### 提 issue 说清这几件事 用的哪个模型服务商和模型名、复现步骤、期望什么实际什么、终端里的报错原文。**贴日志前先自己扫一眼有没有 API Key、令牌、内网地址**,别把这些贴上来。 ### 代码约定 - **CommonJS**(`require`,不是 `import`),Node 18+ 能直接跑,不引入编译步骤。 - **不加构建工具、不引前端框架。** 这是这个项目的产品决定,不是还没来得及做——它让任何人 clone 下来就能改。 - **注释写「为什么」,不写「是什么」。** 代码本身说得清做了什么,值钱的是当初为什么这么选、绕开了什么坑。中文注释。 - **新功能要带测试。** 后端加到 `test/e2e.js`(模拟 LLM,不需要 Key);纯前端的行为加到 `test/frontend.js`(在真 Chromium 里跑)。 - **动到落盘的数据就走 `store.js`**,别自己 `fs.writeFileSync` 一个 JSON——原子写和 `.bak` 兜底都在那儿。 - 提交信息用中文,一句话说清这次改了什么、解决了什么问题。 ### 千万别提交这些 `config.json`(存着所有 API Key)、`data/`(账号、会话、用量)、`workspace/`(成果文件)、`node_modules/`、任何 `.log`。这些 `.gitignore` 已经挡了,但**提交前自己再 `git diff --cached` 扫一眼有没有 Key 和令牌**。真提交上去了,光删一次提交没用,历史里还在。 ### 流程 1. Fork → 建分支(`feat/xxx` 或 `fix/xxx`) 2. 改代码 + 补测试 → `npm test` 全绿 3. 开 PR,说清**改了什么、为什么这么改**;改了界面的话附张截图 ### 好上手的方向 | 方向 | 难度 | |---|---| | **写一个技能** —— 一个 Markdown 文件放进 `skills/`,不用碰任何代码 | ⭐ | | **补一个模型服务商预设** —— `config.example.json` 和 README 的表里加一行 | ⭐ | | **改文档 / 纠错别字** | ⭐ | | **加一个内置专家** —— `experts.json` 里加一份系统提示 | ⭐⭐ | | **接一个新的 IM 渠道** —— 照着 `im-qq.js` / `im-wechat.js` 的样子写 | ⭐⭐⭐ | | **加一个内置工具** —— `tools.js` 里加,记得过安全闸 | ⭐⭐⭐ | > 贡献的代码同样按 [PolyForm Noncommercial 1.0.0](LICENSE) 发布。 --- ## 项目结构 ``` server.js Web API + SSE + 各种端点 agent.js Agent 运行时(协调者/专家循环、工具路由、系统提示) llm.js LLM 适配层(OpenAI 兼容 + Anthropic) tools.js 内置工具 skills.js 技能加载器 skills/ 技能包 plugins.js Agent Plugins 1.0.0 plugins/ 已装插件 mcp.js MCP 客户端(stdio / Streamable HTTP) account.js 账号 / 鉴权 / 用量 / 积分 security.js 安全中心(审批闸门、黑白名单、审计) store.js JSON 落盘(原子写 + .bak 兜底) im-store.js IM 会话仓库 experts.json 专家与专家团定义 im.js IM 总线 im-qq.js / im-wechat.js / im-ilink.js scheduler.js 定时任务 electron-main.js 桌面壳 cli.js 命令行 pet.js 桌面宠物窗口(透明置顶挂件) pet-preload.js / public/pet.html public/ 前端(单文件,没有构建步骤) workspace/ 成果文件输出 data/ 账号与会话 ``` --- ## 测试 ```bash npm test # 端到端,用模拟 LLM,不需要 API Key ```
覆盖了哪些(展开) cron 解析(越界 / 步长 0 / 日周取或)、定时任务运行时(补跑一次 / 不叠跑 / 结果落盘)、 命令闸(换行 / `$()` / 反引号 / 子 shell / 包装词 全拆得开、黑名单压得住放行名单、代码闸)、 账本(坏文件不被空账本覆盖 / 写盘原子 / 登录限流 / https 认得出)、 积分限额(默认关着:余额 0 也照跑、不扣分但流水照记 / 开了才扣才拦 / 开关即时生效,全程在临时目录里跑不碰真账号)、 改登录名(撞名与不合法挡得住 / 账本、登录令牌、用量流水连同充值记录的「谁充的」一起搬走)、 头像规则(emoji 按字素簇算长度 / 只收 `data:image` / 挡外链与标签 / 限 256KB)、 JSON 落盘(空文件自愈 / 坏文件回退 `.bak` / 无 `.bak` 则隔离 / 账本 strict 抛错)、 IM 会话(重启后上下文还在 / 砍历史只从整轮开头下刀)、 workspace 路径越界拦截、成果核验闸门(缺文件 / 0 字节空壳)、 `run_node` 语法预检、上下文预算截断、Word/PPT/Excel 生成、 Agent Plugins(清单校验 / 坏零件隔离 / `${PLUGIN_ROOT}` 展开 / 技能并流与重名让位)、 MCP Streamable HTTP(JSON 与 SSE 两种响应、会话 ID)、MCP 连接器生命周期(按插件停、同名重启不留孤儿)、 Agent 全管线(技能加载 → 代码执行 → 专家委派 → 事件流)、 强制收尾(撞上限补一次交代 / 手动停止不多花一次调用)、 工具调用泄漏救援(特殊 token 还原成真调用 / 半截参数丢弃 / 不漏进界面)、 抓取(JSON 原样返回 / 导航页脚清掉 / GBK 按真实字符集解码 / PDF 存成文件不塞乱码且重名不覆盖 / 空壳与反爬如实报告并给出下一步)、 只读工具并发(真并发跑起来 / 结果顺序与调用 ID 不串 / 混入写操作整批退回串行)、 来源收录(只记真访问到的页面,抓失败/本地文件/非联网工具一律不计)、 前端内联 SVG 信息图(在 Electron 的真 Chromium 里跑:流式逐帧渲染 / 脚本与外链清洗 / `