- 主题色板、圆角、阴影与 0.3s 缓动对齐参考站点,布局与尺寸不变 - index.html 预加载本地字体,index.css 定义 LXGW WenKai / Outfit-Medium - 新增断网渲染端到端用例与 ADR-0006
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 时会自动切换到项目固定版本:
volta --version
node --version
pnpm --version
rustc --version
本地开发
首次拉取项目后安装前端依赖:
pnpm --dir frontend install
终端一,启动 Rust API:
cargo run --manifest-path backend/Cargo.toml
终端二,启动 React 前端:
pnpm --dir frontend dev
访问 http://127.0.0.1:5173。Rust 服务默认监听 127.0.0.1:3000,可通过 TASK_PILOT_PORT 修改端口:
$env:TASK_PILOT_PORT = "3100"
cargo run --manifest-path backend/Cargo.toml
修改后端端口后,还需要同步调整 frontend/vite.config.ts 中的代理目标。
Windows Release
构建前端生产资源、Rust Release 可执行文件和可分发目录:
.\scripts\build-release.ps1
产物位于 release/TaskPilot/。启动打包版本:
.\release\TaskPilot\start.ps1
访问 http://127.0.0.1:3000。打包使用同源 API,因此 Release 模式修改 TASK_PILOT_PORT 后无需重新构建前端。
打包目录中的 RELEASE.md 会作为使用与故障排查说明一并复制。
SQLite 数据库默认创建在启动目录的 data/task_pilot.db。通过以下环境变量可以使用其他数据库路径:
$env:TASK_PILOT_DATABASE_URL = "sqlite://data/custom.db"
cargo run --manifest-path backend/Cargo.toml
数据库迁移会在后端启动时自动执行。
运行日志默认写入 logs/{task_id}/{run_id}.log,可通过以下环境变量配置:
$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 过滤。
健康检查示例:
{
"status": "ok",
"service": "task-pilot-backend",
"version": "0.1.0"
}
创建脚本任务示例:
{
"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"
}
}
}
校验失败和资源不存在时统一返回结构化错误:
{
"code": "VALIDATION_ERROR",
"message": "Cron 表达式无效",
"field": "cron",
"details": {}
}
开发命令
完整检查
在项目根目录执行:
.\scripts\check.ps1
该脚本依次运行:
- 前端 lint
- TypeScript 类型检查
- Vitest
- 前端生产构建
- Rust 格式检查
- Rust Clippy
- Rust 单元和集成测试
- Playwright 浏览器端到端测试
浏览器端到端测试
pnpm --dir frontend test:e2e
端到端测试会清理并重建 .e2e-data/,启动临时 Rust 服务和 Vite,使用本机 Chrome 完成真实任务执行和页面操作。
生成接口契约和类型
修改后端 API DTO 或路由后执行:
.\scripts\generate-api.ps1
该命令会更新:
contracts/openapi.jsonfrontend/src/api/generated.ts
生成文件不应手工修改。
单独检查
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
项目结构
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 解析与任务调度
- ADR-0002:前后端开发与生产托管
- ADR-0003:Python 虚拟环境启动环境
- ADR-0004:运行日志的输出编码
- ADR-0005:由后端提供系统文件夹选择
当前限制
- 重试策略字段已持久化,但自动重试尚未实现。
- HTTP 任务执行器尚未提供响应 Header 转换等高级断言,只校验状态码。
- 敏感值脱敏基于变量名关键词和值替换,不是系统凭据保险箱。
- Conda 环境不会按 venv 自动激活,目前需要显式填写其 Python 解释器路径。
- 当前版本是本地单用户工具,不支持多用户账户和远程部署。
- 尚未安装为 Windows 系统服务,关闭启动脚本后调度器也会停止。
- 旧
task_pilot.html暂时保留在根目录作为设计参考,正式入口为 React 应用。
开发计划
完整架构、数据模型、API 草案、阶段任务和验收标准见 PLAN.md。六个开发阶段均已完成,后续进入维护、功能反馈和版本迭代。