我把 Mac 里沉睡的邮局,改造成了 Claude 的深夜汇报系统

Claude AI Agent macOS Postfix 教程

先看效果

下班前给 Claude Code 留一句话:

把这批测试全跑一遍,失败的修掉,做完邮件通知我。

第二天早上,邮箱里会有这么一封信:

邮件效果

干了什么、结果如何、关键截图、卡在哪,都在里面。任务完成有彩纸庆贺横幅;失败也会发,横幅换成一条断裂的红墨线,内容变成事故报告。发信的不是哪个云服务,是我这台 Mac 自己——用的是 macOS 出厂自带、绝大多数人从没用过的 Postfix。

整套东西打包成了一个开源项目,装好只要几分钟:

git clone https://github.com/tespera/mail-me-when-done.git
cd mail-me-when-done
./install.sh

向导会让你选邮箱服务商(QQ/Foxmail、163、126、Gmail、Outlook、iCloud、阿里云、自定义 SMTP 都支持)、填授权码,然后自动配置 Postfix、安装技能、发一封测试邮件。收到测试邮件就算装完了。技能装给 Claude Code 还是 Codex(ChatGPT)由向导询问,两个都装也行;其他 AI 工具(Cursor、Gemini CLI 之类)也能用——它说到底就是一份说明书加一个发送脚本,照 README 手动放进对应目录即可。

下面说说这东西是怎么来的。

起因是一行提示

我的终端每次打开都有这么一行:

You have new mail

挂了快一个月。它不烦人,就安安静静待在那,像旅馆前台给你留了张字条。但心里始终有个疑问:我从没在这台 Mac 上装过任何邮件服务,谁在给我的终端发邮件?

有天让 Claude Code 查了一下,结果是这样的:

  • 邮件真实存在,在 /var/mail/eric,纯文本文件,cat 就能看;
  • 发件人是 Cron Daemon。七月我配过一个定时同步脚本,它写日志时被 macOS 的目录权限拦了(Operation not permitted),报错输出被打包成了两封邮件。这是 cron 的老规矩:任务有输出,就把输出作为邮件发给任务的主人;
  • 负责投递这两封邮件的,是 macOS 自带的 Postfix,一个功能完整的邮件服务器。

最后一条让我愣了一下:我的 Mac 里一直装着一个邮局?验证很简单:

postconf mail_version
# mail_version = 3.2.2

每台 Mac 都有。它由 launchd 按需拉起,平时不运行,有邮件要投递才醒,投完接着睡。默认只监听本机回环(inet_interfaces = loopback-only),不对外开放任何端口。它存在的原因是 Unix 的老传统:系统进程有话要说就发邮件——cron 出错、磁盘配额满了,都走这条路。只是到了个人电脑时代,没人再看这个信箱,它就一直闲着。

知道它的存在之后,我顺嘴多问了一句:这东西有可玩性吗,能拿来做点什么有趣的事?Claude 列了几个玩法——本地开发时当"邮件黑洞"测试注册邮件、用邮件管道触发脚本……但排在第一的最实用:配一个"中继",让本机任何脚本都能一行命令发真邮件到手机上。这是老派 Unix 服务器的标准玩法,服务器出事了给管理员发信,搬到笔记本上照样成立。

“发真邮件到手机上”——看到这几个字,我想起了一个一直没解决的问题。

我的问题:深夜任务是个黑箱

我经常下班前丢给 Claude Code 一个长任务,第二天早上回来开盲盒。中间这十几个小时是全黑的:做完了没有、卡在哪、做到什么程度,一概不知道。

Claude Code 本身会写总结、会截图,缺的只是把这些东西送到我手机上的通道。现在通道有了。

配置 Postfix 中继

先说为什么不能让 Postfix 直接往外发。家用网络没有固定 IP,也没有 SPF/DKIM 这些发件方身份记录,直发的邮件在收件服务器眼里和垃圾邮件没有区别,基本必进垃圾箱。正确做法是让 Postfix 把信转交给正规邮件服务商的 SMTP 服务器代发,这个配置叫中继(relay)。

以 QQ 邮箱为例,手动配置是四步。install.sh 自动做的也就是这些事,想理解原理的往下看,想直接用的跳到下一节。

第一步,拿授权码。 网页版 QQ 邮箱 → 设置 → 账号 → 开启 SMTP 服务,生成授权码。这是给客户端用的专用密码,不是 QQ 密码,可以随时吊销。

第二步,把授权码交给 Postfix。 密码表是 root 所有、600 权限的文件,Postfix 的标准做法:

sudo sh -c 'echo "[smtp.qq.com]:587 你的邮箱@qq.com:授权码" > /etc/postfix/sasl_passwd'
sudo postmap hash:/etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

第三步,发件人重写。 QQ 要求发件地址必须等于登录账号,而本机进程发信的默认地址是 用户名@主机名.local,会被直接拒收。需要一张改写对照表:

sudo sh -c 'echo "$(whoami)@$(postconf -h myhostname) 你的邮箱@qq.com" > /etc/postfix/generic'
sudo postmap hash:/etc/postfix/generic

