fix(cli,service-sms)!: 启动期拒绝表外的 OS_SMS_PROVIDER,而不是静默降级成 LogSmsTransport (#5713) - #5771
Merged
Merged
Conversation
…ead of silently degrading to LogSmsTransport (#5713) `os serve` read `OS_SMS_PROVIDER` / `config.sms.provider` while assembling the kernel and handed the string to `SmsServicePlugin` with nothing to compare it against. The plugin caught the resulting `makeSmsTransport: unknown provider` throw and substituted `LogSmsTransport`, so a typo booted a server whose `send()` answers `status: 'sent'` and delivers nothing. This path never reaches `SettingsService`, which is why the `sms` namespace's `select` options table, its write-path enforcement (#5131) and the env-override gate (#5204) could none of them see it. `resolveSmsCapabilityArg` is extracted (mirroring `resolveEmailCapabilityArg`) and throws on a tag outside the vocabulary; the capability loop turns that into a hard boot error for a declared `requires: ['sms']`, else a loud console.error. Credentials are deliberately NOT demanded here — the settings namespace binds them at kernel:ready. `@objectstack/service-sms` exports `SMS_TRANSPORT_PROVIDERS` / `isSmsTransportProvider`, with `SmsProviderTag` derived from the array, so the CLI reads the vocabulary rather than restating it (#5094). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 22 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
该页此前只把 OS_SMS_PROVIDER 描述成 settings 的 env 覆盖,读者无从得知它还会在 `os serve` 组装 kernel 时(settings 服务尚不存在)被读一次 —— 而新的启动期拒绝正 发生在那一次。补上两个读取时刻的区分、被检查的只有 provider tag(凭据仍由该命名空间 在 kernel:ready 提供)、以及 OS_SMS_PROVIDER=log 这个明确的退出方式。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5713
先测定:未知 provider 的失败方向 = 静默降级(分诊指定的第一步)
issue 正文诚实标注「没追下去看插件拿到未知 provider 会怎样」,分诊把它列为第一步,因为它决定严重度。实测(在
origin/main上跑一次性 harness,已删除):resolveInitialTransport调makeSmsTransport,后者对未知 tag 抛unknown provider 'twilo',但那个 throw 被插件自己 catch 掉、降级成LogSmsTransport,只留一行warn。于是手机验证码登录会回「已发送」,用户永远等不到短信。按 issue 自己给的判据 ——「如果它静默降级,那就是 #5204 的同级缺陷」—— 本条是同级缺陷,不是消息质量问题。
但前提只成立一半:邮件那扇门早就关着
issue 说「两处都没有拿 provider 去比对任何 options 表」。字面属实(两处都不读 settings 的 options 表),但邮件臂并没有缺少校验:
resolveEmailCapabilityArg从@objectstack/plugin-email读EMAIL_TRANSPORT_PROVIDERS并对表外 tag 直接抛错(serve.ts:3209,#5132 关的),serve-email-capability.test.ts:133-144已经钉住sendgrid/ses/mailgun都 throw。所以两扇门里只有一扇是开的。本 PR 因此只动 sms 臂,并且把它修成邮件臂现成的形状 —— 而不是给邮件臂再加一层。
改法:同一份 provider 注册表,启动期响亮拒绝
分诊允许的两条路里选了「provider 注册表」那条,完全绕开
packages/services/service-settings(边界规则:未改该包任何文件)。@objectstack/service-sms导出词汇表(transports/index.ts):新增SMS_TRANSPORT_PROVIDERS与isSmsTransportProvider(),并把SmsProviderTag从数组派生,而不再是并列的第二份字面量。
makeSmsTransport的switch对它穷举。这正是邮件侧
EMAIL_TRANSPORT_PROVIDERS的对应物 —— 一份词汇表两个消费方,「两份字面量描述一个词汇表」就是 plugin-email: SendGrid / Amazon SES 设置项同样后端无实现 —— #5087 的同形缺口 #5094 里 settings 下拉框和 transports 走散的原因。
serve.ts抽出resolveSmsCapabilityArg(cfgSms, env)(镜像resolveEmailCapabilityArg),表外 tag 抛错。capability loop 原样接住:app 显式requires: ['sms']时是硬启动失败,否则console.error+ 不注册 sms 服务 ——与邮件臂在同一个循环里的行为逐字一致。
刻意不做的两件事
smssettings命名空间在
kernel:ready绑定,那是它们被文档承认的家。裸的OS_SMS_PROVIDER=twilio(密钥存在 Settings 里)是一份完整配置,原样放行。被拒绝的只有 settings 永远修不好的那一样东西:表外的 provider tag。
绑定还能换上可用 transport),也仍然是自建宿主的最后一道防线。本 PR 只是让
os serve不再喂给它一个它永远用不了的输入。为什么 #5204 / #5131 那三道闸门看不到这条路
sms命名空间把provider声明为带 options 表的select;#5131 在写入路径上执行该表;#5204 关掉了SettingsService的 env 覆盖分支。三道闸门都在SettingsService后面 —— 而这次读取发生在组装 kernel 时,settings 服务还不存在。所以这是同一形状(declared ≠ enforced)在更早生命周期阶段的另一扇门,正如 issue 判断的那样。反向验证(方向:红,即常规方向)
先声明预期方向再跑:本改动新增一条拒绝,所以把守卫拆掉后,新增的拒绝类断言应当转红,放行类断言应当保持绿。把
resolveSmsCapabilityArg里的守卫临时短路(还原改动前的裸读)后实测:3 条拒绝钉子全红、5 条放行钉子全绿 —— 与预测一致。脚手架已完整还原(
grep REVERSE-VERIFICATION= 0 命中)。测试
packages/cli/src/commands/serve-sms-capability.test.ts(8 例):默认log、显式
log仍能启动(这是拒绝其余取值的前提)、env 压过 config、大小写归一化、三个合法 tag 无凭据放行、
providerOptions/retries透传(含retries: 0)、env 与
config.sms.provider两个声明渠道各自的拒绝、以及错误消息枚举的就是导出的词汇表。packages/services/service-sms/src/transports/transports.test.ts增 2 例:词汇表恰好是makeSmsTransport能建的那三个(逐个真建一遍),以及isSmsTransportProvider对
twilo/ 非字符串 /LOG的判定。@objectstack/cli84 files / 833 tests 全绿;@objectstack/service-sms3 files / 30 tests 全绿;两包
typecheck、build、eslint 均通过;check:nul-bytes/check:service-providers/check:durability-log-level/check:startup-registry-verdict/check:type-check-coverage/check:published-files全绿。Changeset
@objectstack/cli: major(一种配置的破坏性变更)+@objectstack/service-sms: minor(新导出),与 #5132 邮件同形改动的 changeset 取同一档位。Generated by Claude Code