内容
<div align="center">
<img src="./assets/banner.svg" alt="writ — 记录一次网页任务,永久重复执行,在您拥有的硬件上" width="100%">
<br/>
<p align="center">
<a href="https://github.com/usewrit/writ/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/usewrit/writ/ci.yml?branch=main&style=flat-square&label=CI" alt="CI"></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0-FF4A24?style=flat-square" alt="许可证:AGPL-3.0"></a>
<img src="https://img.shields.io/badge/deploy-docker%20compose%20up-2496ED?style=flat-square&logo=docker&logoColor=white" alt="使用docker compose up部署">
<img src="https://img.shields.io/badge/database-SQLite-003B57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLite">
<img src="https://img.shields.io/badge/MCP-ready-FF4A24?style=flat-square" alt="MCP 就绪">
<img src="https://img.shields.io/badge/self--hosted-yes-16a34a?style=flat-square" alt="自托管">
<img src="https://img.shields.io/badge/PRs-welcome-16a34a?style=flat-square" alt="欢迎PR">
</p>
<h3 align="center">记录一次网页任务。永久重复执行 — 免费每次运行,在您拥有的硬件上。</h3>
<p align="center">将任何网站转换为工作流、REST API 或 MCP 工具。</p>
<p align="center">
<a href="#quickstart"><b>快速开始</b></a> ·
<a href="#what-you-can-do"><b>功能</b></a> ·
<a href="#connect-your-ai-assistant-mcp"><b>连接您的AI</b></a> ·
<a href="#agents-in-more-detail"><b>代理</b></a> ·
<a href="./docs/DEPLOYMENT.md"><b>部署</b></a> ·
<a href="./SECURITY.md"><b>安全</b></a>
</p>
</div>
---
**Writ** 记录一次可重复的浏览器任务,并永久重复执行 — 无需重新抓取,无需脆弱的脚本。这是**开源、自托管的协调器**:`docker compose up` 它提供Web UI和API,将所有内容存储在一个SQLite文件中,读取PDF和扫描页面,并将所有浏览器工作分派给轻量级的`writ-agent`进程,您可以在任何地方运行它们。您的机器,您的数据,您的代理。
> Rust `writ-agent` 位于**其自己的仓库**([`writ-agent`](https://github.com/usewrit/writ-agent))— 单独安装并指向此协调器。该仓库仅是协调器(Python API + 构建的Web UI);它永远不会自行启动浏览器。
<div align="center">
<img src="./assets/media/record-steps.gif" alt="记录工作流:一个实时浏览器位于向导的第2步中,每次点击都捕获为可编辑的步骤" width="100%">
<br/>
<sub><b>记录一次。</b> 驱动一个真正的浏览器,每次操作都作为可编辑的步骤落地 — 然后永久重复执行,零AI代币成本。</sub>
</div>
## 快速开始
您只需要Docker。无需其他。
```bash
git clone https://github.com/usewrit/writ.git && cd writ
./scripts/gen-env.sh
docker compose up -d --build
```
打开 **http://localhost:8000** 并创建您的帐户。安装完成。
> 第一次构建需要几分钟 — OCR运行时及其模型权重已内置,因此文档提取功能离线工作。
### 然后连接一个代理
协调器本身不运行浏览器,因此**直到连接一个代理**,什么都不会执行。在应用程序中打开 **舰队 → 连接新代理**:它会打印一个带有令牌、协调器URL和文档提取器设置的命令,并为您填充。 在任何机器上运行它 — 同一台机器也没问题:
```bash
WRIT_SERVICE_TOKEN=<from the app> writ-agent-fleet
```
代理通过WebSocket连接,因此它不需要入站端口,可以位于NAT后面。它在**舰队**中立即出现,您可以记录和运行。
从 [`writ-agent`](https://github.com/usewrit/writ-agent) 获取二进制文件 — [发布](https://github.com/usewrit/writ-agent/releases),`ghcr.io/usewrit/writ-agent:latest` 镜像,或从源代码构建。[docs/CONNECT_AGENT.md](docs/CONNECT_AGENT.md) 有完整的演练。
### 日常
```bash
docker compose logs -f # 跟随日志
docker compose restart # 重启
docker compose down # 停止,保持数据
docker compose down -v # 停止并删除所有内容
```
从仓库根目录运行这些命令 — 根目录 `compose.yaml` 连接 `docker/docker-compose.yml` 并自动加载您的 `.env`。
<details>
<summary><b>生成密钥</b></summary>
<br/>
`./scripts/gen-env.sh` 为您填充所有这些内容。要手动执行,请在 `.env` 中填写这些内容。永远不要提交已填写的 `.env`。
| 变量 | 如何生成 |
| --- | --- |
| `WRIT_JWT_SECRET` | `openssl rand -hex 32` |
| `API_SECRET_KEY` | `openssl rand -hex 32` |
| `HMAC_SECRET_KEY` | `openssl rand -hex 32` |
| `RECORDER_AUTH_SECRET` | `openssl rand -hex 32` |
| `INTERNAL_API_SECRET` | `openssl rand -hex 32` |
| `GATEWAY_SECRET` | `openssl rand -hex 32` |
| `DOC_EXTRACT_SECRET` | `openssl rand -hex 32` |
| `SECRET_ENCRYPTION_KEY` | `python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"` |
> 每个都是**必需**的 — 默认情况下,`ENVIRONMENT=production`协调器拒绝启动,如果任何一个缺失、为空或长度小于32个字符。 这是故意的:HS256可以随意使用空密钥,因此不完整的模板将使签名密钥公开已知。
> **备份 `SECRET_ENCRYPTION_KEY` 独立于数据卷。** 它加密存储的秘密和凭证。如果您丢失它,这些值将无法恢复 — 请参阅 [SECURITY.md](./SECURITY.md)。
`WRIT_JWT_SECRET` 是操作员面向的名称; compose文件将其映射到应用程序读取的 `JWT_SECRET_KEY`。设置一个,而不是两个。
</details>
## 为什么自托管
- **您拥有所有内容。** 一个SQLite文件,一个卷。没有托管云,没有第三方数据处理器,没有遥测电话回家 — 这里没有任何内容会发出您未请求的出站调用。
- **协调器中没有浏览器。** 它永远不会启动Chromium — 您的 `writ-agent`舰队执行浏览,无论您在哪里运行它。 该镜像只是Python + UI。
- **包含PDF和扫描件。** 文档/OCR提取器已内置并为您配置好,因此爬取可以读取PDF,Word/Excel/PowerPoint文件和扫描页面,而不是跳过它们。 CPU-only且完全离线 — 它在隔离环境中工作。
- **无需外部数据库或缓存服务。** 磁盘上的SQLite加上进程内队列。 备份一个文件。
- **MCP原生。** 将Claude Code,Claude Desktop,Cursor或任何MCP客户端指向您的协调器,您的保存的工作流成为它可以运行、读取、调度、公开的工具 — 甚至*构建*,通过驱动实时录制会话。
## 您能做什么
**捕获任务一次**
| | |
| --- | --- |
| 🎬 **记录** | 在真正的浏览器中点击任务 — 登录、表单、导航、提取 — 并将其保存为可重复执行的工作流。 |
| 🧩 **编辑为步骤** | 每个记录都是一个可编辑的步骤列表,而不是不透明的blob。 修复选择器、添加等待、根据条件分支。 |
| 🔐 **角色** | 将站点的登录存储为角色:凭证和TOTP种子用Fernet加密存储,并在需要它的每个工作流中重用热cookie/localStorage会话。 处理2FA(TOTP、电子邮件OTP、SMS)。 |
**然后在您的条款下运行它**
| | |
| --- | --- |
| ▶️ **运行** | 在您的舰队上重复播放并获取结构化数据 — 本地,零AI代币成本。 记录使用AI一次;重复播放永远不会。 |
| 🗓️ **调度** | 间隔,或每日/每周时钟时间。 |
| 👀 **监控** | 监视页面或CSS选择器更改。 保持在线时间,SSL过期和更改历史,并可以在更改发生时触发工作流。 |
| 🔀 **自动化** | 链接事件到操作:当工作流完成或监视器触发时,运行另一个,POST Webhook,或发送通知。 |
| 🕸️ **爬取** | 将分布式爬取指向整个站点,在您的代理舰队上分片。 |
| 📄 **读取文档** | PDF,Word/Excel/PowerPoint,图像和扫描页面 — 在您自己的CPU上解析和OCR,完全离线。 |
**然后将其交给任何消耗它的东西**
| | |
| --- | --- |
| 🔌 **REST端点** | 将任何工作流发布为具有自己的API密钥和范围的HTTP端点。 |
| 🤖 **MCP工具** | 您保存的工作流成为25种工具,AI助手可以运行、读取、调度、公开 — 甚至*构建*,通过驱动实时录制会话。 |
| 💬 **OpenAI兼容** | 将工作流作为 `/v1/chat/completions`、`/v1/models` 和 `/v1/responses` 提供,因此任何OpenAI SDK都可以更改base-URL指向它。 |
| 🗃️ **数据集** | 每次运行都会追加到可搜索的数据集。 查询整个历史记录,或导出它。 |
## 它是什么样子
<table>
<tr>
<td width="50%"><img src="./assets/media/monitors.png" alt="监视器:三个实时检查,倒计时到下一次运行,监视选择器和更改历史记录"><br/><sub><b>监视器</b> — 监视页面或选择器。 实时记录和更改历史记录,下一次检查倒计时。</sub></td>
<td width="50%"><img src="./assets/media/connect-mcp.png" alt="连接页面:复制粘贴MCP设置,用于Claude Code,Claude Desktop和Cursor,列出助手接收的工具"><br/><sub><b>连接您的AI</b> — 粘贴一个命令,您的工单成为工具。 页面为您生成预填充的片段。</sub></td>
</tr>
<tr>
<td width="50%"><img src="./assets/media/workflows.png" alt="工作流列表,带有计划、步骤计数和运行状态"><br/><sub><b>工作流</b> — 每个记录都是可编辑的步骤列表,旁边是其计划和运行历史记录。</sub></td>
<td width="50%"><img src="./assets/media/fleet.png" alt="舰队页面显示连接的代理及其空闲容量"><br/><sub><b>舰队</b> — 连接任意数量的代理,无论您在哪里运行它们。 连接命令为您生成。</sub></td>
</tr>
</table>
## 为什么是这个而不是爬取API或AI代理
有三种方法可以实现“从Web获取此数据”。它们的区别在于智能的位置,这决定了其他一切。
| | 主机爬取API | AI浏览器代理 | **Writ** |
| --- | --- | --- | --- |
| **何时使用AI** | 从不 — 您编写选择器 | **每次运行**,每次重新推理页面 | **一次**,在记录时 |
| **每次运行的成本** | 每页/信用,永久 | LLM令牌每次运行 — 随着运行频率扩展 | **零。** 重复执行是确定性执行 |
| **相同的结果两次?** | 是的,直到markup更改 | 不保证 — 每次都是一个新的决定 | 是的 — 它重放您记录的步骤 |
| **您的位置数据存储位置 | 他们的云 | 他们的云,此外还有模型提供商 | **您的磁盘。** 一个SQLite文件 |
| **登录站点** | 通常超出范围 | 脆弱 — 凭证交给第三方 | 一流:加密登录,TOTP/OTP,热会话 |
| **哪些IP浏览** | 他们的 | 他们的 | **您的代理运行位置。** 代理运行在您放置的位置 |
| **运行离线** | 否 | 否 | 是的,包括OCR |
成本线是化合物。推理整个流程的代理每次运行都会消耗令牌,而Writ **一次**花费AI — 而您记录时,之后的每次重复执行都是您自己的机器执行保存的步骤列表。 每天的监视器运行成本为零。
确定性线是3点钟把你吵醒的那条线。每次重新决定页面意味着运行可以**不同**于昨天。 记录的工作流执行的操作与之前相同,并且当站点确实更改时,它会大声提示并指向更改的步骤,而不是即兴发挥。
而且它是AGPL-3.0 **没有功能门控**:这里的所有内容都在此存储库中。 这里没有任何内容是锁定试用层 — 阅读它,fork它,运行它。
## 它如何组合
```mermaid
flowchart LR
B[您的浏览器] -->|HTTPS| C["协调器<br/>FastAPI + SQLite<br/>提供Web UI"]
C -->|WebSocket分派| A1[writ-agent-fleet]
C -->|WebSocket分派| A2[writ-agent-fleet]
A1 -->|真正的浏览器| T[目标网站]
A2 -->|真正的浏览器| T
A1 -->|PDF,扫描件,办公文档| D["文档提取<br/>pdfplumber + PDFium + RapidOCR"]
A2 --> D
A1 --> D
`docker compose up` 启动两个容器:**协调器**,它持有数据并提供UI,以及 **doc-extract**,它读取爬取到达的任何非HTML文件。然后任意数量的 `writ-agent-fleet`工作流, 您可以在任何地方运行它们。
注意箭头:代理直接调用doc-extract直接获取字节 — 协调器永远不会触摸它。 协调器还为每个代理提供地址,因此您永远不会自行配置。
## 连接您的AI助手(MCP)
您的协调器支持 **模型上下文协议**。附加AI客户端后,您的保存的工作流成为它可以运行、读取、调度、公开 — 甚至*构建*,通过记录。
**1. 创建API密钥** 在应用程序中 **开发者 → API密钥**。
**2. 将连接器添加到您的客户端。** 捆绑的分派器是`writ-mcp`
桥接器是一个零依赖的stdio↔HTTP代理(它也存在于仓库中,因此不需要安装)。应用程序的“连接”页面生成这些片段,预填充您的端点。
<details open>
<summary><b>Claude Code</b></summary>
```bash
claude mcp add writ-selfhost -e WRIT_API_KEY=<您的API密钥> -- npx -y writ-mcp --url http://localhost:8000
```
</details>
<details>
<summary><b>Claude Desktop / Cursor(配置文件)</b></summary>
```json
{
"mcpServers": {
"writ-selfhost": {
"command": "npx",
"args": ["-y", "writ-mcp", "--url", "http://localhost:8000"],
"env": { "WRIT_API_KEY": "<您的API密钥>" }
}
}
}
```
</details>
<details>
<summary><b>任何Streamable-HTTP客户端(无Node)</b></summary>
```json
{
"mcpServers": {
"writ-selfhost": {
"type": "http",
"url": "http://localhost:8000/mcp",
"headers": { "Authorization": "Bearer <您的API密钥>" }
}
}
}
```
</details>
> 它故意注册为`writ-selfhost`,以便与Writ桌面应用程序共存(注册为`writ`)。服务器将自己标识为*“Writ Self-Host Coordinator”* — 永远不会与桌面应用程序混淆。
**您的助手获得的工具** — 25种工具,以及每个保存的工作流的`run_<name>`工具:
| 家庭 | 工具 |
| --- | --- |
| **操作** | `writ_list_workflows` · `writ_run_workflow` · `writ_workflow_data` · `writ_search_data` · `writ_export_data` · `writ_workflow_runs` |
| **自动化** | `writ_set_schedule` · `writ_expose_workflow_api` · `writ_create_automation` · `writ_create_monitor` · `writ_wire_monitor` |
| **爬取** | `writ_crawl_site` · `writ_crawl_status` |
| **构建**(无引导) | `writ_record_start` · `writ_record_act` · `writ_record_context` · `writ_record_network` · `writ_record_save` · `writ_record_cancel` · `writ_record_network` |
| **驱动浏览器** | `writ_browser_use` · `writ_browser_act` · `writ_browser_context` · `writ_browser_network` · `writ_browser_save` · `writ_browser_cancel` |
构建是**无引导**的:AI驱动实时录制会话,您的舰队为您提供澄清。
> **赋予密钥`mcp:execute`范围。** API密钥具有作用域,MCP端点专门检查该范围 — 如果没有,则每个调用都会返回`API密钥缺少`mcp:execute`范围`。添加您希望AI助手拥有的资源范围(`workflows:read`,`workflows:execute`,`runs:read`,`datasets:read`是一个合理的起点)。
## 代理,更多细节
上面的快速入门涵盖了常见情况。一些值得注意的事情:一旦您拥有多个代理:
- **运行任意数量。** 每个代理都通过WebSocket连接 — 无需入站端口,NAT也没问题 — 协调器将工作分散到有能力的代理中。
- **位于协调器的机器?** **舰队 → 在此主机上运行代理** 为您完成下载、配置和启动。
- **其他机器上的代理** 无法访问文档提取器的默认环回地址。 在 `.env` 中设置 `DOC_EXTRACT_URL` 为可路由地址,每个生成的连接命令都会获取它 — 请参阅 [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)。
- **从源代码构建:**
`cargo build --release --no-default-features --features local,fleet,openai --bin writ-agent-fleet`
[docs/CONNECT_AGENT.md](docs/CONNECT_AGENT.md) 是完整的参考资料 — 安装路径,每个环境变量,健康检查和故障排除。
<details>
<summary><b>生产说明</b></summary>
<br/>
默认配置针对快速本地试验进行了调整 — compose文件仅发布环回端口(`127.0.0.1:8000`)。 在将此公开到互联网之前(完整的指南: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)):
1. **在前面添加TLS反向代理** (nginx,Caddy,Traefik,...)。 在那里终止HTTPS并转发到`127.0.0.1:8000`。 **不要**在公共接口上发布`8000`端口。
2. **将`WRIT_PUBLIC_URL`设置为您的公共HTTPS URL** (例如`https://writ.example.com`)。 代理使用它来回拨,因此它必须可以从代理运行的位置访问。
3. **切换到`ENVIRONMENT=production`**。 强制执行强密钥,主机允许列表,并拒绝通配符CORS策略。
4. **设置`ALLOWED_HOSTS`** 为协调器响应的主机名(逗号分隔,无方案/端口)。 生产环境中拒绝伪造的`Host`头。
5. **设置`CORS_ORIGINS`** 为您的显式https源 — 生产环境中拒绝`*`。
6. **仅信任您的代理的转发IP**,以便每个IP的速率限制看到真正的客户端IP。
因为您的代理终止TLS,所以容器在内部网络上保持监听纯HTTP的`:8000` — 这是预期的。
</details>
<details>
<summary><b>忘记管理员密码?</b></summary>
<br/>
没有电子邮件重置(自托管没有必需的邮件服务器)。 因为协调器是单所有者,所以拥有服务器访问权限的人可以直接重置密码:
```bash
docker compose exec coordinator bash
python manage.py changepassword <username>
```
compose -f docker.yml exec coordinator python reset_passworddocker compose -f docker/docker exec coordinator python reset_password.py --password 'YourNewPass1'
# 本地安装(运行 run-local.sh reset-admin-password.sh
bash-passwordpassword 'YourNewPass1'
bash reset-admin-password.sh --list
</details>
## 包含内容
| 路径 | 描述 |
| --- | --- |
| `coordinator/` | Python API + Alembic 数据库迁移(运行时)。 |
| `ui/` | 构建的 Web UI(单页应用),由 coordinator 提供服务。 |
| `doc-extract/` | 文档 + OCR 提取服务(PDF、办公文件、扫描件)。 |
| `connectors/writ-mcp/` | 零依赖 Node MCP 连接器(stdio↔HTTP 桥接器)。 |
| `docker/` | `Dockerfile.coordinator`、`Dockerfile.doc-extract`、`docker-compose.yml`、`entrypoint.sh`。 |
| `docs/` | 操作指南(代理连接指南、生产环境部署)。 |
| `scripts/gen-env.sh` | 生成一个包含新密钥的 `.env` 文件。 |
健康检查端点是 coordinator (`:8000`) 和 doc-extract (`:8092`) 上的 `GET /health` — 均为 JSON, `HEALTHCHECK` 和 compose 健康检查使用。
要在没有文档运行,只需启动 `docker compose up -d coordinator`,并env` 中设置 `DOC_URL=`(为空)。这样,爬虫在遇到非 HTML 资源时将跳过 — 静默无操作,绝不会报错。
## 社区与支持
- **操作文档**此仓库中,以确保文档与您运行的版本保持/DEPLOYMENT.md/DEPLOYMENT 介绍了生产环境中启用方法,[`docs/CON.md`](./docs/CONNECT_AGENT.md) 提供了代理连接的完整指南(安装路径、环境变量参考、健康检查、故障排除),[`doc-extract/README.md`](./doc-extract/README.md) 介绍了提取 README 涵盖其他内容。
- **错误和功能 — 请在 [GitHubissues) 中提交;已。
- **问题、设置帮助、展示与分享** — 请Hub Discussions](../../discussions)。
- **安全问题** — 请报告;参见 [SECURITY.md](./SECURITY.md)。
## 欢迎提交问题和拉取请求。请先阅读 [CONTRIBUTINGCONTRIBUTING.md) 和准则](./CODE_OF_CONDUCT并根据 [SECURITY.mdCURITY.md) 私下报告。
## 许可证
版权 © 2026 Writ 项目作者。
根据 **GNU Affero 通许可证,版本 3**PL-3.0-only`) 授权 — 参见 [`LICENSE`](./LICENSE)。
| 路径 | 许可证 |
| --- | --- |
| 以下行除外 | **AGPL-3.0-only** ([`LICENSE`](./LICENSE)) |
| `connectors/writ-mcp/`** ([`LICENSE`](./connectors/writ-mcp/LICENSE 它在您的 MCP 客户端,因此必须可以在任何地方嵌入 捆绑字体SIL OFL 1. ([`THIRD_PARTY`](./THIRD_PART.md)) |
| Rust `writ-agent` | 其独立独立许可证 — 不在此发 |
**AGPL-3.0 §13 — 网络源代码提供。** 这是一项 *网络* 共同许可:如果您修改 coordinator 并允许其他人通过网络与之交互,则必须提供您修改版本的完整对应源代码。应用程序为您提供了此服务 — 它提供公共 `GET /api/about` 并从登录屏幕和 **设置 → 通用** 中链接了修补程序构建,请设置
```bashIT_SOURCE_URL=https://githubyou/your-fork这样,链接指向您的源代码游代码。捆第三方材料 — Inter 和 Sch Grotesk 字体,均在 SIL 开放字体许可证 1 下 — 在 [`THY_NOTICES.md`](./_PARTY_NOTICES.md予以注明。Writ 商标、字形
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器