| 1 | import { PROVIDER_DISPLAY_NAMES, type ServerProvider } from "@t3tools/contracts"; |
| 2 | import { compareSemverVersions } from "@t3tools/shared/semver"; |
| 3 | |
| 4 | // Name the thing the user updates; "Claude" alone reads like the app or model. |
| 5 | const RUNTIME_NAMES: Partial<Record<string, string>> = { |
| 6 | claudeAgent: "Claude Code", |
| 7 | codex: "the Codex CLI", |
| 8 | }; |
| 9 | |
| 10 | function formatModelList(names: ReadonlyArray<string>): string { |
| 11 | if (names.length <= 2) return names.join(" and "); |
| 12 | return `${names.slice(0, -1).join(", ")}, and ${names[names.length - 1]}`; |
| 13 | } |
| 14 | |
| 15 | /** |
| 16 | * Model picker notice for models the manifest announces but the installed |
| 17 | * provider is too old to run, e.g. "Update Claude Code to v2.1.300 or newer to |
| 18 | * use Claude Opus 6." With a search query, only gated models it matches are |
| 19 | * named, so searching for one explains why it is missing. Null when nothing |
| 20 | * gated matches. |
| 21 | */ |
| 22 | export function formatProviderUpdateRequiredNotice( |
| 23 | provider: Pick<ServerProvider, "driver" | "updateRequiredModels">, |
| 24 | searchQuery = "", |
| 25 | ): string | null { |
| 26 | const query = searchQuery.trim().toLocaleLowerCase(); |
| 27 | const models = (provider.updateRequiredModels ?? []).filter( |
| 28 | (model) => |
| 29 | query.length === 0 || |
| 30 | model.name.toLocaleLowerCase().includes(query) || |
| 31 | model.slug.toLocaleLowerCase().includes(query), |
| 32 | ); |
| 33 | if (models.length === 0) return null; |
| 34 | // The highest bar unlocks every listed model. |
| 35 | const minVersion = models |
| 36 | .map((model) => model.minVersion) |
| 37 | .reduce((highest, version) => |
| 38 | compareSemverVersions(version, highest) > 0 ? version : highest, |
| 39 | ); |
| 40 | const providerName = |
| 41 | RUNTIME_NAMES[provider.driver] ?? PROVIDER_DISPLAY_NAMES[provider.driver] ?? provider.driver; |
| 42 | const version = minVersion.startsWith("v") ? minVersion : `v${minVersion}`; |
| 43 | const names = formatModelList(models.map((model) => model.name)); |
| 44 | return `Update ${providerName} to ${version} or newer to use ${names}.`; |
| 45 | } |