# TaskPilot TaskPilot 是一个面向 Windows 的本地定时任务管理平台。项目目标是用 React 提供任务管理界面,用 Rust 实现 Cron 调度、Windows 进程管理和运行日志。 当前已完成 Phase 1 至 Phase 6:React 任务管理界面、Rust API、SQLite 持久化、Cron 调度、三类任务执行器、实时日志、安全边界、生产静态托管和 Windows 本地打包均已落地。 仓库根目录的 `task_pilot.html` 是最初的单文件视觉原型,目前只作为 React 迁移期间的设计参考,不是正式前端入口。 React 页面沿用该原型作为视觉基线:1040px 主容器、紧凑指标卡、任务表格、660px 编辑弹窗、Cron 预览区和深色日志查看器均在保留真实功能的前提下复刻。 ## 当前能力 - React 页面可以检测 Rust 后端连接状态。 - Vite 开发服务器将 `/api` 代理到 Axum 后端。 - Rust 服务提供健康检查和 OpenAPI 文档。 - OpenAPI 可以导出为稳定快照,并生成前端 TypeScript 类型。 - Cron POC 支持 5/6 位表达式、秒级调度、别名、月份/星期名称和 POSIX 日/星期语义。 - 任务可以持久化到 SQLite,并支持创建、查询、更新和删除。 - 任务列表支持名称/ID 搜索、启用状态筛选、Action 类型筛选和分页。 - 脚本、HTTP 和二进制进程任务具有独立的类型化载荷与后端校验。 - 启用的脚本任务由 Tokio 调度循环自动执行,并支持启动后恢复调度。 - 后端重启后,已启用任务仍可持久化并重新注册。 - 支持手动立即运行,重复触发时记录为 `skipped`,不会并发执行同一任务。 - 每次运行记录触发来源、状态、耗时、退出码和错误信息。 - 已实现 Windows Job Object,超时或关闭时会终止目标进程树。 - stdout 和 stderr 会写入独立运行日志,并可通过 API 增量读取。 - HTTP Action 支持 GET/POST、Header、Body、超时和期望状态码。 - 二进制进程 Action 使用 Job Object 执行,并支持工作目录、参数和环境变量。 - 运行中任务可以通过 API 或前端日志抽屉取消。 - `/api/v1/events` 使用 SSE 推送运行状态和日志增量,前端断线时自动回退到轮询。 - 运行历史支持按状态和触发方式筛选,并支持下载完整日志。 - 敏感环境变量和敏感 Header 的值不会写入系统日志,输出中的匹配值会替换为 `[REDACTED]`。 - Python 脚本支持显式虚拟环境配置,并会自动向上探测 `.venv`、`venv`、`env`。 - 编辑器会随工作目录实时探测虚拟环境,显示 `已关联虚拟环境: .venv\Scripts\python.exe` 并同步更新调用预览。 - 工作目录支持「选择文件夹…」打开页面内的目录浏览器(盘符 → 逐级进入 → 使用此目录),选中后回填输入框,同时保留手动输入。 - 执行前会校验 `pyvenv.cfg` 和 `Scripts/python.exe`,优先使用虚拟环境解释器,并注入 PATH、`VIRTUAL_ENV` 和无缓冲输出设置。 - 日志按系统 ANSI 代码页(如 GBK)解码非 UTF-8 输出,中文程序不再显示为 `����`,且长日志跨 64 KiB 分块读取不会丢失字符。 - 日志支持保留期限和总容量清理,单次日志仍有 5 MiB 上限。 - React 页面已接入真实任务列表、搜索筛选、分页、CRUD、启停、手动运行、运行历史和日志轮询。 - 三类 Action 使用不同动态表单,并以类型化载荷提交,不再固定为脚本任务。 - Playwright 已覆盖运行日志、编辑、停用、删除和窄屏布局主流程。 - 后端只监听 `127.0.0.1`,拒绝非本地 Origin。 - 浏览器写操作需要运行期随机管理令牌,进程重启后自动更换。 - Axum 在生产模式托管 React 静态资源,并支持 SPA 路由回退。 - 弹窗和日志抽屉支持初始焦点、Tab 循环、Escape 关闭和焦点恢复。 - 提供 Windows Release 打包和一键启动脚本。 - 前端与后端均具备基础测试和统一质量检查命令。 ## 技术栈 ### 前端 - React 19 - TypeScript 5.9 - Vite 8 - React Router - TanStack Query - React Hook Form - Zod - Vitest + Testing Library - Playwright ### 后端 - Rust 1.95.0 MSVC - Axum - Tokio - Serde - Utoipa / OpenAPI - Croner - SQLx / SQLite - Tower HTTP ### 工具链 - Volta 2.0.2 - Node.js 24.12.0 - pnpm 11.20.0 Node.js 和 pnpm 版本固定在 `frontend/package.json` 的 `volta` 字段中,Rust 版本固定在根目录的 `rust-toolchain.toml` 中。 ## 环境要求 - Windows 10/11 - Volta - Rustup - Visual Studio C++ Build Tools,供 Rust MSVC 工具链使用 首次使用 Volta 时会自动切换到项目固定版本: ```powershell volta --version node --version pnpm --version rustc --version ``` ## 本地开发 首次拉取项目后安装前端依赖: ```powershell pnpm --dir frontend install ``` 终端一,启动 Rust API: ```powershell cargo run --manifest-path backend/Cargo.toml ``` 终端二,启动 React 前端: ```powershell pnpm --dir frontend dev ``` 访问 。Rust 服务默认监听 `127.0.0.1:3000`,可通过 `TASK_PILOT_PORT` 修改端口: ```powershell $env:TASK_PILOT_PORT = "3100" cargo run --manifest-path backend/Cargo.toml ``` 修改后端端口后,还需要同步调整 `frontend/vite.config.ts` 中的代理目标。 ## Windows Release 构建前端生产资源、Rust Release 可执行文件和可分发目录: ```powershell .\scripts\build-release.ps1 ``` 产物位于 `release/TaskPilot/`。启动打包版本: ```powershell .\release\TaskPilot\start.ps1 ``` 访问 。打包使用同源 API,因此 Release 模式修改 `TASK_PILOT_PORT` 后无需重新构建前端。 打包目录中的 [RELEASE.md](docs/RELEASE.md) 会作为使用与故障排查说明一并复制。 SQLite 数据库默认创建在启动目录的 `data/task_pilot.db`。通过以下环境变量可以使用其他数据库路径: ```powershell $env:TASK_PILOT_DATABASE_URL = "sqlite://data/custom.db" cargo run --manifest-path backend/Cargo.toml ``` 数据库迁移会在后端启动时自动执行。 运行日志默认写入 `logs/{task_id}/{run_id}.log`,可通过以下环境变量配置: ```powershell $env:TASK_PILOT_LOG_DIR = "logs" $env:TASK_PILOT_LOG_RETENTION_DAYS = "30" $env:TASK_PILOT_LOG_MAX_TOTAL_MB = "512" $env:TASK_PILOT_LOG_ENCODING = "auto" ``` `TASK_PILOT_LOG_ENCODING` 默认为 `auto`:逐行先按 UTF-8 解码,失败时回退到系统 ANSI 代码页(简体中文为 GBK),保证中文程序输出不会变成 `����`。需要强制某一种编码时可填写 `gbk`、`utf-8`、`windows-1251` 等标签。 单次运行日志达到 5 MiB 后会停止记录后续输出。程序启动和每次运行结束时会清理过期日志,并在总容量超限时优先删除最旧日志。 ## API | 方法 | 路径 | 用途 | | --- | --- | --- | | `GET` | `/api/v1/health` | 后端健康检查 | | `GET` | `/api/v1/session` | 获取当前进程的本地管理令牌 | | `GET` | `/api/v1/openapi.json` | 运行时 OpenAPI 文档 | | `POST` | `/api/v1/python/venv` | 探测工作目录会关联的 Python 虚拟环境 | | `GET` | `/api/v1/system/directories` | 列出盘符或指定目录下的子文件夹 | | `GET` | `/api/v1/tasks` | 查询任务列表 | | `POST` | `/api/v1/tasks` | 创建任务 | | `GET` | `/api/v1/tasks/{id}` | 查询任务详情 | | `PUT` | `/api/v1/tasks/{id}` | 完整更新任务 | | `DELETE` | `/api/v1/tasks/{id}` | 删除任务 | | `POST` | `/api/v1/tasks/{id}/enable` | 启用任务并注册调度 | | `POST` | `/api/v1/tasks/{id}/disable` | 停用任务并取消调度 | | `POST` | `/api/v1/tasks/{id}/run` | 手动立即执行 | | `GET` | `/api/v1/tasks/{id}/runs` | 查询任务运行历史 | | `GET` | `/api/v1/runs/{run_id}` | 查询单次运行状态 | | `GET` | `/api/v1/runs/{run_id}/logs?offset=0` | 增量读取运行日志 | | `GET` | `/api/v1/runs/{run_id}/logs/download` | 下载完整运行日志 | | `POST` | `/api/v1/runs/{run_id}/cancel` | 取消正在运行的任务 | | `GET` | `/api/v1/events` | SSE 运行状态和日志事件 | 任务列表查询参数: - `q`:按任务 ID 或名称搜索。 - `enabled`:按启用状态筛选。 - `action_type`:`script`、`http` 或 `process`。 - `page`:页码,默认 `1`。 - `page_size`:每页数量,默认 `20`,最大 `100`。 运行历史查询额外支持 `status` 和 `trigger` 过滤。 健康检查示例: ```json { "status": "ok", "service": "task-pilot-backend", "version": "0.1.0" } ``` 创建脚本任务示例: ```json { "id": "sync_data", "name": "数据同步", "cron": "0 0 2 * * *", "timezone": "Asia/Shanghai", "enabled": true, "timeout_secs": 300, "retry_policy": { "max_attempts": 0, "delay_secs": 0 }, "action": { "type": "script", "working_dir": "D:\\Projects\\sync", "interpreter": "python.exe", "script": "main.py", "args": ["--mode", "full"], "env": { "PYTHONUNBUFFERED": "1" } } } ``` 校验失败和资源不存在时统一返回结构化错误: ```json { "code": "VALIDATION_ERROR", "message": "Cron 表达式无效", "field": "cron", "details": {} } ``` ## 开发命令 ### 完整检查 在项目根目录执行: ```powershell .\scripts\check.ps1 ``` 该脚本依次运行: - 前端 lint - TypeScript 类型检查 - Vitest - 前端生产构建 - Rust 格式检查 - Rust Clippy - Rust 单元和集成测试 - Playwright 浏览器端到端测试 ### 浏览器端到端测试 ```powershell pnpm --dir frontend test:e2e ``` 端到端测试会清理并重建 `.e2e-data/`,启动临时 Rust 服务和 Vite,使用本机 Chrome 完成真实任务执行和页面操作。 ### 生成接口契约和类型 修改后端 API DTO 或路由后执行: ```powershell .\scripts\generate-api.ps1 ``` 该命令会更新: - `contracts/openapi.json` - `frontend/src/api/generated.ts` 生成文件不应手工修改。 ### 单独检查 ```powershell pnpm --dir frontend lint pnpm --dir frontend typecheck pnpm --dir frontend test pnpm --dir frontend build cargo fmt --manifest-path backend/Cargo.toml -- --check cargo clippy --manifest-path backend/Cargo.toml --all-targets -- -D warnings cargo test --manifest-path backend/Cargo.toml ``` ## 项目结构 ```text task_pilot/ ├── backend/ │ ├── src/ │ │ ├── api.rs │ │ ├── cron.rs │ │ ├── database.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── process_job.rs │ │ ├── scheduler.rs │ │ ├── security.rs │ │ ├── state.rs │ │ ├── runs/ │ │ └── tasks/ │ ├── migrations/ │ ├── tests/ │ ├── Cargo.toml │ └── Cargo.lock ├── contracts/ │ └── openapi.json ├── docs/ │ └── decisions/ ├── frontend/ │ ├── e2e/ │ ├── src/ │ │ ├── components/ │ │ ├── lib/ │ │ └── pages/ │ ├── package.json │ ├── playwright.config.ts │ ├── vite.config.ts │ └── vitest.config.ts ├── scripts/ │ ├── check.ps1 │ ├── build-release.ps1 │ ├── e2e.ps1 │ ├── generate-api.ps1 │ └── start.ps1 ├── task_pilot.html ├── PLAN.md ├── README.md └── rust-toolchain.toml ``` 架构决策记录: - [ADR-0001:Cron 解析与任务调度](docs/decisions/0001-cron-engine.md) - [ADR-0002:前后端开发与生产托管](docs/decisions/0002-static-hosting.md) - [ADR-0003:Python 虚拟环境启动环境](docs/decisions/0003-python-environment.md) - [ADR-0004:运行日志的输出编码](docs/decisions/0004-log-encoding.md) - [ADR-0005:由后端提供系统文件夹选择](docs/decisions/0005-folder-picker.md) ## 当前限制 - 重试策略字段已持久化,但自动重试尚未实现。 - HTTP 任务执行器尚未提供响应 Header 转换等高级断言,只校验状态码。 - 敏感值脱敏基于变量名关键词和值替换,不是系统凭据保险箱。 - Conda 环境不会按 venv 自动激活,目前需要显式填写其 Python 解释器路径。 - 当前版本是本地单用户工具,不支持多用户账户和远程部署。 - 尚未安装为 Windows 系统服务,关闭启动脚本后调度器也会停止。 - 旧 `task_pilot.html` 暂时保留在根目录作为设计参考,正式入口为 React 应用。 ## 开发计划 完整架构、数据模型、API 草案、阶段任务和验收标准见 [PLAN.md](PLAN.md)。六个开发阶段均已完成,后续进入维护、功能反馈和版本迭代。