第四步,主配置。 指定中继地址,打开认证和加密:

sudo postconf -e \
  "relayhost = [smtp.qq.com]:587" \
  "smtp_sasl_auth_enable = yes" \
  "smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd" \
  "smtp_sasl_security_options = noanonymous" \
  "smtp_tls_security_level = encrypt" \
  "smtp_generic_maps = hash:/etc/postfix/generic"

配完了,见证时刻:

echo "你好,我是你的 Mac" | mail -s "邮局复活了" 你的邮箱@qq.com

几秒后手机震了一下。诞生于半个世纪前的 mail 命令,把信送进了我的 QQ 邮箱。

查投递状态用 mailq:队列空了就是已交给 QQ 服务器;有滞留条目会写明原因,535 是授权码错误。不要指望系统日志——macOS 的统一日志默认不记录 mail 相关条目,/var/log/mail.log 也不存在,我在这上面浪费过时间。

安全上三点,都别跳过:授权码只存在 root 600 的文件里,不要写进任何脚本;inet_interfaces = loopback-only 是默认值,不要改——对外开放的邮件中继在公网上几小时内就会被扫描到,拿去发垃圾邮件;macOS 大版本升级可能重置 main.cf,重跑一遍配置即可。

让 Claude 会发报告

剩下的工作是两个文件。

一个是渲染脚本 send_report.py。Claude 写的报告是 Markdown,要变成邮件里的富文本。邮件的 HTML 和网页的 HTML 是两回事,有三个问题必须处理:

  1. 邮件客户端会删掉 <style> 块,所有样式必须内联到每个标签上;
  2. 外链图片默认被拦截(QQ 邮箱就是这样),报告里的截图要转成 MIME 内嵌附件,用 cid: 引用,打开邮件直接显示;
  3. 手机客户端遇到超宽内容(不折行的长命令、超宽的图)不会给滚动条,而是把整封邮件等比缩小,字就变得特别小。解决办法是代码块强制折行、长链接允许断字、容器用流式宽度。这个问题伪装成"字号太小",实际调字号没有任何用。

视觉上走克制路线:白底、细分割线、通篇基本单色,手机上读着不累。不喜欢这个审美也没关系,模板是可替换的:往 ~/.config/email-report/ 放一个 template.html(整体框架)或 styles.json(各元素的字号配色)就能覆盖默认样式,不用改代码,也不用懂邮件 HTML 的那些坑——防缩放、样式内联这些底层处理都是现成的。

另一个是 Claude Code 的技能文件 SKILL.md。技能就是一份写给 Claude 的说明书:什么时候用(用户说了"做完邮件通知我")、报告按什么结构写(TL;DR、完成情况、关键结果、问题、下一步)、发完怎么验证(mailq)。里面有两条我特意写死的规则:

  • 任务失败也要发,红色状态,写清卡在哪、试过什么。比坏消息更糟的是没有消息;
  • 没验证过的结论必须标"未验证"。这封邮件没法当场追问,不能掺水。

这两个文件加上安装脚本,就是开头那个仓库的全部内容。

再进一步:连那句话都不用说

用了几天之后我发现自己经常忘了说"做完邮件通知我"。索性把判断也交给 Claude——在全局 ~/.claude/CLAUDE.md 里加一条规则:

## 长程任务自动邮件汇报

判据不是"任务大小",而是**我大概率看不到对话里的汇报**:任务连续自主执行超过约 30 分钟、
且期间我没有插过话(接到长任务时用 `date` 记一下开始时间,结束时对比),就视为我已离开电脑——
任务结束时(完成、失败、被阻塞都算"结束")自动用 email-report 技能发报告邮件。
为什么:我在场时邮件是噪音,我离开时对话里的汇报我看不见。

- 我明确说"做完邮件通知我" → 无论时长一律发;明确说"不用发邮件" → 一律不发。
- 一个任务只发一封总结邮件,不要过程中刷屏;追加的小修小补不重复发。

这条规则解决的是"该不该发"的判断问题。你真正关心的其实不是任务大小,而是你能不能看到对话里的汇报:人在屏幕前,对话里说清楚就行,邮件反而是噪音;人已经下班走了,对话里写得再详细也是白写。但 Claude 感知不到你人在不在,所以判据必须换成它能真实测量的东西——任务自主跑了多久、期间你有没有说话。跑了半小时你都没吭声,基本就是走了,该发邮件;你一直在旁边盯着改需求,那就不发。

配上这条规则,整套系统才算闭环:短任务当面汇报,长任务邮件汇报,全程不用你操心通知方式。

结语

Postfix 的前身 Sendmail 诞生于 1983 年,mail 命令更是能追到 1970 年代的贝尔实验室。这些比我岁数还大的 Unix 基建,安安静静在每台 Mac 里躺了十几年,等来的第一份正经工作,是给一个 2026 年的 AI 当邮差。

老古董和新智能之间,原来只隔一个授权码。

你 Mac 里的那个邮局也睡了很多年了。要不要叫醒它试试?


本文的配置过程、渲染脚本、Claude Code 技能均已开源:mail-me-when-done

评论