Skip to content

fix(cli,plugin-email)!: 缺 apiKey 的 resend/postmark 不再静默降级为 LogTransport (#5132) - #5153

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-5132-cli-email-provider-loud
Aug 4, 2026
Merged

fix(cli,plugin-email)!: 缺 apiKey 的 resend/postmark 不再静默降级为 LogTransport (#5132)#5153
os-zhuang merged 2 commits into
mainfrom
claude/issue-5132-cli-email-provider-loud

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5132

改了什么

packages/cli/src/commands/serve.tsresolveEmailCapabilityArg 里,providerlog/smtp 且缺 apiKey 时把 provider 改写成 log、打一条 warning、照常启动的那段没了。现在与紧邻的 smtp 分支同形:建不出投递通道就抛,由 capability 循环把它变成响亮失败。

覆盖三种「声明了投递意图但投不出去」的配置:

配置 改前 改后
OS_EMAIL_PROVIDER=resend/postmark,无 OS_EMAIL_API_KEY 降级 log + warning,正常启动
OS_EMAIL_PROVIDER=sendgrid(已退役)/ 拼错的 tag 无 key 时同样降级 log;有 key 时透传到插件层再抛 在 CLI 层抛,消息里带 #5094 的 SMTP 迁移写法
OS_EMAIL_PROVIDER=smtp 无 host 抛(#5087 已修) 不变

「响亮失败」的准确含义(未夸大):app 在 requires: [...] 里显式声明了 email 时是硬启动失败;email 是平台自动补齐的默认能力时,是 console.error + 不挂载 email 服务。两种都不再是「每封都成功、一封没发出」——这与 smtp 分支现有行为完全一致。

抛错成立的前提是「本环境不发信」有说得出口的写法:OS_EMAIL_PROVIDER=log(也是默认值)。测试专门钉住了这一条仍能正常启动。

错误消息按 AGENTS.md degradation-log-level 同时给出后果与修复,措辞照抄 smtp 缺 host 那条的形状:

provider='resend' selects resend delivery but no API key is configured, so every send would
be recorded in sys_email as sent and nothing would leave the box — set OS_EMAIL_API_KEY
(or config.email.apiKey), or set OS_EMAIL_PROVIDER=log if this environment is not meant to send mail.

provider 词汇表:复用而非重写

按裁定要求,CLI 不再自己写 provider !== 'log' && provider !== 'smtp',而是从 @objectstack/plugin-email(#5133 合入的单一真相)读:isEmailTransportProvider / unsupportedProviderFix

「哪些 provider 需要 apiKey」此前没有共享形式(makeTransport 的 switch 里、mail.manifest.ts 里各有一份),所以本 PR 在同一个文件里补上:

  • API_KEY_EMAIL_PROVIDERS(satisfies readonly EmailTransportProvider[])、ApiKeyEmailProvideremailProviderRequiresApiKey();
  • makeTransportresend/postmark 分支改走 requireApiKey(provider, apiKey),其形参类型是 ApiKeyEmailProvider —— 常量里删掉一个 tag,switch 直接编译不过(这是一个方向的锁);
  • 另一个方向(常量里加了 tag、对应分支忘了校验)由新增的 transports/api-key-providers.contract.test.ts 遍历 EMAIL_TRANSPORT_PROVIDERS 逐个实测 makeTransport 的真实行为钉住。

makeTransport 抛出的文案与改前逐字一致,插件层行为不变。

破坏性变更

@objectstack/cli 记为 major,changeset 里写明了受影响人群与迁移:只设 OS_EMAIL_PROVIDER=resend|postmark 而不配 key、靠降级启动的部署(典型是 CI / preview 环境),两条一行修法——改成 OS_EMAIL_PROVIDER=log,或补上 OS_EMAIL_API_KEY。完整配置、以及不设 OS_EMAIL_PROVIDER 的默认路径都不受影响。

顺带把 content/docs/deployment/environment-variables.mdxOS_EMAIL_API_KEY 一行改成 Required(参照同表 OS_EMAIL_SMTP_HOST 的写法),并点明 log 是「本环境不发信」的声明方式。

测试

pnpm --filter @objectstack/cli test            → 69 files / 612 tests passed
pnpm --filter @objectstack/cli typecheck       → clean
pnpm --filter @objectstack/plugin-email test   → 9 files / 133 tests passed
pnpm --filter @objectstack/plugin-email typecheck → clean
eslint(改动文件)/ check:doc-authoring / check:docs-audit-scope → clean

serve-email-capability.test.ts:钉降级行为的那条用例已翻面(postmark 无 key 从「断言回落到 log」改为「断言抛错 + 消息里同时有后果、OS_EMAIL_API_KEYOS_EMAIL_PROVIDER=log」),另加显式 OS_EMAIL_PROVIDER=log 仍能启动、未知/退役 provider 抛错并带迁移、完整 resend/postmark 配置原样透传三条;warning 字段随最后一个生产者一起删除(接口上不再保留没人写入的降级通道)。

越界发现

🤖 Generated with Claude Code

https://claude.ai/code/session_017MCKJaEomEqg4tvz4SzdNd


Generated by Claude Code

claude added 2 commits August 4, 2026 06:30
…Key fails the boot instead of becoming a LogTransport (#5132)

`resolveEmailCapabilityArg` answered a missing API key by rewriting the
provider to `log`, printing a warning, and booting: a server that accepted
every send, recorded each in `sys_email` as sent, and delivered nothing.
#5087 closed that inside plugin-email (`makeTransport` throws rather than
substituting a transport); the CLI kept doing it one layer up, which the
#5087 PR itself flagged in this function's docstring.

It now refuses every mail configuration it cannot deliver through, the way
its neighbouring `smtp` arm already did — resend/postmark with no key, and
a provider tag outside the supported set (retired `sendgrid`/`ses` get the
SMTP migration). Each message names the consequence and both fixes, per
AGENTS.md degradation-log-level. Refusing is only fair because
`OS_EMAIL_PROVIDER=log` is how a deployment says "no mail from here" — a
test pins that it still boots.

The provider vocabulary is read from `@objectstack/plugin-email`
(`isEmailTransportProvider` / `unsupportedProviderFix` from #5094, plus the
new `API_KEY_EMAIL_PROVIDERS` / `emailProviderRequiresApiKey`) rather than
restated in the CLI, and the new constant is tied to `makeTransport` by a
compile error in one direction and a contract test in the other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MCKJaEomEqg4tvz4SzdNd
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 6:33am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/plugin-email.

24 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/cli)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli)
  • content/docs/automation/flows.mdx (via @objectstack/plugin-email)
  • content/docs/automation/hook-bodies.mdx (via packages/cli)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/cli.mdx (via @objectstack/cli)
  • content/docs/deployment/environment-variables.mdx (via @objectstack/plugin-email)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/deployment/validating-metadata.mdx (via packages/cli)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/plugins/index.mdx (via @objectstack/cli)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/plugin-email)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/plugin-email)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli)
  • content/docs/releases/v16.mdx (via @objectstack/cli)
  • content/docs/releases/v17.mdx (via @objectstack/cli)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review August 4, 2026 06:40
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit bcfebb0 Aug 4, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5132-cli-email-provider-loud branch August 4, 2026 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cli: OS_EMAIL_PROVIDER=resend/postmark 缺 apiKey 时静默降级为 LogTransport —— #5087 在 CLI 层遗留的同形缺口

2 participants