1 line
41 KiB
JSON
1 line
41 KiB
JSON
{"repo": "chaunyzhang/hiveward", "files": {"apps/api/src/store/openClawConfigWizard.ts": {"c851bd96eee4ad77152e8f8d2caf536c7d177486": {"t": 1785674145.7999642, "n": 38912, "h": "5c5eafc2f19c1190", "c": "import type {\n ConfigureOpenClawChannelRequest,\n ConfigureOpenClawModelAuthRequest,\n CreateOpenClawChannelRequest,\n CreateOpenClawModelRequest,\n OpenClawChannelSetupOption,\n OpenClawConfigWizardMetadata,\n OpenClawModelAuthMethodOption,\n OpenClawModelAuthProviderOption,\n OpenClawWizardField,\n OpenClawWizardValue\n} from \"@hiveward/shared\";\n\ntype ModelProviderDefinition = Omit<OpenClawModelAuthProviderOption, \"methods\"> & {\n methods: ModelMethodDefinition[];\n};\n\ntype ModelMethodDefinition = OpenClawModelAuthMethodOption & {\n write: {\n providerId?: string;\n providerIdField?: string;\n api?: string;\n baseUrl?: string;\n baseUrlTemplate?: (values: Record<string, OpenClawWizardValue>) => string | undefined;\n apiKeyMarker?: string;\n defaultEnv?: string;\n };\n};\n\nconst DEFAULT_MODEL_BY_PROVIDER: Record<string, string> = {\n anthropic: \"claude-sonnet-4-6\",\n arcee: \"spotlight\",\n byteplus: \"doubao-seed-1-6\",\n cerebras: \"llama-3.3-70b\",\n chutes: \"deepseek-ai/DeepSeek-V3.1\",\n cloudflare: \"@cf/meta/llama-3.1-8b-instruct\",\n codex: \"gpt-5.5\",\n \"codex-cli\": \"gpt-5.5\",\n deepinfra: \"deepseek-ai/DeepSeek-V3.1\",\n deepseek: \"deepseek-v4-pro\",\n fireworks: \"accounts/fireworks/models/deepseek-v3\",\n google: \"gemini-2.5-pro\",\n \"google-vertex\": \"gemini-2.5-pro\",\n groq: \"llama-3.3-70b-versatile\",\n huggingface: \"meta-llama/Llama-3.1-8B-Instruct\",\n kilocode: \"anthropic/claude-sonnet-4.5\",\n litellm: \"gpt-4.1-mini\",\n lmstudio: \"local-model\",\n \"microsoft-foundry\": \"deployment-name\",\n minimax: \"MiniMax-M2.7\",\n \"minimax-portal\": \"MiniMax-M2.7\",\n mistral: \"mistral-medium-3-5\",\n moonshot: \"kimi-k2.6\",\n nvidia: \"meta/llama-3.1-70b-instruct\",\n ollama: \"llama3.2\",\n openai: \"gpt-5.5\",\n \"openai-codex\": \"gpt-5.5\",\n opencode: \"qwen/qwen3-coder\",\n openrouter: \"openai/gpt-5.5\",\n qianfan: \"ernie-4.5-turbo\",\n qwen: \"qwen3-coder-plus\",\n sglang: \"local-model\",\n stepfun: \"step-3.5-flash\",\n \"stepfun-plan\": \"step-3.5-flash\",\n synthetic: \"claude-sonnet-4-5\",\n \"tencent-tokenhub\": \"hunyuan-t1-latest\",\n together: \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n venice: \"llama-3.3-70b\",\n \"vercel-ai-gateway\": \"openai/gpt-5.5\",\n vllm: \"local-model\",\n volcengine: \"doubao-seed-1-6\",\n xai: \"grok-4.3\",\n xiaomi: \"mimo-v2.5-pro\",\n zai: \"glm-5.1\"\n};\n\nconst OPENAI_MODEL_OPTIONS = [\n \"gpt-5.5\",\n \"gpt-5.4\",\n \"gpt-5.4-mini\",\n \"gpt-5.4-nano\"\n];\n\nconst GEMINI_MODEL_OPTIONS = [\n \"gemini-3.1-pro-preview\",\n \"gemini-3.1-pro-preview-customtools\",\n \"gemini-3.5-flash\",\n \"gemini-3-flash-preview\",\n \"gemini-3.1-flash-lite\",\n \"gemini-2.5-pro\",\n \"gemini-2.5-flash\",\n \"gemini-2.5-flash-lite\"\n];\n\nconst CLAUDE_MODEL_OPTIONS = [\n \"claude-opus-4-8\",\n \"claude-opus-4-7\",\n \"claude-opus-4-6\",\n \"claude-sonnet-4-6\",\n \"claude-opus-4-5\",\n \"claude-sonnet-4-5\",\n \"claude-haiku-4-5\",\n \"claude-haiku-4-5-20251001\"\n];\n\nconst QWEN_MODEL_OPTIONS = [\n \"qwen3.7-max\",\n \"qwen3.7-max-preview\",\n \"qwen3.6-plus\",\n \"qwen3.6-flash\",\n \"qwen3.5-plus\",\n \"qwen3.5-flash\",\n \"qwen3-coder-next\",\n \"qwen3-coder-plus\",\n \"qwen3-coder-flash\",\n \"qwen3-coder-480b-a35b-instruct\",\n \"qwen3-max\",\n \"qwen3-max-preview\",\n \"qwen3-235b-a22b\",\n \"qwen3-next-80b-a3b-instruct\",\n \"qwen3-next-80b-a3b-thinking\"\n];\n\n// Curated from provider-published model IDs; the UI still merges OpenClaw's runtime catalog for account-specific models.\nconst MODEL_OPTIONS_BY_PROVIDER: Record<string, string[]> = {\n anthropic: CLAUDE_MODEL_OPTIONS,\n codex: OPENAI_MODEL_OPTIONS,\n \"codex-cli\": OPENAI_MODEL_OPTIONS,\n cerebras: [\n \"qwen-3-235b-a22b-instruct-2507\",\n \"qwen-3-32b\",\n \"llama3.3-70b\",\n \"llama3.1-8b\"\n ],\n deepseek: [\n \"deepseek-v4-pro\",\n \"deepseek-v4-flash\"\n ],\n google: GEMINI_MODEL_OPTIONS,\n \"google-vertex\": GEMINI_MODEL_OPTIONS,\n groq: [\n \"llama-3.3-70b-versatile\",\n \"llama-3.1-8b-instant\",\n \"openai/gpt-oss-120b\",\n \"openai/gpt-oss-20b\",\n \"meta-llama/llama-4-scout-17b-16e-instruct\",\n \"qwen/qwen3-32b\",\n \"groq/compound\",\n \"groq/compound-mini\"\n ],\n minimax: [\n \"MiniMax-M2.7\",\n \"MiniMax-M2.7-highspeed\",\n \"MiniMax-M2.5\",\n \"MiniMax-M2.5-highspeed\",\n \"MiniMax-M2.1\",\n \"MiniMax-M2.1-highspeed\",\n \"MiniMax-M2\",\n \"M2-her\"\n ],\n \"minimax-portal\": [\n \"MiniMax-M2.7\",\n \"MiniMax-M2.7-highspeed\",\n \"MiniMax-M2.5\",\n \"MiniMax-M2.5-highspeed\",\n \"MiniMax-M2.1\",\n \"MiniMax-M2.1-highspeed\",\n \"MiniMax-M2\",\n \"M2-her\"\n ],\n moonshot: [\n \"kimi-k2.6\",\n \"kimi-k2.5\",\n \"moonshot-v1-8k\",\n \"moonshot-v1-32k\",\n \"moonshot-v1-128k\",\n \"moonshot-v1-8k-vision-preview\",\n \"moonshot-v1-32k-vision-preview\",\n \"moonshot-v1-128k-vision-preview\"\n ],\n openai: OPENAI_MODEL_OPTIONS,\n \"openai-codex\": OPENAI_MODEL_OPTIONS,\n mistral: [\n \"mistral-medium-3-5\",\n \"mistral-large-3\",\n \"mistral-medium-3-1\",\n \"mistral-small-4\",\n \"ministral-14b-latest\",\n \"ministral-8b-latest\",\n \"magistral-medium-latest\",\n \"magistral-small-latest\",\n \"devstral-medium-latest\",\n \"devstral-small-latest\",\n \"codestral-latest\",\n \"mistral-nemo-latest\"\n ],\n qwen: QWEN_MODEL_OPTIONS,\n stepfun: [\n \"step-3.5-flash-2603\",\n \"step-3.5-flash\",\n \"step-2-mini\",\n \"step-2\",\n \"step-2-16k-exp\",\n \"step-1\"\n ],\n \"stepfun-plan\": [\n \"step-3.5-flash-2603\",\n \"step-3.5-flash\"\n ],\n synthetic: CLAUDE_MODEL_OPTIONS,\n xai: [\n \"grok-4.3\",\n \"grok-4-0709\",\n \"grok-3\",\n \"grok-3-mini\",\n \"grok-2-vision-1212\"\n ],\n xiaomi: [\n \"mimo-v2.5-pro\",\n \"mimo-v2.5\",\n \"mimo-v2-pro\",\n \"mimo-v2-omni\",\n \"mimo-v2-flash\"\n ],\n zai: [\n \"glm-5.1\",\n \"glm-5-turbo\",\n \"glm-5\",\n \"glm-4.7\",\n \"glm-4.7-flash\",\n \"glm-4.7-flashx\",\n \"glm-4.6\",\n \"glm-4.5-air\",\n \"glm-4.5-airx\",\n \"glm-4.5-flash\",\n \"glm-4-flash-250414\",\n \"glm-4-flashx-250414\"\n ]\n};\n\nconst MODEL_API_OPENAI = \"openai-responses\";\nconst MODEL_API_OPENAI_CODEX = \"openai-codex-responses\";\nconst MODEL_API_ANTHROPIC = \"anthropic-messages\";\nconst MODEL_API_GOOGLE = \"google-generative-ai\";\nconst MODEL_API_COPILOT = \"github-copilot\";\nconst MODEL_API_OLLAMA = \"ollama\";\nconst MODEL_API_AZURE = \"azure-openai-responses\";\nconst OPENAI_RESPONSES_BASE_URL = \"https://api.openai.com/v1\";\nconst OPENAI_CODEX_RESPONSES_BASE_URL = \"https://chatgpt.com/backend-api/codex\";\n\nconst apiKeyField: OpenClawWizardField = {\n id: \"apiKey\",\n label: \"API Key\",\n type: \"password\",\n required: true,\n placeholder: \"sk-...\"\n};\n\nconst modelBaseFields = (modelId = \"default\", providerId?: string): OpenClawWizardField[] => {\n const options = modelOptionsForProvider(providerId, modelId);\n return [{\n id: \"modelId\",\n label: \"Model\",\n type: \"select\",\n required: true,\n defaultValue: options.some((option) => option.value === modelId) ? modelId : options[0]?.value ?? modelId,\n options\n }];\n};\n\nconst apiKeyFields = (_defaultEnv: string, modelId?: string, extraFields: OpenClawWizardField[] = [], providerId?: string): OpenClawWizardField[] => [\n ...modelBaseFields(modelId, providerId),\n apiKeyField,\n ...extraFields\n];\n\nconst localFields = (_baseUrl: string, modelPlaceholder?: string): OpenClawWizardField[] => modelBaseFields(modelPlaceholder);\n\nconst tokenFields = (tokenLabel: string, modelPlaceholder?: string, providerId?: string): OpenClawWizardField[] => [\n ...modelBaseFields(modelPlaceholder, providerId),\n {\n id: \"apiKey\",\n label: tokenLabel,\n type: \"password\",\n required: true\n }\n];\n\nconst markerFields = (modelPlaceholder?: string, providerId?: string): OpenClawWizardField[] => modelBaseFields(modelPlaceholder, providerId);\n\nfunction modelOptionsForProvider(providerId: string | undefined, fallbackModelId: string): NonNullable<OpenClawWizardField[\"options\"]> {\n const values = providerId ? MODEL_OPTIONS_BY_PROVIDER[providerId] : undefined;\n const options = (values ?? []).map((value) => ({ value, label: value }));\n if (fallbackModelId && !options.some((option) => option.value === fallbackModelId)) {\n options.unshift({ value: fallbackModelId, label: fallbackModelId });\n }\n return options.length ? options : [{ value: fallbackModelId, label: fallbackModelId }];\n}\n\nfunction apiProvider(params: {\n id: string;\n label: string;\n env: string;\n hint?: string;\n api?: string;\n baseUrl?: string;\n modelPlaceholder?: string;\n methodLabel?: string;\n methodHint?: string;\n choiceId?: string;\n}): ModelProviderDefinition {\n return {\n id: params.id,\n label: params.label,\n hint: params.hint,\n methods: [\n {\n id: \"api-key\",\n label: params.methodLabel ?? `${params.label} API key`,\n hint: params.methodHint ?? \"Direct API key\",\n kind: \"api_key\",\n choiceId: params.choiceId,\n fields: apiKeyFields(params.env, params.modelPlaceholder ?? DEFAULT_MODEL_BY_PROVIDER[params.id], [], params.id),\n write: {\n api: params.api ?? MODEL_API_OPENAI,\n baseUrl: params.baseUrl,\n defaultEnv: params.env\n }\n }\n ]\n };\n}\n\nconst MODEL_PROVIDER_DEFINITIONS: ModelProviderDefinition[] = [\n {\n id: \"anthropic\",\n label: \"Anthropic\",\n hint: \"Claude CLI + API key\",\n methods: [\n {\n id: \"cli\",\n label: \"Anthropic Claude CLI\",\n hint: \"Reuse a local Claude CLI login on this host\",\n kind: \"custom\",\n choiceId: \"anthropic-cli\",\n fields: markerFields(DEFAULT_MODEL_BY_PROVIDER.anthropic, \"anthropic\"),\n write: {\n providerId: \"claude-cli\",\n api: MODEL_API_ANTHROPIC,\n apiKeyMarker: \"oauth:claude-cli\"\n }\n },\n {\n id: \"api-key\",\n label: \"Anthropic API Key\",\n hint: \"Direct Anthropic API key\",\n kind: \"api_key\",\n choiceId: \"apiKey\",\n fields: apiKeyFields(\"ANTHROPIC_API_KEY\", DEFAULT_MODEL_BY_PROVIDER.anthropic, [], \"anthropic\"),\n write: {\n api: MODEL_API_ANTHROPIC,\n defaultEnv: \"ANTHROPIC_API_KEY\"\n }\n },\n {\n id: \"setup-token\",\n label: \"Anthropic setup-token\",\n hint: \"Paste a setup token generated by the Anthropic provider flow\",\n kind: \"token\",\n choiceId: \"setup-token\",\n fields: tokenFields(\"Setup token\", DEFAULT_MODEL_BY_PROVIDER.anthropic, \"anthropic\"),\n write: {\n api: MODEL_API_ANTHROPIC\n }\n }\n ]\n },\n apiProvider({\n id: \"arcee\",\n label: \"Arcee AI\",\n env: \"ARCEEAI_API_KEY\",\n choiceId: \"arceeai-api-key\",\n methodLabel: \"Arcee AI API key\",\n methodHint: \"Direct access to Arcee platform\"\n }),\n apiProvider({ id: \"byteplus\", label: \"BytePlus\", env: \"BYTEPLUS_API_KEY\", choiceId: \"byteplus-api-key\" }),\n apiProvider({\n id: \"cerebras\",\n label: \"Cerebras\",\n env: \"CEREBRAS_API_KEY\",\n choiceId: \"cerebras-api-key\",\n methodHint: \"Fast OpenAI-compatible inference\"\n }),\n {\n id: \"chutes\",\n label: \"Chutes\",\n methods: [\n {\n id: \"oauth\",\n label: \"Chutes OAuth\",\n hint: \"Use an existing Chutes browser sign-in profile\",\n kind: \"oauth\",\n choiceId: \"chutes\",\n fields: markerFields(),\n write: {\n api: MODEL_API_OPENAI,\n apiKeyMarker: \"oauth:chutes\"\n }\n },\n {\n id: \"api-key\",\n label: \"Chutes API key\",\n hint: \"Open-source models including Llama, DeepSeek, and more\",\n kind: \"api_key\",\n choiceId: \"chutes-api-key\",\n fields: apiKeyFields(\"CHUTES_API_KEY\"),\n write: {\n api: MODEL_API_OPENAI,\n defaultEnv: \"CHUTES_API_KEY\"\n }\n }\n ]\n },\n {\n id: \"cloudflare-ai-gateway\",\n label: \"Cloudflare AI Gateway\",\n hint: \"Account ID + Gateway ID + API key\",\n methods: [\n {\n id: \"api-key\",\n label: \"Cloudflare AI Gateway\",\n hint: \"OpenAI-compatible Cloudflare AI Gateway endpoint\",\n kind: \"api_key\",\n choiceId: \"cloudflare-ai-gateway-api-key\",\n fields: apiKeyFields(\"CLOUDFLARE_API_TOKEN\", \"openai/gpt-4o-mini\", [\n {\n id: \"accountId\",\n label: \"Cloudflare Account ID\",\n type: \"text\",\n required: true\n },\n {\n id: \"gatewayId\",\n label: \"Cloudflare AI Gateway ID\",\n type: \"text\",\n required: true\n }\n ], \"openai\"),\n write: {\n api: MODEL_API_OPENAI,\n defaultEnv: \"CLOUDFLARE_API_TOKEN\",\n baseUrlTemplate: (values) => {\n const accountId = readWizardString(values.accountId);\n const gatewayId = readWizardString(values.gatewayId);\n if (!accountId || !gatewayId) return undefined;\n return `https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/openai`;\n }\n }\n }\n ]\n },\n {\n id: \"codex\",\n label: \"Codex\",\n hint: \"Reuse a local Codex CLI login\",\n methods: [\n {\n id: \"codex-cli\",\n label: \"Codex CLI\",\n hint: \"Use an existing Codex CLI auth profile on this host\",\n kind: \"custom\",\n choiceId: \"codex-cli\",\n fields: markerFields(\"gpt-5.5\", \"codex\"),\n write: {\n providerId: \"codex-cli\",\n api: MODEL_API_OPENAI_CODEX,\n baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,\n apiKeyMarker: \"oauth:codex-cli\"\n }\n }\n ]\n },\n {\n id: \"github-copilot\",\n label: \"Copilot\",\n hint: \"GitHub Copilot device login\",\n methods: [\n {\n id: \"device\",\n label: \"GitHub device login\",\n hint: \"Use an existing GitHub Copilot device-code auth profile\",\n kind: \"device_code\",\n choiceId: \"github-copilot\",\n fields: markerFields(\"gpt-5-mini\"),\n write: {\n api: MODEL_API_COPILOT,\n apiKeyMarker: \"oauth:github-copilot\"\n }\n }\n ]\n },\n apiProvider({ id: \"deepinfra\", label: \"DeepInfra\", env: \"DEEPINFRA_API_KEY\", choiceId: \"deepinfra-api-key\" }),\n apiProvider({ id: \"deepseek\", label: \"DeepSeek\", env: \"DEEPSEEK_API_KEY\", choiceId: \"deepseek-api-key\" }),\n apiProvider({ id: \"fireworks\", label: \"Fireworks\", env: \"FIREWORKS_API_KEY\", choiceId: \"fireworks-api-key\" }),\n apiProvider({\n id: \"google\",\n label: \"Google\",\n env: \"GOOGLE_API_KEY\",\n api: MODEL_API_GOOGLE,\n choiceId: \"gemini-api-key\",\n methodLabel: \"Google Gemini API key\",\n methodHint: \"AI Studio / Gemini API key\",\n modelPlaceholder: \"gemini-2.5-pro\"\n }),\n apiProvider({\n id: \"google-vertex\",\n label: \"Google Vertex\",\n env: \"GOOGLE_APPLICATION_CREDENTIALS\",\n api: MODEL_API_GOOGLE,\n choiceId: \"google-vertex-api-key\",\n methodLabel: \"Google Vertex credentials\",\n modelPlaceholder: \"gemini-2.5-pro\"\n }),\n apiProvider({ id: \"groq\", label: \"Groq\", env: \"GROQ_API_KEY\", choiceId: \"groq-api-key\" }),\n apiProvider({\n id: \"huggingface\",\n label: \"Hugging Face\",\n env: \"HUGGINGFACE_API_KEY\",\n choiceId: \"huggingface-api-key\",\n methodHint: \"Inference API (HF token)\"\n }),\n apiProvider({\n id: \"kilocode\",\n label: \"Kilo Gateway\",\n env: \"KILOCODE_API_KEY\",\n choiceId: \"kilocode-api-key\",\n methodHint: \"API key (OpenRouter-compatible)\"\n }),\n apiProvider({ id: \"litellm\", label: \"LiteLLM\", env: \"LITELLM_API_KEY\", choiceId: \"litellm-api-key\" }),\n {\n id: \"lmstudio\",\n label: \"LM Studio\",\n methods: [\n {\n id: \"custom\",\n label: \"LM Studio\",\n hint: \"Local/self-hosted LM Studio server\",\n kind: \"local\",\n fields: localFields(\"http://127.0.0.1:1234/v1\", \"local-model\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: \"http://127.0.0.1:1234/v1\",\n apiKeyMarker: \"lmstudio-local\"\n }\n }\n ]\n },\n {\n id: \"microsoft-foundry\",\n label: \"Microsoft Foundry\",\n methods: [\n {\n id: \"entra-id\",\n label: \"Entra ID (az login)\",\n hint: \"Use an existing Azure login; no API key needed\",\n kind: \"custom\",\n choiceId: \"microsoft-foundry-entra\",\n fields: localFields(\"https://your-resource.openai.azure.com/openai/v1\", \"deployment-name\"),\n write: {\n api: MODEL_API_AZURE,\n apiKeyMarker: \"oauth:microsoft-foundry\"\n }\n },\n {\n id: \"api-key\",\n label: \"Azure OpenAI API key\",\n hint: \"Direct Azure OpenAI API key\",\n kind: \"api_key\",\n choiceId: \"microsoft-foundry-apikey\",\n fields: apiKeyFields(\"AZURE_OPENAI_API_KEY\", \"deployment-name\", [\n {\n id: \"baseUrl\",\n label: \"Azure OpenAI base URL\",\n type: \"text\",\n required: true,\n placeholder: \"https://your-resource.openai.azure.com/openai/v1\"\n }\n ]),\n write: {\n api: MODEL_API_AZURE,\n defaultEnv: \"AZURE_OPENAI_API_KEY\"\n }\n }\n ]\n },\n {\n id: \"minimax\",\n label: \"MiniMax\",\n methods: [\n {\n id: \"api-cn\",\n label: \"MiniMax API Key (CN)\",\n hint: \"CN endpoint - api.minimaxi.com\",\n kind: \"api_key\",\n choiceId: \"minimax-cn-api\",\n fields: apiKeyFields(\"MINIMAX_API_KEY\", DEFAULT_MODEL_BY_PROVIDER.minimax, [], \"minimax\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: \"https://api.minimaxi.com/v1\",\n defaultEnv: \"MINIMAX_API_KEY\"\n }\n },\n {\n id: \"api-global\",\n label: \"MiniMax API Key (Global)\",\n hint: \"Global endpoint - api.minimax.io\",\n kind: \"api_key\",\n choiceId: \"minimax-global-api\",\n fields: apiKeyFields(\"MINIMAX_API_KEY\", DEFAULT_MODEL_BY_PROVIDER.minimax, [], \"minimax\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: \"https://api.minimax.io/v1\",\n defaultEnv: \"MINIMAX_API_KEY\"\n }\n },\n {\n id: \"oauth-cn\",\n label: \"MiniMax OAuth (CN)\",\n hint: \"CN endpoint - api.minimaxi.com\",\n kind: \"device_code\",\n choiceId: \"minimax-cn-oauth\",\n fields: markerFields(DEFAULT_MODEL_BY_PROVIDER.minimax, \"minimax\"),\n write: {\n providerId: \"minimax-portal\",\n api: MODEL_API_OPENAI,\n baseUrl: \"https://api.minimaxi.com/v1\",\n apiKeyMarker: \"minimax-oauth\"\n }\n },\n {\n id: \"oauth-global\",\n label: \"MiniMax OAuth (Global)\",\n hint: \"Global endpoint - api.minimax.io\",\n kind: \"device_code\",\n choiceId: \"minimax-global-oauth\",\n fields: markerFields(DEFAULT_MODEL_BY_PROVIDER.minimax, \"minimax\"),\n write: {\n providerId: \"minimax-portal\",\n api: MODEL_API_OPENAI,\n baseUrl: \"https://api.minimax.io/v1\",\n apiKeyMarker: \"minimax-oauth\"\n }\n }\n ]\n },\n apiProvider({ id: \"mistral\", label: \"Mistral AI\", env: \"MISTRAL_API_KEY\", choiceId: \"mistral-api-key\" }),\n {\n id: \"moonshot\",\n label: \"Moonshot AI (Kimi K2.6)\",\n methods: [\n {\n id: \"api-key\",\n label: \"Kimi API key (.ai)\",\n hint: \"Kimi K2.6 + Kimi\",\n kind: \"api_key\",\n choiceId: \"moonshot-api-key\",\n fields: apiKeyFields(\"MOONSHOT_API_KEY\", DEFAULT_MODEL_BY_PROVIDER.moonshot, [], \"moonshot\"),\n write: {\n api: MODEL_API_OPENAI,\n defaultEnv: \"MOONSHOT_API_KEY\"\n }\n },\n {\n id: \"api-key-cn\",\n label: \"Kimi API key (.cn)\",\n hint: \"Kimi K2.6 + Kimi\",\n kind: \"api_key\",\n choiceId: \"moonshot-api-key-cn\",\n fields: apiKeyFields(\"MOONSHOT_API_KEY\", DEFAULT_MODEL_BY_PROVIDER.moonshot, [], \"moonshot\"),\n write: {\n api: MODEL_API_OPENAI,\n defaultEnv: \"MOONSHOT_API_KEY\"\n }\n }\n ]\n },\n apiProvider({ id: \"nvidia\", label: \"NVIDIA\", env: \"NVIDIA_API_KEY\", choiceId: \"nvidia-api-key\" }),\n {\n id: \"ollama\",\n label: \"Ollama\",\n methods: [\n {\n id: \"local\",\n label: \"Ollama\",\n hint: \"Cloud and local open models\",\n kind: \"local\",\n fields: localFields(\"http://127.0.0.1:11434/v1\", \"llama3.2\"),\n write: {\n api: MODEL_API_OLLAMA,\n baseUrl: \"http://127.0.0.1:11434/v1\",\n apiKeyMarker: \"ollama-local\"\n }\n }\n ]\n },\n {\n id: \"openai\",\n label: \"OpenAI\",\n hint: \"ChatGPT subscription or API key\",\n methods: [\n {\n id: \"chatgpt-login\",\n label: \"ChatGPT Login\",\n hint: \"Sign in with your ChatGPT or Codex subscription\",\n kind: \"oauth\",\n choiceId: \"openai\",\n fields: markerFields(\"gpt-5.5\", \"openai\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: OPENAI_RESPONSES_BASE_URL,\n apiKeyMarker: \"oauth:openai\"\n }\n },\n {\n id: \"device-code\",\n label: \"ChatGPT Device Pairing\",\n hint: \"Pair your ChatGPT account in browser with a device code\",\n kind: \"device_code\",\n choiceId: \"openai-device-code\",\n fields: markerFields(\"gpt-5.5\", \"openai\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: OPENAI_RESPONSES_BASE_URL,\n apiKeyMarker: \"oauth:openai\"\n }\n },\n {\n id: \"api-key\",\n label: \"OpenAI API Key\",\n hint: \"Use your OpenAI API key directly\",\n kind: \"api_key\",\n choiceId: \"openai-api-key\",\n fields: apiKeyFields(\"OPENAI_API_KEY\", \"gpt-5.5\", [], \"openai\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: OPENAI_RESPONSES_BASE_URL,\n defaultEnv: \"OPENAI_API_KEY\"\n }\n }\n ]\n },\n {\n id: \"openai-codex\",\n label: \"OpenAI Codex\",\n hint: \"ChatGPT/Codex sign-in\",\n methods: [\n {\n id: \"oauth\",\n label: \"OpenAI Codex Browser Login\",\n hint: \"Sign in with OpenAI in your browser\",\n kind: \"oauth\",\n choiceId: \"openai-codex\",\n fields: markerFields(\"gpt-5.5\", \"openai-codex\"),\n write: {\n api: MODEL_API_OPENAI_CODEX,\n baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,\n apiKeyMarker: \"oauth:openai-codex\"\n }\n },\n {\n id: \"device-code\",\n label: \"OpenAI Codex Device Pairing\",\n hint: \"Pair in browser with a device code\",\n kind: \"device_code\",\n choiceId: \"openai-codex-device-code\",\n fields: markerFields(\"gpt-5.5\", \"openai-codex\"),\n write: {\n api: MODEL_API_OPENAI_CODEX,\n baseUrl: OPENAI_CODEX_RESPONSES_BASE_URL,\n apiKeyMarker: \"oauth:openai-codex\"\n }\n },\n {\n id: \"api-key\",\n label: \"OpenAI API Key Backup\",\n hint: \"Use an OpenAI API key when your Codex subscription is unavailable\",\n kind: \"api_key\",\n choiceId: \"openai-codex-api-key\",\n fields: apiKeyFields(\"OPENAI_API_KEY\", \"gpt-5.5\", [], \"openai\"),\n write: {\n providerId: \"openai\",\n api: MODEL_API_OPENAI,\n baseUrl: OPENAI_RESPONSES_BASE_URL,\n defaultEnv: \"OPENAI_API_KEY\"\n }\n }\n ]\n },\n apiProvider({\n id: \"opencode\",\n label: \"OpenCode\",\n env: \"OPENCODE_API_KEY\",\n choiceId: \"opencode-zen\",\n methodLabel: \"OpenCode Zen catalog\"\n }),\n apiProvider({ id: \"openrouter\", label: \"OpenRouter\", env: \"OPENROUTER_API_KEY\", choiceId: \"openrouter-api-key\", baseUrl: \"https://openrouter.ai/api/v1\" }),\n apiProvider({ id: \"qianfan\", label: \"Qianfan\", env: \"QIANFAN_API_KEY\", choiceId: \"qianfan-api-key\" }),\n {\n id: \"qwen\",\n label: \"Qwen Cloud\",\n methods: [\n qwenMethod(\"standard-api-key-cn\", \"Standard API Key for China (pay-as-you-go)\", \"qwen-standard-api-key-cn\", \"dashscope.aliyuncs.com\"),\n qwenMethod(\"standard-api-key\", \"Standard API Key for Global/Intl (pay-as-you-go)\", \"qwen-standard-api-key\", \"dashscope-intl.aliyuncs.com\"),\n qwenMethod(\"api-key-cn\", \"Coding Plan API Key for China (subscription)\", \"qwen-api-key-cn\", \"coding.dashscope.aliyuncs.com\"),\n qwenMethod(\"api-key\", \"Coding Plan API Key for Global/Intl (subscription)\", \"qwen-api-key\", \"coding-intl.dashscope.aliyuncs.com\")\n ]\n },\n {\n id: \"sglang\",\n label: \"SGLang\",\n methods: [\n {\n id: \"custom\",\n label: \"SGLang\",\n hint: \"Fast self-hosted OpenAI-compatible server\",\n kind: \"local\",\n fields: localFields(\"http://127.0.0.1:30000/v1\", \"local-model\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: \"http://127.0.0.1:30000/v1\",\n apiKeyMarker: \"custom-local\"\n }\n }\n ]\n },\n {\n id: \"stepfun\",\n label: \"Stepfun\",\n methods: [\n stepfunMethod(\"standard-api-key-cn\", \"StepFun Standard API key (China)\", \"stepfun-standard-api-key-cn\", \"https://api.stepfun.com/v1\", \"stepfun\"),\n stepfunMethod(\"standard-api-key-intl\", \"StepFun Standard API key (Global/Intl)\", \"stepfun-standard-api-key-intl\", \"https://api.stepfun.ai/v1\", \"stepfun\")\n ]\n },\n {\n id: \"stepfun-plan\",\n label: \"Stepfun Plan\",\n methods: [\n stepfunMethod(\"plan-api-key-cn\", \"StepFun Step Plan API key (China)\", \"stepfun-plan-api-key-cn\", \"https://api.stepfun.com/step_plan/v1\", \"stepfun-plan\"),\n stepfunMethod(\"plan-api-key-intl\", \"StepFun Step Plan API key (Global/Intl)\", \"stepfun-plan-api-key-intl\", \"https://api.stepfun.ai/step_plan/v1\", \"stepfun-plan\")\n ]\n },\n apiProvider({\n id: \"synthetic\",\n label: \"Synthetic\",\n env: \"SYNTHETIC_API_KEY\",\n api: MODEL_API_ANTHROPIC,\n choiceId: \"synthetic-api-key\",\n methodHint: \"Anthropic-compatible multi-model provider\"\n }),\n apiProvider({\n id: \"tencent-tokenhub\",\n label: \"Tencent Cloud (Tencent TokenHub)\",\n env: \"TENCENT_TOKENHUB_API_KEY\",\n choiceId: \"tokenhub-api-key\",\n methodLabel: \"Tencent TokenHub\"\n }),\n apiProvider({ id: \"together\", label: \"Together AI\", env: \"TOGETHER_API_KEY\", choiceId: \"together-api-key\" }),\n apiProvider({ id: \"venice\", label: \"Venice AI\", env: \"VENICE_API_KEY\", choiceId: \"venice-api-key\" }),\n apiProvider({ id: \"vercel-ai-gateway\", label: \"Vercel AI Gateway\", env: \"AI_GATEWAY_API_KEY\", choiceId: \"ai-gateway-api-key\" }),\n {\n id: \"vllm\",\n label: \"vLLM\",\n methods: [\n {\n id: \"custom\",\n label: \"vLLM\",\n hint: \"Local/self-hosted OpenAI-compatible server\",\n kind: \"local\",\n fields: localFields(\"http://127.0.0.1:8000/v1\", \"local-model\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: \"http://127.0.0.1:8000/v1\",\n apiKeyMarker: \"custom-local\"\n }\n }\n ]\n },\n apiProvider({ id: \"volcengine\", label: \"Volcengine\", env: \"VOLCENGINE_API_KEY\", choiceId: \"volcengine-api-key\" }),\n apiProvider({ id: \"xai\", label: \"xAI\", env: \"XAI_API_KEY\", choiceId: \"xai-api-key\" }),\n apiProvider({ id: \"xiaomi\", label: \"Xiaomi\", env: \"XIAOMI_API_KEY\", choiceId: \"xiaomi-api-key\" }),\n {\n id: \"zai\",\n label: \"Z.AI\",\n methods: [\n zaiMethod(\"api-key\", \"Z.AI API key\", \"zai-api-key\"),\n zaiMethod(\"coding-global\", \"Coding-Plan-Global\", \"zai-coding-global\", \"https://api.z.ai/api/coding/paas/v4\"),\n zaiMethod(\"coding-cn\", \"Coding-Plan-CN\", \"zai-coding-cn\", \"https://open.bigmodel.cn/api/coding/paas/v4\"),\n zaiMethod(\"global\", \"Global\", \"zai-global\", \"https://api.z.ai/api/paas/v4\"),\n zaiMethod(\"cn\", \"CN\", \"zai-cn\", \"https://open.bigmodel.cn/api/paas/v4\")\n ]\n },\n {\n id: \"custom\",\n label: \"Custom Provider\",\n hint: \"OpenAI-compatible or Anthropic-compatible custom endpoint\",\n methods: [\n {\n id: \"custom-api-key\",\n label: \"Custom Provider API key\",\n hint: \"Write a custom provider entry under models.providers\",\n kind: \"api_key\",\n choiceId: \"custom-api-key\",\n fields: [\n {\n id: \"providerId\",\n label: \"Provider ID\",\n type: \"text\",\n required: true,\n placeholder: \"my-provider\"\n },\n ...apiKeyFields(\"CUSTOM_API_KEY\", \"model-name\", [\n {\n id: \"baseUrl\",\n label: \"Base URL\",\n type: \"text\",\n required: true,\n placeholder: \"https://api.example.com/v1\"\n },\n {\n id: \"api\",\n label: \"API compatibility\",\n type: \"select\",\n defaultValue: MODEL_API_OPENAI,\n options: [\n { value: MODEL_API_OPENAI, label: \"OpenAI Responses\" },\n { value: \"openai-completions\", label: \"OpenAI Completions\" },\n { value: MODEL_API_ANTHROPIC, label: \"Anthropic Messages\" }\n ]\n }\n ])\n ],\n write: {\n providerIdField: \"providerId\",\n defaultEnv: \"CUSTOM_API_KEY\"\n }\n }\n ]\n }\n];\n\nfunction qwenMethod(id: string, label: string, choiceId: string, endpoint: string): ModelMethodDefinition {\n return {\n id,\n label,\n hint: `Endpoint: ${endpoint}`,\n kind: \"api_key\",\n choiceId,\n fields: apiKeyFields(id.includes(\"cn\") ? \"DASHSCOPE_API_KEY\" : \"DASHSCOPE_API_KEY\", \"qwen3-coder-plus\", [], \"qwen\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl: `https://${endpoint}/compatible-mode/v1`,\n defaultEnv: \"DASHSCOPE_API_KEY\"\n }\n };\n}\n\nfunction stepfunMethod(id: string, label: string, choiceId: string, baseUrl: string, providerId: \"stepfun\" | \"stepfun-plan\"): ModelMethodDefinition {\n return {\n id,\n label,\n hint: `Endpoint: ${baseUrl.replace(/^https?:\\/\\//, \"\")}`,\n kind: \"api_key\",\n choiceId,\n fields: apiKeyFields(\"STEPFUN_API_KEY\", DEFAULT_MODEL_BY_PROVIDER[providerId], [], providerId),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl,\n defaultEnv: \"STEPFUN_API_KEY\"\n }\n };\n}\n\nfunction zaiMethod(id: string, label: string, choiceId: string, baseUrl?: string): ModelMethodDefinition {\n return {\n id,\n label,\n kind: \"api_key\",\n choiceId,\n fields: apiKeyFields(\"ZAI_API_KEY\", DEFAULT_MODEL_BY_PROVIDER.zai, [], \"zai\"),\n write: {\n api: MODEL_API_OPENAI,\n baseUrl,\n defaultEnv: \"ZAI_API_KEY\"\n }\n };\n}\n\nconst commonChannelFields: OpenClawWizardField[] = [\n {\n id: \"account\",\n label: \"Account ID\",\n type: \"text\",\n placeholder: \"default\"\n },\n {\n id: \"name\",\n label: \"Display name\",\n type: \"text\",\n placeholder: \"Support inbox\"\n },\n {\n id: \"useEnv\",\n label: \"Use environment variables\",\n type: \"checkbox\",\n defaultValue: false\n }\n];\n\nconst tokenChannelFields = (tokenId = \"token\", tokenLabel = \"Token\"): OpenClawWizardField[] => [\n ...commonChannelFields,\n {\n id: tokenId,\n label: tokenLabel,\n type: \"password\",\n placeholder: \"Leave empty when using env\"\n },\n {\n id: \"tokenFile\",\n label: \"Token file\",\n type: \"text\",\n placeholder: \"Optional path\"\n }\n];\n\nconst CHANNEL_SETUP_OPTIONS: OpenClawChannelSetupOption[] = [\n {\n id: \"telegram\",\n label: \"Telegram\",\n hint: \"Bot token account\",\n fields: tokenChannelFields(\"token\", \"Bot token\")\n },\n {\n id: \"whatsapp\",\n label: \"WhatsApp\",\n hint: \"WhatsApp bridge token or URL\",\n fields: [...tokenChannelFields(\"token\", \"Access token\"), { id: \"url\", label: \"Webhook URL\", type: \"text\" }]\n },\n {\n id: \"discord\",\n label: \"Discord\",\n hint: \"Bot token\",\n fields: tokenChannelFields(\"botToken\", \"Bot token\")\n },\n {\n id: \"irc\",\n label: \"IRC\",\n hint: \"Server URL and password\",\n fields: [\n ...commonChannelFields,\n { id: \"url\", label: \"Server URL\", type: \"text\", placeholder: \"ircs://irc.example.net:6697/#channel\" },\n { id: \"password\", label: \"Password\", type: \"password\" }\n ]\n },\n {\n id: \"googlechat\",\n label: \"Google Chat\",\n hint: \"Webhook URL\",\n fields: [...commonChannelFields, { id: \"url\", label: \"Webhook URL\", type: \"text\", required: true }]\n },\n {\n id: \"slack\",\n label: \"Slack\",\n hint: \"Bot token + optional signing secret/app token\",\n fields: [\n ...commonChannelFields,\n { id: \"botToken\", label: \"Bot token\", type: \"password\" },\n { id: \"appToken\", label: \"App token\", type: \"password\" },\n { id: \"secret\", label: \"Signing secret\", type: \"password\" }\n ]\n },\n {\n id: \"signal\",\n label: \"Signal\",\n hint: \"Signal CLI / signal-cli-rest-api settings\",\n fields: [\n ...commonChannelFields,\n { id: \"signalNumber\", label: \"Signal number\", type: \"text\", placeholder: \"+15551234567\" },\n { id: \"httpUrl\", label: \"HTTP URL\", type: \"text\", placeholder: \"http://127.0.0.1:8080\" },\n { id: \"cliPath\", label: \"Signal CLI path\", type: \"text\" },\n { id: \"authDir\", label: \"Auth directory\", type: \"text\" }\n ]\n },\n {\n id: \"imessage\",\n label: \"iMessage\",\n hint: \"Local Messages database\",\n fields: [\n ...commonChannelFields,\n { id: \"dbPath\", label: \"Messages DB path\", type: \"text\" },\n {\n id: \"service\",\n label: \"Service\",\n type: \"select\",\n defaultValue: \"auto\",\n options: [\n { value: \"auto\", label: \"Auto\" },\n { value: \"iMessage\", label: \"iMessage\" },\n { value: \"SMS\", label: \"SMS\" }\n ]\n },\n { id: \"region\", label: \"Region\", type: \"text\", placeholder: \"US\" }\n ]\n },\n {\n id: \"feishu\",\n label: \"Feishu\",\n hint: \"App credentials\",\n fields: [\n ...commonChannelFields,\n { id: \"appToken\", label: \"App token\", type: \"password\" },\n { id: \"secret\", label: \"App secret\", type: \"password\" },\n { id: \"url\", label: \"Webhook URL\", type: \"text\" }\n ]\n },\n { id: \"nostr\", label: \"Nostr\", hint: \"Relay URL + private key\", fields: [...commonChannelFields, { id: \"url\", label: \"Relay URL\", type: \"text\" }, { id: \"secret\", label: \"Private key\", type: \"password\" }] },\n { id: \"msteams\", label: \"Microsoft Teams\", hint: \"Webhook URL\", fields: [...commonChannelFields, { id: \"url\", label: \"Webhook URL\", type: \"text\", required: true }] },\n { id: \"mattermost\", label: \"Mattermost\", hint: \"Base URL + token\", fields: [...commonChannelFields, { id: \"baseUrl\", label: \"Base URL\", type: \"text\" }, { id: \"token\", label: \"Token\", type: \"password\" }] },\n { id: \"nextcloud-talk\", label: \"Nextcloud Talk\", hint: \"Base URL + token/password\", fields: [...commonChannelFields, { id: \"baseUrl\", label: \"Base URL\", type: \"text\" }, { id: \"token\", label: \"Token\", type: \"password\" }, { id: \"password\", label: \"Password\", type: \"password\" }] },\n { id: \"matrix\", label: \"Matrix\", hint: \"Homeserver + token\", fields: [...commonChannelFields, { id: \"baseUrl\", label: \"Homeserver URL\", type: \"text\" }, { id: \"token\", label: \"Access token\", type: \"password\" }] },\n { id: \"line\", label: \"LINE\", hint: \"Channel token + secret\", fields: [...commonChannelFields, { id: \"token\", label: \"Channel access token\", type: \"password\" }, { id: \"secret\", label: \"Channel secret\", type: \"password\" }] },\n { id: \"zalo\", label: \"Zalo\", hint: \"Token + secret\", fields: [...commonChannelFields, { id: \"token\", label: \"Token\", type: \"password\" }, { id: \"secret\", label: \"Secret\", type: \"password\" }] },\n { id: \"clickclack\", label: \"ClickClack\", hint: \"Token\", fields: tokenChannelFields(\"token\", \"Token\") },\n { id: \"zalouser\", label: \"Zalo User\", hint: \"Auth directory\", fields: [...commonChannelFields, { id: \"authDir\", label: \"Auth directory\", type: \"text\" }] },\n { id: \"synology-chat\", label: \"Synology Chat\", hint: \"Webhook URL\", fields: [...commonChannelFields, { id: \"url\", label: \"Webhook URL\", type: \"text\" }] },\n { id: \"tlon\", label: \"Tlon\", hint: \"URL + password\", fields: [...commonChannelFields, { id: \"url\", label: \"URL\", type: \"text\" }, { id: \"password\", label: \"Password\", type: \"password\" }] },\n { id: \"qa-channel\", label: \"QA Channel\", hint: \"Local QA channel\", fields: commonChannelFields },\n { id: \"qqbot\", label: \"QQ Bot\", hint: \"Bot token + secret\", fields: [...commonChannelFields, { id: \"botToken\", label: \"Bot token\", type: \"password\" }, { id: \"secret\", label: \"Secret\", type: \"password\" }] },\n { id: \"twitch\", label: \"Twitch\", hint: \"OAuth token\", fields: tokenChannelFields(\"token\", \"OAuth token\") }\n];\n\nexport function getOpenClawConfigWizardMetadata(): OpenClawConfigWizardMetadata {\n return {\n modelProviders: MODEL_PROVIDER_DEFINITIONS.map(({ methods, ...provider }) => ({\n ...provider,\n methods: methods.map(({ write: _write, ...method }) => method)\n })),\n channels: CHANNEL_SETUP_OPTIONS\n };\n}\n\nexport function buildModelAuthRequest(input: ConfigureOpenClawModelAuthRequest): CreateOpenClawModelRequest {\n const provider = MODEL_PROVIDER_DEFINITIONS.find((candidate) => candidate.id === input.providerId);\n if (!provider) throw new Error(`Unknown model provider: ${input.providerId}`);\n const method = provider.methods.find((candidate) => candidate.id === input.methodId);\n if (!method) throw new Error(`Unknown auth method for ${provider.id}: ${input.methodId}`);\n\n const providerIdFromField = method.write.providerIdField ? readWizardString(input.values[method.write.providerIdField]) : undefined;\n const providerId = providerIdFromField ?? method.write.providerId ?? provider.id;\n const modelId = readWizardString(input.values.modelId);\n if (!modelId) throw new Error(\"Model ID is required.\");\n\n const api = readWizardString(input.values.api) ?? method.write.api;\n const baseUrl = readWizardString(input.values.baseUrl) ?? method.write.baseUrlTemplate?.(input.values) ?? method.write.baseUrl;\n const apiKeyEnv = readWizardString(input.values.apiKeyEnv);\n const directApiKey = readWizardString(input.values.apiKey);\n const markerApiKey = method.write.apiKeyMarker;\n\n return {\n provider: providerId,\n modelId,\n label: readWizardString(input.values.label),\n alias: readWizardString(input.values.alias),\n api,\n baseUrl,\n apiKeyEnv: markerApiKey ? undefined : apiKeyEnv,\n apiKey: markerApiKey ?? directApiKey,\n setDefault: input.values.setDefault !== false\n };\n}\n\nexport function buildChannelRequest(input: ConfigureOpenClawChannelRequest): CreateOpenClawChannelRequest {\n const channel = CHANNEL_SETUP_OPTIONS.find((candidate) => candidate.id === input.channelId);\n if (!channel) throw new Error(`Unknown channel: ${input.channelId}`);\n\n const request: CreateOpenClawChannelRequest = {\n channel: channel.id,\n account: readWizardString(input.values.account),\n name: readWizardString(input.values.name),\n useEnv: input.values.useEnv === true\n };\n\n for (const key of [\n \"token\",\n \"botToken\",\n \"appToken\",\n \"password\",\n \"secret\",\n \"url\",\n \"baseUrl\",\n \"dbPath\",\n \"httpHost\",\n \"httpPort\",\n \"httpUrl\",\n \"cliPath\",\n \"authDir\",\n \"region\",\n \"service\",\n \"signalNumber\",\n \"tokenFile\",\n \"secretFile\"\n ] as const) {\n const value = readWizardString(input.values[key]);\n if (value) request[key] = value;\n }\n\n return request;\n}\n\nfunction readWizardString(value: unknown): string | undefined {\n if (typeof value === \"string\" && value.trim()) return value.trim();\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n return undefined;\n}\n"}}}} |