mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat(providers): show API key link in Claude Desktop, OpenClaw and Hermes forms
The "Get API Key" link (and partner promotion) below the API key input was only wired for claude/codex/gemini/opencode. Claude Desktop uses a separate form that never rendered it, and OpenClaw/Hermes were blocked by two gaps: - useApiKeyLink whitelisted only 4 app ids, so the link was suppressed for claude-desktop/openclaw/hermes even when a preset carried apiKeyUrl. - useProviderCategory only parsed claude/codex/gemini/opencode preset ids, so OpenClaw/Hermes category stayed undefined and the link condition (cn_official/aggregator/third_party) never held. Changes: - ClaudeDesktopProviderForm: call useApiKeyLink and replace the bare API key Input with the shared ApiKeySection. - useApiKeyLink: add claude-desktop/openclaw/hermes to the whitelist and add ClaudeDesktopProviderPreset to the PresetEntry union. - useProviderCategory: parse and resolve openclaw/hermes preset categories. - Hermes/OpenClaw form fields: don't let an "official" category disable the API key input, since these apps have no OAuth-only official providers (e.g. Hermes' Nous Research is "official" but still needs a user key).
This commit is contained in:
@@ -39,9 +39,11 @@ import { Switch } from "@/components/ui/switch";
|
||||
import { BasicFormFields } from "./BasicFormFields";
|
||||
import { CodexOAuthSection } from "./CodexOAuthSection";
|
||||
import { CopilotAuthSection } from "./CopilotAuthSection";
|
||||
import { ApiKeySection } from "./shared/ApiKeySection";
|
||||
import { EndpointField } from "./shared/EndpointField";
|
||||
import { ModelDropdown } from "./shared/ModelDropdown";
|
||||
import { ProviderPresetSelector } from "./ProviderPresetSelector";
|
||||
import { useApiKeyLink } from "./hooks/useApiKeyLink";
|
||||
import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider";
|
||||
import type {
|
||||
ClaudeApiFormat,
|
||||
@@ -383,6 +385,21 @@ export function ClaudeDesktopProviderForm({
|
||||
activeProviderType === "github_copilot" ||
|
||||
activeProviderType === "codex_oauth";
|
||||
|
||||
// API Key 获取/邀请链接(与 Claude Code 表单同款,见 ClaudeFormFields)
|
||||
const apiKeyLinkCategory = activePreset?.category ?? initialData?.category;
|
||||
const {
|
||||
shouldShowApiKeyLink,
|
||||
websiteUrl: apiKeyLinkWebsiteUrl,
|
||||
isPartner: apiKeyLinkIsPartner,
|
||||
partnerPromotionKey: apiKeyLinkPromotionKey,
|
||||
} = useApiKeyLink({
|
||||
appId: "claude-desktop",
|
||||
category: apiKeyLinkCategory,
|
||||
selectedPresetId,
|
||||
presetEntries,
|
||||
formWebsiteUrl: form.watch("websiteUrl") || "",
|
||||
});
|
||||
|
||||
const applyDesktopPreset = (preset: ClaudeDesktopProviderPreset) => {
|
||||
form.setValue("name", preset.nameKey ? t(preset.nameKey) : preset.name);
|
||||
form.setValue("websiteUrl", preset.websiteUrl);
|
||||
@@ -766,15 +783,15 @@ export function ClaudeDesktopProviderForm({
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-1">
|
||||
<Label>{"API Key"}</Label>
|
||||
<Input
|
||||
value={apiKey}
|
||||
onChange={(event) => setApiKey(event.target.value)}
|
||||
type="password"
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</div>
|
||||
<ApiKeySection
|
||||
value={apiKey}
|
||||
onChange={setApiKey}
|
||||
category={apiKeyLinkCategory}
|
||||
shouldShowLink={shouldShowApiKeyLink}
|
||||
websiteUrl={apiKeyLinkWebsiteUrl}
|
||||
isPartner={apiKeyLinkIsPartner}
|
||||
partnerPromotionKey={apiKeyLinkPromotionKey}
|
||||
/>
|
||||
)}
|
||||
|
||||
<EndpointField
|
||||
|
||||
@@ -330,7 +330,9 @@ export function HermesFormFields({
|
||||
<ApiKeySection
|
||||
value={apiKey}
|
||||
onChange={onApiKeyChange}
|
||||
category={category}
|
||||
// Hermes 没有 OAuth-only 的免 key 官方供应商:即便是 official 预设
|
||||
// (如 Nous Research)也需用户自填 key,故不让 official 禁用输入框。
|
||||
category={category === "official" ? undefined : category}
|
||||
shouldShowLink={shouldShowApiKeyLink}
|
||||
websiteUrl={websiteUrl}
|
||||
isPartner={isPartner}
|
||||
|
||||
@@ -257,7 +257,9 @@ export function OpenClawFormFields({
|
||||
<ApiKeySection
|
||||
value={apiKey}
|
||||
onChange={onApiKeyChange}
|
||||
category={category}
|
||||
// OpenClaw 的 API key 始终由用户自填,没有 OAuth-only 的免 key 官方供应商,
|
||||
// 故不让 official 禁用输入框(与 Hermes 对齐)。
|
||||
category={category === "official" ? undefined : category}
|
||||
shouldShowLink={shouldShowApiKeyLink}
|
||||
websiteUrl={websiteUrl}
|
||||
isPartner={isPartner}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ProviderPreset } from "@/config/claudeProviderPresets";
|
||||
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
|
||||
import type { GeminiProviderPreset } from "@/config/geminiProviderPresets";
|
||||
import type { OpenCodeProviderPreset } from "@/config/opencodeProviderPresets";
|
||||
import type { ClaudeDesktopProviderPreset } from "@/config/claudeDesktopProviderPresets";
|
||||
|
||||
type PresetEntry = {
|
||||
id: string;
|
||||
@@ -12,7 +13,8 @@ type PresetEntry = {
|
||||
| ProviderPreset
|
||||
| CodexProviderPreset
|
||||
| GeminiProviderPreset
|
||||
| OpenCodeProviderPreset;
|
||||
| OpenCodeProviderPreset
|
||||
| ClaudeDesktopProviderPreset;
|
||||
};
|
||||
|
||||
interface UseApiKeyLinkProps {
|
||||
@@ -80,9 +82,12 @@ export function useApiKeyLink({
|
||||
return {
|
||||
shouldShowApiKeyLink:
|
||||
appId === "claude" ||
|
||||
appId === "claude-desktop" ||
|
||||
appId === "codex" ||
|
||||
appId === "gemini" ||
|
||||
appId === "opencode"
|
||||
appId === "opencode" ||
|
||||
appId === "openclaw" ||
|
||||
appId === "hermes"
|
||||
? shouldShowApiKeyLink
|
||||
: false,
|
||||
websiteUrl: getWebsiteUrl,
|
||||
|
||||
@@ -5,6 +5,8 @@ import { providerPresets } from "@/config/claudeProviderPresets";
|
||||
import { codexProviderPresets } from "@/config/codexProviderPresets";
|
||||
import { geminiProviderPresets } from "@/config/geminiProviderPresets";
|
||||
import { opencodeProviderPresets } from "@/config/opencodeProviderPresets";
|
||||
import { openclawProviderPresets } from "@/config/openclawProviderPresets";
|
||||
import { hermesProviderPresets } from "@/config/hermesProviderPresets";
|
||||
|
||||
interface UseProviderCategoryProps {
|
||||
appId: AppId;
|
||||
@@ -44,7 +46,7 @@ export function useProviderCategory({
|
||||
|
||||
// 从预设 ID 提取索引
|
||||
const match = selectedPresetId.match(
|
||||
/^(claude|codex|gemini|opencode)-(\d+)$/,
|
||||
/^(claude|codex|gemini|opencode|openclaw|hermes)-(\d+)$/,
|
||||
);
|
||||
if (!match) return;
|
||||
|
||||
@@ -75,6 +77,16 @@ export function useProviderCategory({
|
||||
if (preset) {
|
||||
setCategory(preset.category || undefined);
|
||||
}
|
||||
} else if (type === "openclaw" && appId === "openclaw") {
|
||||
const preset = openclawProviderPresets[index];
|
||||
if (preset) {
|
||||
setCategory(preset.category || undefined);
|
||||
}
|
||||
} else if (type === "hermes" && appId === "hermes") {
|
||||
const preset = hermesProviderPresets[index];
|
||||
if (preset) {
|
||||
setCategory(preset.category || undefined);
|
||||
}
|
||||
}
|
||||
}, [appId, selectedPresetId, isEditMode, initialCategory]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user