You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
if(provider!=='log'&&provider!=='smtp'&&!apiKey){options.provider='log';return{
options,warning: `provider='${provider}' but no apiKey found (set OS_EMAIL_API_KEY or config.email.apiKey). `+'Falling back to LogTransport.',};}
provider='smtp' with no host throws. The capability loop turns that into a boot failure, which is the point: the alternative (quietly substituting the LogTransport, as this function's resend/postmark arm still does for a missing API key) hands the operator a server that accepts every send, records it in sys_email, and delivers nothing — the exact declared-but-not-delivered gap #5087 closed inside the plugin.
Activity
os-zhuang commentedon Aug 4, 2026
分诊(PM 循环,邮件线派生):auto-queue ——
pm:queue+domain:cli。这是 #5087 的同一条缺陷在 CLI 层的残留,而且插件层已经修好了(
makeTransport建不出就抛),所以现状是 CLI 单方面把一个本该响亮的失败按了下去。属 restore-invariant 类,不需要维护者拍板。PM 裁定:与
smtp分支对齐,缺 apiKey 直接抛,让 boot 失败。 不走「先升到 error、再按迁移窗口改抛」那条渐进路线,理由是你自己在建议里点出的那一条:log这个显式取值的存在,正是这条抛错成立的前提——想要「本环境不发信」的部署有一个说得出口的写法(OS_EMAIL_PROVIDER=log),不必靠「配了 provider 却不配 key」来表达。而渐进路线的代价是这段时间里服务器继续「每封都成功、一封都没发出」,那正是本单要消灭的状态。你提的兼容性顾虑(是否有 CI 环境只设
OS_EMAIL_PROVIDER而依赖降级启动)按这个口径处理:这不是需要事先普查的阻塞项,而是 changeset 里要写清楚的破坏性变更——迁移动作只有一句「把OS_EMAIL_PROVIDER=resend改成=log,或补上OS_EMAIL_API_KEY」,成本极低,而继续静默的代价是用户报「收不到验证码」才暴露。派发时会要求错误消息同时给出后果与修复,并把serve-email-capability.test.ts里钉当前降级行为的那条用例一并翻面。排队,不与 PR #5133 同批(同属邮件装配路径,且 #5133 正在改
plugin-email/transports/index.ts的 provider 集合,本单要读那个集合)。Generated by Claude Code
os-zhuang commentedon Aug 4, 2026
认领:PM 循环第 4 轮
会话:
session_017MCKJaEomEqg4tvz4SzdNd分支:
claude/issue-5132-cli-email-provider-loudWorktree:
objectstack-issue-5132域:
domain:cli文件面:
packages/cli/src/commands/serve.ts(resolveEmailCapabilityArg一带)、packages/cli/src/commands/serve-email-capability.test.ts(越界即停,报告说明)前置条件已满足:#5094 的 PR #5133 已合入 main(
8597a7d79),EMAIL_TRANSPORT_PROVIDERS现在是 provider 集合的单一真相,本单可以直接读它。裁定见上一条评论,不重复。在飞撞车检查:
domain:cli有 #5091 / #5092 在飞(#5040 executor 家族),但它们落在 runtime/hono 的端点派发面,与serve.ts的 capability 装配段不相交。仍要求你合并最新origin/main后再动手 —— 该文件今天已被多次改动。Generated by Claude Code
os-zhuang commentedon Aug 4, 2026
复核结论:ACCEPT —— PR #5153,转 ready 并入合并队列。CI 在 head
420d0865(已合并最新 main)上 7 个工作流全绿。落地的正是裁定要求的形状
resolveEmailCapabilityArg缺 apiKey 不再改写 provider + warning 启动,而是与紧邻的smtp分支同形直接抛;错误消息同时给出后果(每封都记为 sent、一封不出门)与修复(设OS_EMAIL_API_KEY,或显式OS_EMAIL_PROVIDER=log)。docstring 也重写了,把「refusing is only defensible because『本环境不发信』有说得出口的写法」这条前提写进代码,而不是只留在 issue 里。dev 找到了我没看见的第二份隐含列表
我只要求「复用 #5133 的
EMAIL_TRANSPORT_PROVIDERS,不要在 CLI 里再手写 provider 字面量」。dev 照做了,并且发现还有一份:「哪些 provider 需要 API key」这个判断此前硬编码在 CLI 的provider !== 'log' && provider !== 'smtp'里 —— 同样是一份会漂移的隐含清单,只是它编码的是「谁要 key」而不是「谁存在」。它把这条提成 plugin-email 的
API_KEY_EMAIL_PROVIDERS/emailProviderRequiresApiKey,并让makeTransport的 resend/postmark 分支经由requireApiKey(provider: ApiKeyEmailProvider, …)取值 —— 删常量即编译不过,反方向由新增的api-key-providers.contract.test.ts钉住(断言它是EMAIL_TRANSPORT_PROVIDERS的子集、缺 key 时建构真的抛、且emailProviderRequiresApiKey('sendgrid') === false)。这是 #5094 那条「两份字面量可以靠改另一份『修好』」的教训被主动应用到了一个我没点名的地方。makeTransport对外文案逐字不变,所以这层重构没有把行为改动混进来。一处删除我特意核过
EmailCapabilityArg.warning整个通道被删了。它只承载过一条消息(「falling back to LogTransport」),而那条降级现在是抛错。我确认过serve.ts里已无.warning消费者 —— 是删干净,不是删一半留个悬空字段。changeset 按要求写清了 breaking:受影响人群 + 一行迁移;
environment-variables.mdx的OS_EMAIL_API_KEY也标了 Required。顺带确认的搜索先行
dev 路过又看到
EmailProviderSchema缺'smtp',查到 #5104 已在跟,没有开孪生单。#5104 仍在排队等 spec 车道(#5114 / #5011 / #4964 / #4001 四单在飞)。Generated by Claude Code
fix(cli,plugin-email)!: OS_EMAIL_PROVIDER=resend/postmark with no api…