mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat(openclaw): add providerKey input field with validation
- Add providerKey input field in ProviderForm for OpenClaw - Implement duplicate key detection by querying existing providers - Add format validation (lowercase letters, numbers, hyphens only) - Disable field in edit mode (key cannot be changed after creation) - Update mutations.ts to support OpenClaw providerKey - Add i18n translations for zh/en/ja
This commit is contained in:
@@ -871,6 +871,24 @@ export function ProviderForm({
|
||||
return providerId || "";
|
||||
});
|
||||
|
||||
// OpenClaw: query existing providers for duplicate key checking
|
||||
const { data: openclawProvidersData } = useProvidersQuery("openclaw");
|
||||
const existingOpenclawKeys = useMemo(() => {
|
||||
if (!openclawProvidersData?.providers) return [];
|
||||
// Exclude current provider ID when in edit mode
|
||||
return Object.keys(openclawProvidersData.providers).filter(
|
||||
(k) => k !== providerId,
|
||||
);
|
||||
}, [openclawProvidersData?.providers, providerId]);
|
||||
|
||||
// OpenClaw Provider Key state
|
||||
const [openclawProviderKey, setOpenclawProviderKey] = useState<string>(() => {
|
||||
if (appId !== "openclaw") return "";
|
||||
// In edit mode, use the existing provider ID as the key
|
||||
return providerId || "";
|
||||
});
|
||||
|
||||
// OpenCode 配置状态
|
||||
const [opencodeNpm, setOpencodeNpm] = useState<string>(() => {
|
||||
if (appId !== "opencode") return OPENCODE_DEFAULT_NPM;
|
||||
return initialOpencodeConfig?.npm || OPENCODE_DEFAULT_NPM;
|
||||
@@ -1261,6 +1279,24 @@ export function ProviderForm({
|
||||
}
|
||||
}
|
||||
|
||||
// OpenClaw: validate provider key
|
||||
if (appId === "openclaw") {
|
||||
const keyPattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
||||
if (!openclawProviderKey.trim()) {
|
||||
toast.error(t("openclaw.providerKeyRequired"));
|
||||
return;
|
||||
}
|
||||
if (!keyPattern.test(openclawProviderKey)) {
|
||||
toast.error(t("openclaw.providerKeyInvalid"));
|
||||
return;
|
||||
}
|
||||
if (!isEditMode && existingOpenclawKeys.includes(openclawProviderKey)) {
|
||||
toast.error(t("openclaw.providerKeyDuplicate"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 非官方供应商必填校验:端点和 API Key
|
||||
if (category !== "official") {
|
||||
if (appId === "claude") {
|
||||
if (!baseUrl.trim()) {
|
||||
@@ -1394,6 +1430,8 @@ export function ProviderForm({
|
||||
} else {
|
||||
payload.providerKey = opencodeProviderKey;
|
||||
}
|
||||
} else if (appId === "openclaw") {
|
||||
payload.providerKey = openclawProviderKey;
|
||||
}
|
||||
|
||||
if (category === "omo" && !payload.presetCategory) {
|
||||
@@ -1594,6 +1632,7 @@ export function ProviderForm({
|
||||
}
|
||||
// OpenClaw 自定义模式:重置为空配置
|
||||
if (appId === "openclaw") {
|
||||
setOpenclawProviderKey("");
|
||||
setOpenclawBaseUrl("");
|
||||
setOpenclawApiKey("");
|
||||
setOpenclawApi("openai-completions");
|
||||
@@ -1687,6 +1726,9 @@ export function ProviderForm({
|
||||
const preset = entry.preset as OpenClawProviderPreset;
|
||||
const config = preset.settingsConfig;
|
||||
|
||||
// Clear provider key (user must enter their own unique key)
|
||||
setOpenclawProviderKey("");
|
||||
|
||||
// Update OpenClaw-specific states
|
||||
setOpenclawBaseUrl(config.baseUrl || "");
|
||||
setOpenclawApiKey(config.apiKey || "");
|
||||
@@ -1808,6 +1850,54 @@ export function ProviderForm({
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : appId === "openclaw" ? (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="openclaw-key">
|
||||
{t("openclaw.providerKey")}
|
||||
<span className="text-destructive ml-1">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="openclaw-key"
|
||||
value={openclawProviderKey}
|
||||
onChange={(e) =>
|
||||
setOpenclawProviderKey(
|
||||
e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ""),
|
||||
)
|
||||
}
|
||||
placeholder={t("openclaw.providerKeyPlaceholder")}
|
||||
disabled={isEditMode}
|
||||
className={
|
||||
(existingOpenclawKeys.includes(openclawProviderKey) &&
|
||||
!isEditMode) ||
|
||||
(openclawProviderKey.trim() !== "" &&
|
||||
!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(openclawProviderKey))
|
||||
? "border-destructive"
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
{existingOpenclawKeys.includes(openclawProviderKey) &&
|
||||
!isEditMode && (
|
||||
<p className="text-xs text-destructive">
|
||||
{t("openclaw.providerKeyDuplicate")}
|
||||
</p>
|
||||
)}
|
||||
{openclawProviderKey.trim() !== "" &&
|
||||
!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(openclawProviderKey) && (
|
||||
<p className="text-xs text-destructive">
|
||||
{t("openclaw.providerKeyInvalid")}
|
||||
</p>
|
||||
)}
|
||||
{!(
|
||||
existingOpenclawKeys.includes(openclawProviderKey) &&
|
||||
!isEditMode
|
||||
) &&
|
||||
(openclawProviderKey.trim() === "" ||
|
||||
/^[a-z0-9]+(-[a-z0-9]+)*$/.test(openclawProviderKey)) && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("openclaw.providerKeyHint")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -732,6 +732,14 @@
|
||||
"modelOptionKeyPlaceholder": "provider",
|
||||
"modelOptionValuePlaceholder": "{\"order\": [\"baseten\"]}"
|
||||
},
|
||||
"openclaw": {
|
||||
"providerKey": "Provider Key",
|
||||
"providerKeyPlaceholder": "my-provider",
|
||||
"providerKeyHint": "Unique identifier in config file. Cannot be changed after creation. Use lowercase letters, numbers, and hyphens only.",
|
||||
"providerKeyRequired": "Provider key is required",
|
||||
"providerKeyDuplicate": "This key is already in use",
|
||||
"providerKeyInvalid": "Invalid format. Use lowercase letters, numbers, and hyphens only."
|
||||
},
|
||||
"providerPreset": {
|
||||
"label": "Provider Preset",
|
||||
"custom": "Custom Configuration",
|
||||
|
||||
@@ -732,6 +732,14 @@
|
||||
"modelOptionKeyPlaceholder": "provider",
|
||||
"modelOptionValuePlaceholder": "{\"order\": [\"baseten\"]}"
|
||||
},
|
||||
"openclaw": {
|
||||
"providerKey": "プロバイダーキー",
|
||||
"providerKeyPlaceholder": "my-provider",
|
||||
"providerKeyHint": "設定ファイル内のユニーク識別子。作成後は変更できません。小文字、数字、ハイフンのみ使用可能。",
|
||||
"providerKeyRequired": "プロバイダーキーを入力してください",
|
||||
"providerKeyDuplicate": "このキーは既に使用されています",
|
||||
"providerKeyInvalid": "無効な形式です。小文字、数字、ハイフンのみ使用可能。"
|
||||
},
|
||||
"providerPreset": {
|
||||
"label": "プロバイダータイプ",
|
||||
"custom": "カスタム設定",
|
||||
|
||||
@@ -732,6 +732,14 @@
|
||||
"modelOptionKeyPlaceholder": "provider",
|
||||
"modelOptionValuePlaceholder": "{\"order\": [\"baseten\"]}"
|
||||
},
|
||||
"openclaw": {
|
||||
"providerKey": "供应商标识",
|
||||
"providerKeyPlaceholder": "my-provider",
|
||||
"providerKeyHint": "配置文件中的唯一标识符,创建后无法修改,只能使用小写字母、数字和连字符",
|
||||
"providerKeyRequired": "请填写供应商标识",
|
||||
"providerKeyDuplicate": "此标识已被使用,请更换",
|
||||
"providerKeyInvalid": "标识格式无效,只能使用小写字母、数字和连字符"
|
||||
},
|
||||
"providerPreset": {
|
||||
"label": "预设供应商",
|
||||
"custom": "自定义配置",
|
||||
|
||||
@@ -16,12 +16,12 @@ export const useAddProviderMutation = (appId: AppId) => {
|
||||
) => {
|
||||
let id: string;
|
||||
|
||||
if (appId === "opencode") {
|
||||
if (appId === "opencode" || appId === "openclaw") {
|
||||
if (providerInput.category === "omo") {
|
||||
id = `omo-${generateUUID()}`;
|
||||
} else {
|
||||
if (!providerInput.providerKey) {
|
||||
throw new Error("Provider key is required for OpenCode");
|
||||
throw new Error(`Provider key is required for ${appId}`);
|
||||
}
|
||||
id = providerInput.providerKey;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user