【本文由 AI 辅助完成】
上篇《Everyday CLI开发经验谈》记录到 v0.10–v0.11 时代。此后两周,Everyday CLI 从 v0.13.0 迭代到 v0.18.0,跨了 11 个版本点。本文按「版本时间线 + 主题深挖」双线梳理这段演进,最后单独成章,分享这期间最有代表性的一类经验——在 WorkBuddy 沙盒不稳定性下的工程实践。
版本时间线
| 版本 | 日期 | 主要内容 |
|---|---|---|
| v0.13.0 | 08-09 | WebDAV 同步(D001-D003) |
| v0.14.0 | 08-10 | auth 环境变量凭证回退(R020) |
| v0.15.0 | 08-10 | MCP server over stdio(F014) |
| v0.16.0–2 | 08-10 | tracing 分级日志(F015) |
| v0.17.0 | 08-14 | daemon 守护进程(F016) |
| v0.17.1 | 08-14 | 日期序列 ID + PID(R021) |
| v0.17.2 | 08-18 | task 命令执行 + cron(F017) |
| v0.17.4 | 08-18 | task/config/output 重构 |
| v0.17.6 | 08-19 | rss digest –since(F008 修订) |
| v0.17.7 | 08-21 | 日历时区修复(Utc → Local) |
| v0.18.0 | 08-24 | mail search –cached + mail gc |
两周 11 个版本点,节奏相当密集。以下按主题展开。
同步与凭证
v0.13.0 引入 WebDAV 同步(src/modules/sync/,D001-D003),跨设备数据打通,这是 daily 数据能跟随 agent 迁移的基础设施。
v0.14.0 的 R020 补强了凭证体系:[auth] env_credentials = true 与 EVERYDAY_ENV_CREDENTIALS=1 双通道,变量命名 EVERYDAY_<MODULE>_<ACCOUNT>_PASSWORD,读取链 keyring → env → 报错。这条规则解决了无钥匙串环境下 agent 取不到凭据的问题——CI 或容器里没有系统 keyring,环境变量回退是刚需。
MCP server over stdio
v0.15.0(F014)让 everyday 可直接作为 MCP server 被 WorkBuddy 等客户端拉起。这个版本定下的契约后来成为所有模块被 agent 稳定调用的基础:
- stdout 专供 JSON-RPC,任何日志、调试信息一律走 stderr;
- JSON 输出三系形状
{"_log"}/{"_warning"}/{"_error"}不可变。
日志与可观测
v0.16.0–2 落地 tracing 分级日志(F015),默认 WARN 静音。原因很实际:agent 会话里如果默认 INFO,调试噪音会淹没真正的问题;需要细粒度追踪时再显式调级别。
daemon 守护进程
v0.17.0(F016)是走向「个人 Info Agent」的关键一步。daemon run [--once] / daemon status,周期性同步 timeline + mail + rss,状态落在 ~/.config/everyday/daemon-state.json。设计上值得注意的两点:
- pid 存活检测防重入:同一时刻只允许一个 daemon 实例;
- 同步 = timeline
run_sync+ mail 全 folders 增量(每周期 IMAP LIST)+ rss。
编号体系与 task 子系统
v0.17.1 定稿 R021 编号规则:id = {前缀}{YYYYMMDD}-{当日序号}(n/t/b/m/ev/mc/ri),按天重置、每前缀独立计数;旧格式共存不迁移。
v0.17.2(F017)给 task 模块加入命令执行与 cron 能力,v0.17.4 又对 task/config/output 做了一轮重构(Scheduler 上下文)——这是 v0.17.x 里真正的 SOLID 依赖反转对象。
rss 与日历
v0.17.6 修订 F008:rss digest --since 支持时间窗口聚合。
v0.17.7 修了一个隐蔽的日历时区 bug:cal list 对 UTC 存储事件直接输出 UTC 字符串、不转本地,导致日程显示早 8 小时。修复是 format_date_perhaps_time 与 date_perhaps_time_to_naive 对 Utc 变体先转 Local 再输出。更深一层是服务端数据缺陷:QQ 日历对手动创建的事件按「钟面时间直接标 Z」存储(naive-as-UTC),对同步导入事件存正确 UTC,两者从 ICS 无法区分——这个只能留作已知限制。
mail 收尾
v0.18.0 补齐 mail 模块最后两块拼图:mail search --cached(M006,信封缓存内搜索)与 mail gc(M007,缓存回收)。
开发经验:WorkBuddy 沙盒下的工程实践
v0.17.x 期间踩得最深的一类坑,来自开发环境本身——WorkBuddy 沙盒在 Windows 上对命令执行有各种不稳定性。沉淀出三条铁律:
1. 输出落盘 + 哨兵行,日志是唯一事实来源
1 | cmd > log 2>&1; echo "EXIT=$?" >> log |
vitest 等工具经管道输出时退出码会误报(管道吞掉真实 exit code);\r 进度条刷新符在管道捕获层被吞,工具返回空但命令其实已跑完。做法:所有测试/构建/安装命令重定向到日志文件,末尾追加哨兵行,只以日志为判断依据,绝不凭管道退出码下结论。
2. 工具空返回 ≠ 命令失败,先读日志再决定
「命令执行完 ≠ 工具感知到」。工具返回空或超时,先读日志、再查进程是否存活(tasklist),最后才决定是否重跑——盲重跑不仅浪费,还会掩盖真实的间歇性失败。
3. 怀疑命令执行错误时,用 Python subprocess 落盘验证
工具层报 Permission denied / os error 5 / 空返回,先写一次性 Python 脚本用 subprocess 把 stdout/stderr 落盘跑一遍。沙盒文件层写入受限而命令本身可正常执行,是 Windows 沙盒下的常见情况——工具报错不一定是命令真的失败。
附一个 Rust 专属边注:多次强杀 cargo 后,沙盒文件层可能持有 .cargo-build-lock 句柄,导致所有 target 构建失败(WinError 5)。cargo clean 直接根治,比重启应用快得多。
现状与下一步
当前 v0.18.0:13 个模块 + MCP server + WebDAV 同步,350+ 测试,冷启动 <100ms。下一步重心在 timeline 聚合的深化,以及 system/fs/network 模块缺口的评估。
参考链接: