@bitkyc08/opencodex 0.2.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/errors.ts ADDED
@@ -0,0 +1,47 @@
1
+ export interface OcxErrorPayload {
2
+ message: string;
3
+ type: string;
4
+ code: string | null;
5
+ }
6
+
7
+ export function classifyError(status: number, type: string, message: string): OcxErrorPayload {
8
+ const text = message.toLowerCase();
9
+ if (
10
+ text.includes("context_length_exceeded") ||
11
+ text.includes("context window") ||
12
+ text.includes("context length") ||
13
+ text.includes("maximum context") ||
14
+ text.includes("too many tokens")
15
+ ) {
16
+ return { message, type: "invalid_request_error", code: "context_length_exceeded" };
17
+ }
18
+ if (
19
+ text.includes("insufficient_quota") ||
20
+ text.includes("exceeded your current quota")
21
+ ) {
22
+ return { message, type: "insufficient_quota", code: "insufficient_quota" };
23
+ }
24
+ if (status === 429 || text.includes("rate limit") || text.includes("too many requests")) {
25
+ return { message, type: "rate_limit_error", code: "rate_limit_exceeded" };
26
+ }
27
+ if (status === 401 || status === 403 || type === "authentication_error") {
28
+ return { message, type: "authentication_error", code: "invalid_api_key" };
29
+ }
30
+ if (
31
+ status === 503 ||
32
+ text.includes("overloaded") ||
33
+ text.includes("server is busy") ||
34
+ text.includes("temporarily unavailable")
35
+ ) {
36
+ // Codex recognizes "server_is_overloaded" and applies retry-after backoff
37
+ // (responses.rs is_server_overloaded_error); generic "upstream_server_error" is not recognized.
38
+ return { message, type: "server_error", code: "server_is_overloaded" };
39
+ }
40
+ if (status >= 500) {
41
+ return { message, type: "server_error", code: "upstream_server_error" };
42
+ }
43
+ if (status === 400 || type === "invalid_request_error") {
44
+ return { message, type: "invalid_request_error", code: "invalid_request_error" };
45
+ }
46
+ return { message, type, code: type || null };
47
+ }
@@ -0,0 +1,69 @@
1
+ /* eslint-disable */
2
+ // Generated by scripts/generate-jawcode-metadata.ts. Do not edit by hand.
3
+
4
+ export interface JawcodeModelMetadata {
5
+ provider: string;
6
+ id: string;
7
+ contextWindow?: number;
8
+ maxTokens?: number;
9
+ input?: ("text" | "image")[];
10
+ reasoning?: boolean;
11
+ wireModelId?: string;
12
+ }
13
+
14
+ const PROVIDER_ALIASES: Record<string, string> = {
15
+ "xai": "xai",
16
+ "anthropic": "anthropic",
17
+ "kimi": "moonshot",
18
+ "opencode-go": "opencode-go",
19
+ "openrouter": "openrouter",
20
+ "google": "google",
21
+ "gemini": "google",
22
+ "moonshot": "moonshot",
23
+ "minimax": "minimax",
24
+ "minimax-cn": "minimax"
25
+ } as const;
26
+
27
+ type Row = readonly [id: string, contextWindow?: number, maxTokens?: number, input?: string, reasoning?: 0 | 1, wireModelId?: string];
28
+ const DATA: Record<string, readonly Row[]> = {
29
+ "anthropic": [["claude-3-5-sonnet-20240620",200000,8192,"text,image",0],["claude-3-5-sonnet-20241022",200000,8192,"text,image",0],["claude-3-haiku-20240307",200000,4096,"text,image",0],["claude-fable-5",1000000,128000,"text,image",1],["claude-haiku-4-5",200000,64000,"text,image",1],["claude-haiku-4-5-20251001",200000,64000,"text,image",1],["claude-opus-4-0",200000,32000,"text,image",1],["claude-opus-4-1",200000,32000,"text,image",1],["claude-opus-4-1-20250805",200000,32000,"text,image",1],["claude-opus-4-20250514",200000,32000,"text,image",1],["claude-opus-4-5",200000,64000,"text,image",1],["claude-opus-4-5-20251101",200000,64000,"text,image",1],["claude-opus-4-6",1000000,128000,"text,image",1],["claude-opus-4-6[1m]",1000000,128000,"text,image",1,"claude-opus-4-6"],["claude-opus-4-7",1000000,128000,"text,image",1],["claude-opus-4-7[1m]",1000000,128000,"text,image",1,"claude-opus-4-7"],["claude-opus-4-8",1000000,128000,"text,image",1],["claude-opus-4-8[1m]",1000000,128000,"text,image",1,"claude-opus-4-8"],["claude-sonnet-4-0",200000,64000,"text,image",1],["claude-sonnet-4-20250514",200000,64000,"text,image",1],["claude-sonnet-4-5",200000,64000,"text,image",1],["claude-sonnet-4-5-20250929",200000,64000,"text,image",1],["claude-sonnet-4-6",200000,64000,"text,image",1],["claude-sonnet-4-6[1m]",200000,64000,"text,image",1,"claude-sonnet-4-6"]],
30
+ "google": [["gemini-1.5-flash",1000000,8192,"text,image",0],["gemini-1.5-flash-8b",1000000,8192,"text,image",0],["gemini-1.5-pro",1000000,8192,"text,image",0],["gemini-2.0-flash",1048576,8192,"text,image",0],["gemini-2.0-flash-lite",1048576,8192,"text,image",0],["gemini-2.5-flash",1048576,65536,"text,image",1],["gemini-2.5-flash-lite",1048576,65536,"text,image",1],["gemini-2.5-flash-lite-preview-06-17",1048576,65536,"text,image",1],["gemini-2.5-flash-lite-preview-09-2025",1048576,65536,"text,image",1],["gemini-2.5-flash-preview-04-17",1048576,65536,"text,image",1],["gemini-2.5-flash-preview-05-20",1048576,65536,"text,image",1],["gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1],["gemini-2.5-pro",1048576,65536,"text,image",1],["gemini-2.5-pro-preview-05-06",1048576,65536,"text,image",1],["gemini-2.5-pro-preview-06-05",1048576,65536,"text,image",1],["gemini-3-flash-preview",1048576,65536,"text,image",1],["gemini-3-pro-preview",1048576,65536,"text,image",1],["gemini-3.1-flash-lite",1048576,65536,"text,image",1],["gemini-3.1-flash-lite-preview",1048576,65536,"text,image",1],["gemini-3.1-pro-preview",1048576,65536,"text,image",1],["gemini-3.1-pro-preview-customtools",1048576,65536,"text,image",1],["gemini-3.5-flash",1048576,65536,"text,image",1],["gemini-flash-latest",1048576,65536,"text,image",1],["gemini-flash-lite-latest",1048576,65536,"text,image",1],["gemini-live-2.5-flash",128000,8000,"text,image",1],["gemini-live-2.5-flash-preview-native-audio",131072,65536,"text",1],["gemma-3-27b-it",131072,8192,"text,image",0],["gemma-4-26b",256000,8192,"text,image",1],["gemma-4-26b-a4b-it",262144,32768,"text,image",1],["gemma-4-26b-it",256000,8192,"text,image",1],["gemma-4-31b",256000,8192,"text,image",1],["gemma-4-31b-it",262144,32768,"text,image",1],["gemma-4-E2B-it",131072,8192,"text,image",1],["gemma-4-E4B-it",131072,8192,"text,image",1]],
31
+ "minimax": [["MiniMax-M2",196608,128000,"text",1],["MiniMax-M2.1",204800,131072,"text",1],["MiniMax-M2.5",204800,131072,"text",1],["MiniMax-M2.5-highspeed",204800,131072,"text",1],["MiniMax-M2.5-lightning",204800,32000,"text",1],["MiniMax-M2.7",204800,131072,"text",1],["MiniMax-M2.7-highspeed",204800,131072,"text",1],["minimax-m3",512000,128000,"text,image",1],["MiniMax-M3",512000,128000,"text,image",1]],
32
+ "moonshot": [["kimi-k2.5",262144,65536,"text,image",1]],
33
+ "opencode-go": [["deepseek-v4-flash",1000000,384000,"text",1],["deepseek-v4-pro",1000000,384000,"text",1],["glm-5",204800,131072,"text",1],["glm-5.1",200000,131072,"text",1],["glm-5.2",1000000,131072,"text",1],["hy3-preview",256000,64000,"text",1],["kimi-k2.5",262144,262144,"text,image",1],["kimi-k2.6",262144,262144,"text,image",1],["kimi-k2.7-code",262144,262144,"text,image",1],["mimo-v2-omni",262144,131072,"text,image",1],["mimo-v2-pro",1048576,131072,"text",1],["mimo-v2.5",1048576,131072,"text,image",1],["mimo-v2.5-pro",1048576,131072,"text",1],["minimax-m2.5",204800,131072,"text",1],["minimax-m2.7",204800,131072,"text",1],["minimax-m3",512000,128000,"text,image",1],["qwen3.5-plus",1000000,65536,"text,image",1],["qwen3.6-plus",1000000,65536,"text,image",1],["qwen3.7-max",1000000,65536,"text",1],["qwen3.7-plus",1000000,64000,"text,image",1]],
34
+ "openrouter": [["~anthropic/claude-fable-latest",1000000,128000,"text,image",1],["~anthropic/claude-haiku-latest",200000,64000,"text,image",1],["~anthropic/claude-opus-latest",1000000,128000,"text,image",1],["~anthropic/claude-sonnet-latest",1000000,128000,"text,image",1],["~google/gemini-flash-latest",1048576,65536,"text,image",1],["~google/gemini-pro-latest",1048576,65536,"text,image",1],["~moonshotai/kimi-latest",262144,262144,"text,image",1],["~openai/gpt-latest",1050000,128000,"text,image",1],["~openai/gpt-mini-latest",400000,128000,"text,image",1],["ai21/jamba-large-1.7",256000,4096,"text",0],["alibaba/tongyi-deepresearch-30b-a3b",131072,131072,"text",1],["allenai/olmo-3.1-32b-instruct",65536,16384,"text",0],["amazon/nova-2-lite-v1",1000000,65535,"text,image",1],["amazon/nova-lite-v1",300000,5120,"text,image",0],["amazon/nova-micro-v1",128000,5120,"text",0],["amazon/nova-premier-v1",1000000,32000,"text,image",0],["amazon/nova-pro-v1",300000,5120,"text,image",0],["anthropic/claude-3-haiku",200000,4096,"text,image",0],["anthropic/claude-3.5-haiku",200000,8192,"text,image",0],["anthropic/claude-3.5-sonnet",200000,8192,"text,image",0],["anthropic/claude-3.7-sonnet",200000,128000,"text,image",1],["anthropic/claude-3.7-sonnet:thinking",200000,64000,"text,image",1],["anthropic/claude-fable-5",1000000,128000,"text,image",1],["anthropic/claude-haiku-4.5",200000,64000,"text,image",0],["anthropic/claude-opus-4",200000,32000,"text,image",1],["anthropic/claude-opus-4.1",200000,32000,"text,image",1],["anthropic/claude-opus-4.5",200000,64000,"text,image",1],["anthropic/claude-opus-4.6",1000000,128000,"text,image",1],["anthropic/claude-opus-4.6-fast",1000000,128000,"text,image",1],["anthropic/claude-opus-4.7",1000000,128000,"text,image",1],["anthropic/claude-opus-4.7-fast",1000000,128000,"text,image",1],["anthropic/claude-opus-4.8",1000000,128000,"text,image",1],["anthropic/claude-opus-4.8-fast",1000000,128000,"text,image",1],["anthropic/claude-sonnet-4",1000000,64000,"text,image",1],["anthropic/claude-sonnet-4.5",1000000,64000,"text,image",1],["anthropic/claude-sonnet-4.6",1000000,128000,"text,image",1],["arcee-ai/trinity-large-preview",131000,8888,"text",0],["arcee-ai/trinity-large-preview:free",131000,8888,"text",0],["arcee-ai/trinity-large-thinking",262144,262144,"text",1],["arcee-ai/trinity-large-thinking:free",262144,80000,"text",1],["arcee-ai/trinity-mini",131072,131072,"text",1],["arcee-ai/trinity-mini:free",131072,8888,"text",1],["arcee-ai/virtuoso-large",131072,64000,"text",0],["auto",2000000,30000,"text,image",1],["baidu/cobuddy:free",131072,65536,"text",1],["baidu/ernie-4.5-21b-a3b",131072,8000,"text",0],["baidu/ernie-4.5-vl-28b-a3b",131072,8000,"text,image",1],["bytedance-seed/seed-1.6",262144,32768,"text,image",1],["bytedance-seed/seed-1.6-flash",262144,32768,"text,image",1],["bytedance-seed/seed-2.0-lite",262144,131072,"text,image",1],["bytedance-seed/seed-2.0-mini",262144,131072,"text,image",1],["cohere/command-r-08-2024",128000,4000,"text",0],["cohere/command-r-plus-08-2024",128000,4000,"text",0],["cohere/north-mini-code:free",256000,64000,"text",1],["deepseek/deepseek-chat",131072,16000,"text",0],["deepseek/deepseek-chat-v3-0324",163840,16384,"text",1],["deepseek/deepseek-chat-v3.1",163840,32768,"text",1],["deepseek/deepseek-r1",163840,16000,"text",1],["deepseek/deepseek-r1-0528",163840,32768,"text",1],["deepseek/deepseek-v3.1-terminus",163840,32768,"text",1],["deepseek/deepseek-v3.1-terminus:exacto",163840,8888,"text",1],["deepseek/deepseek-v3.2",131072,64000,"text",1],["deepseek/deepseek-v3.2-exp",163840,65536,"text",1],["deepseek/deepseek-v4-flash",1048576,65536,"text",1],["deepseek/deepseek-v4-flash:free",1048576,384000,"text",1],["deepseek/deepseek-v4-pro",1048576,384000,"text",1],["essentialai/rnj-1-instruct",32768,8888,"text",0],["google/gemini-2.0-flash-001",1048576,8192,"text,image",0],["google/gemini-2.0-flash-lite-001",1048576,8192,"text,image",0],["google/gemini-2.5-flash",1048576,65535,"text,image",1],["google/gemini-2.5-flash-lite",1048576,65535,"text,image",0],["google/gemini-2.5-flash-lite-preview-09-2025",1048576,65535,"text,image",1],["google/gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1],["google/gemini-2.5-pro",1048576,65536,"text,image",1],["google/gemini-2.5-pro-preview",1048576,65536,"text,image",1],["google/gemini-2.5-pro-preview-05-06",1048576,65535,"text,image",1],["google/gemini-3-flash-preview",1048576,65535,"text,image",1],["google/gemini-3-pro-image",65536,32768,"text,image",1],["google/gemini-3-pro-preview",1048000,64000,"text,image",1],["google/gemini-3.1-flash-lite",1048576,65536,"text,image",1],["google/gemini-3.1-flash-lite-preview",1048576,65536,"text,image",0],["google/gemini-3.1-pro-preview",1048576,65536,"text,image",1],["google/gemini-3.1-pro-preview-customtools",1048756,65536,"text,image",1],["google/gemini-3.5-flash",1048576,65536,"text,image",1],["google/gemma-3-12b-it",131072,16384,"text,image",0],["google/gemma-3-27b-it",131072,16384,"text,image",1],["google/gemma-3-27b-it:free",131072,8192,"text,image",0],["google/gemma-4-26b-a4b-it",262144,8888,"text,image",1],["google/gemma-4-26b-a4b-it:free",262144,32768,"text,image",1],["google/gemma-4-31b-it",262144,262144,"text,image",1],["google/gemma-4-31b-it:free",262144,8192,"text,image",1],["ibm-granite/granite-4.1-8b",131072,131072,"text",0],["inception/mercury",128000,32000,"text",0],["inception/mercury-2",128000,50000,"text",1],["inception/mercury-coder",128000,32000,"text",0],["inclusionai/ling-2.6-1t",262144,32768,"text",0],["inclusionai/ling-2.6-1t:free",262144,32768,"text",0],["inclusionai/ling-2.6-flash",262144,32768,"text",0],["inclusionai/ling-2.6-flash:free",262144,32768,"text",0],["inclusionai/ring-2.6-1t",262144,65536,"text",1],["inclusionai/ring-2.6-1t:free",262144,65536,"text",1],["kwaipilot/kat-coder-pro",256000,128000,"text",0],["kwaipilot/kat-coder-pro-v2",256000,80000,"text",0],["liquid/lfm-2.5-1.2b-thinking:free",32768,8888,"text",1],["meituan/longcat-flash-chat",131072,131072,"text",0],["meta-llama/llama-3-8b-instruct",8192,16384,"text",0],["meta-llama/llama-3.1-405b-instruct",131000,8888,"text",0],["meta-llama/llama-3.1-70b-instruct",131072,16384,"text",0],["meta-llama/llama-3.1-8b-instruct",131072,16384,"text",0],["meta-llama/llama-3.3-70b-instruct",131072,16384,"text",0],["meta-llama/llama-3.3-70b-instruct:free",131072,8888,"text",0],["meta-llama/llama-4-maverick",1048576,16384,"text,image",0],["meta-llama/llama-4-scout",10000000,16384,"text,image",0],["minimax/minimax-m1",1000000,40000,"text",1],["minimax/minimax-m2",204800,196608,"text",1],["minimax/minimax-m2.1",204800,196608,"text",1],["minimax/minimax-m2.5",204800,196608,"text",1],["minimax/minimax-m2.5:free",262144,8192,"text",1],["minimax/minimax-m2.7",204800,131072,"text",1],["minimax/minimax-m3",1048576,512000,"text,image",1],["mistralai/codestral-2508",256000,8888,"text",0],["mistralai/devstral-2512",262144,8888,"text",0],["mistralai/devstral-medium",131072,8888,"text",0],["mistralai/devstral-small",131072,8888,"text",0],["mistralai/ministral-14b-2512",262144,8888,"text,image",0],["mistralai/ministral-3b-2512",131072,8888,"text,image",0],["mistralai/ministral-8b-2512",262144,8888,"text,image",0],["mistralai/mistral-large",128000,8888,"text",0],["mistralai/mistral-large-2407",131072,8888,"text",0],["mistralai/mistral-large-2411",131072,8888,"text",0],["mistralai/mistral-large-2512",262144,8888,"text,image",0],["mistralai/mistral-medium-3",131072,8888,"text,image",0],["mistralai/mistral-medium-3-5",262144,8888,"text,image",1],["mistralai/mistral-medium-3.1",131072,8888,"text,image",0],["mistralai/mistral-nemo",131072,8888,"text",0],["mistralai/mistral-saba",32768,8888,"text",0],["mistralai/mistral-small-24b-instruct-2501",32768,16384,"text",0],["mistralai/mistral-small-2603",262144,8888,"text,image",1],["mistralai/mistral-small-3.1-24b-instruct",131072,131072,"text,image",0],["mistralai/mistral-small-3.1-24b-instruct:free",128000,8888,"text,image",0],["mistralai/mistral-small-3.2-24b-instruct",128000,16384,"text,image",0],["mistralai/mistral-small-creative",32768,8888,"text",0],["mistralai/mixtral-8x22b-instruct",65536,13108,"text",0],["mistralai/mixtral-8x7b-instruct",32768,16384,"text",0],["mistralai/pixtral-large-2411",131072,8888,"text,image",0],["mistralai/voxtral-small-24b-2507",32000,8888,"text",0],["moonshotai/kimi-k2",131072,32768,"text",0],["moonshotai/kimi-k2-0905",262144,262144,"text",0],["moonshotai/kimi-k2-0905:exacto",262144,8888,"text",0],["moonshotai/kimi-k2-thinking",262144,262144,"text",1],["moonshotai/kimi-k2.5",262144,64000,"text,image",1],["moonshotai/kimi-k2.6",262144,262144,"text,image",1],["moonshotai/kimi-k2.6:free",262144,8888,"text,image",1],["moonshotai/kimi-k2.7-code",262144,262144,"text,image",1],["nex-agi/deepseek-v3.1-nex-n1",131072,163840,"text",0],["nex-agi/nex-n2-pro:free",262144,262144,"text,image",1],["nousresearch/deephermes-3-mistral-24b-preview",32768,32768,"text",1],["nousresearch/hermes-4-70b",131072,131072,"text",1],["nvidia/llama-3.1-nemotron-70b-instruct",131072,16384,"text",0],["nvidia/llama-3.3-nemotron-super-49b-v1.5",131072,16384,"text",1],["nvidia/nemotron-3-nano-30b-a3b",262144,228000,"text",1],["nvidia/nemotron-3-nano-30b-a3b:free",256000,8888,"text",1],["nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free",256000,65536,"text,image",1],["nvidia/nemotron-3-super-120b-a12b",1000000,262144,"text",1],["nvidia/nemotron-3-super-120b-a12b:free",1000000,262144,"text",1],["nvidia/nemotron-3-ultra-550b-a55b",1000000,16384,"text",1],["nvidia/nemotron-3-ultra-550b-a55b:free",1000000,65536,"text",1],["nvidia/nemotron-nano-12b-v2-vl:free",128000,128000,"text,image",1],["nvidia/nemotron-nano-9b-v2",131072,16384,"text",1],["nvidia/nemotron-nano-9b-v2:free",128000,8888,"text",1],["openai/gpt-3.5-turbo",16385,4096,"text",0],["openai/gpt-3.5-turbo-0613",4095,4096,"text",0],["openai/gpt-3.5-turbo-16k",16385,4096,"text",0],["openai/gpt-4",8191,8192,"text",0],["openai/gpt-4-0314",8191,4096,"text",0],["openai/gpt-4-1106-preview",128000,4096,"text",0],["openai/gpt-4-turbo",128000,4096,"text,image",0],["openai/gpt-4-turbo-preview",128000,4096,"text",0],["openai/gpt-4.1",1047576,8888,"text,image",0],["openai/gpt-4.1-mini",1047576,32768,"text,image",0],["openai/gpt-4.1-nano",1047576,32768,"text,image",0],["openai/gpt-4o",128000,16384,"text,image",0],["openai/gpt-4o-2024-05-13",128000,4096,"text,image",0],["openai/gpt-4o-2024-08-06",128000,16384,"text,image",0],["openai/gpt-4o-2024-11-20",128000,16384,"text,image",0],["openai/gpt-4o-audio-preview",128000,16384,"text",0],["openai/gpt-4o-mini",128000,16384,"text,image",0],["openai/gpt-4o-mini-2024-07-18",128000,16384,"text,image",0],["openai/gpt-4o:extended",128000,64000,"text,image",0],["openai/gpt-5",400000,128000,"text,image",1],["openai/gpt-5-codex",272000,128000,"text,image",1],["openai/gpt-5-image",400000,128000,"text,image",1],["openai/gpt-5-image-mini",400000,128000,"text,image",1],["openai/gpt-5-mini",400000,128000,"text,image",1],["openai/gpt-5-nano",400000,8888,"text,image",1],["openai/gpt-5-pro",400000,128000,"text,image",1],["openai/gpt-5.1",400000,128000,"text,image",1],["openai/gpt-5.1-chat",128000,32000,"text,image",0],["openai/gpt-5.1-codex",272000,128000,"text,image",1],["openai/gpt-5.1-codex-max",272000,128000,"text,image",1],["openai/gpt-5.1-codex-mini",272000,100000,"text,image",1],["openai/gpt-5.2",400000,128000,"text,image",1],["openai/gpt-5.2-chat",128000,16384,"text,image",0],["openai/gpt-5.2-codex",272000,128000,"text,image",1],["openai/gpt-5.2-pro",400000,128000,"text,image",1],["openai/gpt-5.3-chat",128000,16384,"text",0],["openai/gpt-5.3-codex",272000,128000,"text,image",1],["openai/gpt-5.4",1050000,128000,"text,image",1],["openai/gpt-5.4-mini",400000,128000,"text",0],["openai/gpt-5.4-nano",400000,128000,"text",0],["openai/gpt-5.4-pro",1050000,128000,"text,image",1],["openai/gpt-5.5",1050000,128000,"text,image",1],["openai/gpt-5.5-pro",1050000,128000,"text,image",1],["openai/gpt-audio",128000,16384,"text",0],["openai/gpt-audio-mini",128000,16384,"text",0],["openai/gpt-chat-latest",400000,128000,"text,image",0],["openai/gpt-oss-120b",131072,65536,"text",1],["openai/gpt-oss-120b:exacto",131072,8888,"text",1],["openai/gpt-oss-120b:free",131072,131072,"text",1],["openai/gpt-oss-20b",131072,65536,"text",1],["openai/gpt-oss-20b:free",131072,32768,"text",1],["openai/gpt-oss-safeguard-20b",131072,65536,"text",1],["openai/o1",200000,100000,"text,image",1],["openai/o3",200000,100000,"text,image",1],["openai/o3-deep-research",200000,100000,"text,image",1],["openai/o3-mini",200000,100000,"text",1],["openai/o3-mini-high",200000,100000,"text",1],["openai/o3-pro",200000,100000,"text,image",1],["openai/o4-mini",200000,100000,"text,image",1],["openai/o4-mini-deep-research",200000,100000,"text,image",1],["openai/o4-mini-high",200000,100000,"text,image",1],["openrouter/aurora-alpha",128000,50000,"text",1],["openrouter/auto",2000000,8888,"text,image",1],["openrouter/elephant-alpha",262144,32768,"text",0],["openrouter/free",200000,8888,"text,image",1],["openrouter/healer-alpha",262144,32000,"text,image",1],["openrouter/hunter-alpha",1048576,32000,"text",1],["openrouter/owl-alpha",1048756,262144,"text",0],["poolside/laguna-m.1",262144,32768,"text",1],["poolside/laguna-m.1:free",262144,32768,"text",1],["poolside/laguna-xs.2",262144,32768,"text",1],["poolside/laguna-xs.2:free",262144,32768,"text",1],["prime-intellect/intellect-3",131072,131072,"text",1],["qwen/qwen-2.5-72b-instruct",131072,16384,"text",0],["qwen/qwen-2.5-7b-instruct",131072,32768,"text",0],["qwen/qwen-max",32768,8192,"text",0],["qwen/qwen-plus",1000000,32768,"text",0],["qwen/qwen-plus-2025-07-28",1000000,32768,"text",0],["qwen/qwen-plus-2025-07-28:thinking",1000000,32768,"text",1],["qwen/qwen-turbo",131072,8192,"text",0],["qwen/qwen-vl-max",131072,32768,"text,image",0],["qwen/qwen3-14b",131702,40960,"text",1],["qwen/qwen3-235b-a22b",131072,8192,"text",1],["qwen/qwen3-235b-a22b-2507",262144,16384,"text",1],["qwen/qwen3-235b-a22b-thinking-2507",262144,262144,"text",1],["qwen/qwen3-30b-a3b",131072,16384,"text",1],["qwen/qwen3-30b-a3b-instruct-2507",131072,32000,"text",0],["qwen/qwen3-30b-a3b-thinking-2507",131072,131072,"text",1],["qwen/qwen3-32b",131072,16384,"text",1],["qwen/qwen3-4b",131072,8192,"text",1],["qwen/qwen3-4b:free",40960,8888,"text",1],["qwen/qwen3-8b",131072,8192,"text",1],["qwen/qwen3-coder",1048576,65536,"text",0],["qwen/qwen3-coder-30b-a3b-instruct",160000,32768,"text",0],["qwen/qwen3-coder-flash",1000000,65536,"text",0],["qwen/qwen3-coder-next",262144,262144,"text",0],["qwen/qwen3-coder-plus",1000000,65536,"text",0],["qwen/qwen3-coder:exacto",262144,65536,"text",0],["qwen/qwen3-coder:free",1048576,262000,"text",0],["qwen/qwen3-max",262144,32768,"text",1],["qwen/qwen3-max-thinking",262144,32768,"text",1],["qwen/qwen3-next-80b-a3b-instruct",262144,16384,"text",0],["qwen/qwen3-next-80b-a3b-instruct:free",262144,8888,"text",0],["qwen/qwen3-next-80b-a3b-thinking",262144,32768,"text",1],["qwen/qwen3-vl-235b-a22b-instruct",262144,16384,"text,image",0],["qwen/qwen3-vl-235b-a22b-thinking",131072,32768,"text,image",1],["qwen/qwen3-vl-30b-a3b-instruct",262144,32768,"text,image",0],["qwen/qwen3-vl-30b-a3b-thinking",131072,32768,"text,image",1],["qwen/qwen3-vl-32b-instruct",262144,32768,"text,image",0],["qwen/qwen3-vl-8b-instruct",256000,32768,"text,image",0],["qwen/qwen3-vl-8b-thinking",256000,32768,"text,image",1],["qwen/qwen3.5-122b-a10b",262144,262144,"text,image",1],["qwen/qwen3.5-27b",262144,65536,"text,image",1],["qwen/qwen3.5-35b-a3b",262144,262144,"text,image",1],["qwen/qwen3.5-397b-a17b",256000,8192,"text,image",1],["qwen/qwen3.5-9b",262144,262144,"text,image",1],["qwen/qwen3.5-flash-02-23",1000000,65536,"text,image",1],["qwen/qwen3.5-plus-02-15",1000000,65536,"text,image",1],["qwen/qwen3.5-plus-20260420",1000000,65536,"text,image",1],["qwen/qwen3.6-27b",262144,262140,"text,image",1],["qwen/qwen3.6-35b-a3b",262144,262144,"text,image",1],["qwen/qwen3.6-flash",1000000,65536,"text,image",1],["qwen/qwen3.6-max-preview",262144,65536,"text",1],["qwen/qwen3.6-plus",1000000,65536,"text",1],["qwen/qwen3.6-plus-preview:free",1000000,32000,"text",1],["qwen/qwen3.6-plus:free",1000000,65536,"text,image",1],["qwen/qwen3.7-max",1000000,65536,"text",1],["qwen/qwen3.7-plus",1000000,65536,"text,image",1],["qwen/qwq-32b",131072,131072,"text",1],["reka/reka-edge",16384,16384,"text,image",0],["rekaai/reka-edge",16384,16384,"text,image",0],["relace/relace-search",256000,128000,"text",0],["sao10k/l3-euryale-70b",8192,8192,"text",0],["sao10k/l3.1-euryale-70b",131072,16384,"text",0],["stepfun/step-3.5-flash",262144,16384,"text",0],["stepfun/step-3.5-flash:free",256000,256000,"text",1],["stepfun/step-3.7-flash",256000,256000,"text,image",1],["tencent/hy3-preview",262144,262144,"text",1],["tencent/hy3-preview:free",262144,262144,"text",1],["thedrummer/rocinante-12b",32768,32768,"text",0],["thedrummer/unslopnemo-12b",32768,32768,"text",0],["tngtech/deepseek-r1t2-chimera",163840,163840,"text",1],["tngtech/tng-r1t-chimera",163840,65536,"text",1],["upstage/solar-pro-3",128000,8888,"text",1],["upstage/solar-pro-3:free",128000,8888,"text",1],["x-ai/grok-3",131072,8888,"text",0],["x-ai/grok-3-beta",131072,8888,"text",0],["x-ai/grok-3-mini",131072,8888,"text",1],["x-ai/grok-3-mini-beta",131072,8888,"text",1],["x-ai/grok-4",256000,64000,"text,image",1],["x-ai/grok-4-fast",2000000,30000,"text,image",1],["x-ai/grok-4.1-fast",2000000,30000,"text,image",1],["x-ai/grok-4.20",2000000,8888,"text,image",1],["x-ai/grok-4.20-beta",2000000,8888,"text,image",1],["x-ai/grok-4.3",1000000,1000000,"text,image",1],["x-ai/grok-build-0.1",256000,256000,"text,image",1],["x-ai/grok-code-fast-1",256000,10000,"text",1],["xiaomi/mimo-v2-flash",262144,65536,"text",1],["xiaomi/mimo-v2-omni",262144,65536,"text,image",1],["xiaomi/mimo-v2-pro",1048576,131072,"text",1],["xiaomi/mimo-v2.5",1048576,131072,"text,image",1],["xiaomi/mimo-v2.5-pro",1048576,131072,"text",1],["z-ai/glm-4-32b",128000,8888,"text",0],["z-ai/glm-4.5",131072,98304,"text",1],["z-ai/glm-4.5-air",131072,98304,"text",1],["z-ai/glm-4.5-air:free",131072,96000,"text",1],["z-ai/glm-4.5v",65536,16384,"text,image",1],["z-ai/glm-4.6",202752,131072,"text",1],["z-ai/glm-4.6:exacto",204800,131072,"text",1],["z-ai/glm-4.6v",131072,32768,"text,image",1],["z-ai/glm-4.7",202752,131072,"text",1],["z-ai/glm-4.7-flash",202752,16384,"text",1],["z-ai/glm-5",202752,128000,"text",1],["z-ai/glm-5-turbo",262144,131072,"text",1],["z-ai/glm-5.1",202752,131072,"text",1],["z-ai/glm-5.2",1048576,131072,"text",1],["z-ai/glm-5v-turbo",202752,131072,"text,image",1]],
35
+ "xai": [["grok-2",131072,8192,"text",0],["grok-2-1212",131072,8192,"text",0],["grok-2-latest",131072,8192,"text",0],["grok-2-vision",8192,4096,"text,image",0],["grok-2-vision-1212",8192,4096,"text,image",0],["grok-2-vision-latest",8192,4096,"text,image",0],["grok-3",131072,8192,"text",0],["grok-3-fast",131072,8192,"text",0],["grok-3-fast-latest",131072,8192,"text",0],["grok-3-latest",131072,8192,"text",0],["grok-3-mini",131072,8192,"text",1],["grok-3-mini-fast",131072,8192,"text",1],["grok-3-mini-fast-latest",131072,8192,"text",1],["grok-3-mini-latest",131072,8192,"text",1],["grok-4",256000,64000,"text",1],["grok-4-1-fast",2000000,30000,"text,image",1],["grok-4-1-fast-non-reasoning",2000000,30000,"text,image",0],["grok-4-fast",2000000,30000,"text,image",1],["grok-4-fast-non-reasoning",2000000,30000,"text,image",0],["grok-4.20-0309-non-reasoning",1000000,30000,"text,image",0],["grok-4.20-0309-reasoning",1000000,30000,"text,image",1],["grok-4.20-beta-latest-non-reasoning",2000000,30000,"text,image",0],["grok-4.20-beta-latest-reasoning",2000000,30000,"text,image",1],["grok-4.20-multi-agent-beta-latest",2000000,30000,"text,image",1],["grok-4.3",1000000,30000,"text,image",1],["grok-beta",131072,4096,"text",0],["grok-build-0.1",256000,256000,"text,image",1],["grok-code-fast-1",256000,10000,"text",1],["grok-composer-2.5-fast",200000,64000,"text",1],["grok-vision-beta",8192,4096,"text,image",0]],
36
+ };
37
+
38
+ export function resolveJawcodeProvider(provider: string): string | undefined {
39
+ return PROVIDER_ALIASES[provider];
40
+ }
41
+
42
+ export function getJawcodeModelMetadata(provider: string, modelId: string): JawcodeModelMetadata | undefined {
43
+ const row = DATA[provider]?.find(r => r[0] === modelId);
44
+ if (!row) return undefined;
45
+ return rowToMetadata(provider, row);
46
+ }
47
+
48
+ export function getJawcodeModelMetadataCaseInsensitive(provider: string, modelId: string): JawcodeModelMetadata | undefined {
49
+ const lower = modelId.toLowerCase();
50
+ const row = DATA[provider]?.find(r => r[0].toLowerCase() === lower);
51
+ if (!row) return undefined;
52
+ return rowToMetadata(provider, row);
53
+ }
54
+
55
+ export function listJawcodeModelMetadata(provider: string): JawcodeModelMetadata[] {
56
+ return (DATA[provider] ?? []).map(row => rowToMetadata(provider, row));
57
+ }
58
+
59
+ function rowToMetadata(provider: string, row: Row): JawcodeModelMetadata {
60
+ const [id, contextWindow, maxTokens, input, reasoning, wireModelId] = row;
61
+ return {
62
+ provider, id,
63
+ ...(contextWindow !== undefined ? { contextWindow } : {}),
64
+ ...(maxTokens !== undefined ? { maxTokens } : {}),
65
+ ...(input ? { input: input.split(",") as ("text" | "image")[] } : {}),
66
+ ...(reasoning !== undefined ? { reasoning: reasoning === 1 } : {}),
67
+ ...(wireModelId !== undefined ? { wireModelId } : {}),
68
+ };
69
+ }
package/src/init.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as readline from "node:readline";
2
2
  import { injectCodexConfig } from "./codex-inject";
3
3
  import { getDefaultConfig, saveConfig } from "./config";
4
- import { KEY_LOGIN_PROVIDERS, enrichProviderFromCatalog } from "./oauth/key-providers";
5
- import { OAUTH_PROVIDERS } from "./oauth";
4
+ import { enrichProviderFromCatalog } from "./oauth/key-providers";
5
+ import { deriveInitProviders } from "./providers/derive";
6
6
  import type { OcxConfig, OcxProviderConfig } from "./types";
7
7
 
8
8
  function createPrompt(): { ask(question: string): Promise<string>; close(): void } {
@@ -26,39 +26,12 @@ export interface InitProvider {
26
26
  defaultModel?: string;
27
27
  }
28
28
 
29
- const OAUTH_LABELS: Record<string, string> = {
30
- xai: "xAI (Grok)", anthropic: "Anthropic (Claude)", kimi: "Kimi (Moonshot)",
31
- };
32
-
33
29
  /**
34
- * The full CLI provider menu, built from the SAME registries the GUI uses (OAUTH_PROVIDERS +
35
- * KEY_LOGIN_PROVIDERS) plus the ChatGPT-forward, a few non-catalog key providers, and local servers
36
- * so `ocx init` reaches provider parity with the GUI. Exported for verification.
30
+ * The full CLI provider menu, derived from the canonical provider registry so `ocx init`,
31
+ * the GUI picker, key-login catalog, OAuth seeds, and metadata aliases cannot drift.
37
32
  */
38
33
  export function buildInitProviders(): InitProvider[] {
39
- const out: InitProvider[] = [];
40
- // ChatGPT login (no key) — the default forward provider.
41
- out.push({ id: "openai", label: "OpenAI — ChatGPT login (no key)", adapter: "openai-responses", baseUrl: "https://chatgpt.com/backend-api/codex", kind: "forward" });
42
- // Real account logins (OAuth).
43
- for (const id of Object.keys(OAUTH_PROVIDERS)) {
44
- const pc = OAUTH_PROVIDERS[id].providerConfig;
45
- out.push({ id, label: `${OAUTH_LABELS[id] ?? id} — account login`, adapter: pc.adapter, baseUrl: pc.baseUrl, kind: "oauth", defaultModel: pc.defaultModel });
46
- }
47
- // Key providers not in the catalog (native adapters / well-known endpoints).
48
- out.push({ id: "openai-apikey", label: "OpenAI (API key)", adapter: "openai-responses", baseUrl: "https://api.openai.com/v1", kind: "key", dashboardUrl: "https://platform.openai.com/api-keys", defaultModel: "gpt-5.5" });
49
- out.push({ id: "openrouter", label: "OpenRouter", adapter: "openai-chat", baseUrl: "https://openrouter.ai/api/v1", kind: "key", dashboardUrl: "https://openrouter.ai/keys" });
50
- out.push({ id: "groq", label: "Groq", adapter: "openai-chat", baseUrl: "https://api.groq.com/openai/v1", kind: "key", dashboardUrl: "https://console.groq.com/keys" });
51
- out.push({ id: "google", label: "Google Gemini", adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", kind: "key", dashboardUrl: "https://aistudio.google.com/apikey", defaultModel: "gemini-3-pro" });
52
- out.push({ id: "azure-openai", label: "Azure OpenAI", adapter: "azure", baseUrl: "https://{resource}.openai.azure.com/openai/deployments/{deployment}", kind: "key", dashboardUrl: "https://portal.azure.com" });
53
- // The full API-key catalog (deepseek, mistral, kilo, minimax, … — same set the GUI shows).
54
- for (const [id, p] of Object.entries(KEY_LOGIN_PROVIDERS)) {
55
- out.push({ id, label: p.label, adapter: p.adapter, baseUrl: p.baseUrl, kind: "key", dashboardUrl: p.dashboardUrl, defaultModel: p.defaultModel });
56
- }
57
- // Local servers (usually no key).
58
- out.push({ id: "ollama", label: "Ollama (local)", adapter: "openai-chat", baseUrl: "http://localhost:11434/v1", kind: "local" });
59
- out.push({ id: "vllm", label: "vLLM (local)", adapter: "openai-chat", baseUrl: "http://localhost:8000/v1", kind: "local" });
60
- out.push({ id: "lm-studio", label: "LM Studio (local)", adapter: "openai-chat", baseUrl: "http://localhost:1234/v1", kind: "local" });
61
- return out;
34
+ return deriveInitProviders();
62
35
  }
63
36
 
64
37
  const KIND_HEADING: Record<InitKind, string> = {
@@ -5,6 +5,7 @@ import { getCredential, saveCredential } from "./store";
5
5
  import { loginXai, refreshXaiToken } from "./xai";
6
6
  import { ANTHROPIC_OAUTH_BETA, loginAnthropic, refreshAnthropicToken } from "./anthropic";
7
7
  import { loginKimi, refreshKimiToken } from "./kimi";
8
+ import { deriveOAuthDefaultModel, deriveOAuthProviderConfig } from "../providers/derive";
8
9
 
9
10
  const REFRESH_SKEW_MS = 60_000;
10
11
 
@@ -16,51 +17,36 @@ interface OAuthProviderDef {
16
17
  defaultModel: string;
17
18
  }
18
19
 
20
+ function oauthConfig(id: string): OcxProviderConfig {
21
+ const config = deriveOAuthProviderConfig(id);
22
+ if (!config) throw new Error(`OAuth provider missing from registry: ${id}`);
23
+ return config;
24
+ }
25
+
26
+ function oauthDefaultModel(id: string): string {
27
+ const model = deriveOAuthDefaultModel(id);
28
+ if (!model) throw new Error(`OAuth provider missing default model in registry: ${id}`);
29
+ return model;
30
+ }
31
+
19
32
  export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
20
33
  xai: {
21
34
  login: (ctrl) => loginXai(ctrl, { importLocal: "fallback" }),
22
35
  refresh: refreshXaiToken,
23
- providerConfig: {
24
- adapter: "openai-chat",
25
- baseUrl: "https://api.x.ai/v1",
26
- authMode: "oauth",
27
- // Real xAI model ids (verified live via GET api.x.ai/v1/models); the proxy also fetches
28
- // the live list at sync time, so this is the routing hint / fallback + explicit additions.
29
- models: ["grok-4.3", "grok-4.20-0309-reasoning", "grok-4.20-0309-non-reasoning", "grok-build-0.1", "grok-composer-2.5-fast"],
30
- defaultModel: "grok-4.3",
31
- // These don't accept a reasoning/thinking param — never forward reasoning_effort for them.
32
- noReasoningModels: ["grok-build-0.1", "grok-composer-2.5-fast"],
33
- // These are text-only (no image input) — the vision sidecar describes images for them.
34
- noVisionModels: ["grok-build-0.1", "grok-composer-2.5-fast"],
35
- },
36
- defaultModel: "grok-4.3",
36
+ providerConfig: oauthConfig("xai"),
37
+ defaultModel: oauthDefaultModel("xai"),
37
38
  },
38
39
  anthropic: {
39
40
  login: (ctrl) => loginAnthropic(ctrl, { importLocal: "fallback" }),
40
41
  refresh: refreshAnthropicToken,
41
- providerConfig: {
42
- adapter: "anthropic",
43
- baseUrl: "https://api.anthropic.com",
44
- authMode: "oauth",
45
- // Current dateless flagship ids — routing hint / fallback only; the proxy fetches the live
46
- // list from Anthropic's GET /v1/models at sync time (always-latest), so this just seeds a
47
- // sane set when that fetch is unavailable.
48
- models: ["claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"],
49
- defaultModel: "claude-sonnet-4-6",
50
- },
51
- defaultModel: "claude-sonnet-4-6",
42
+ providerConfig: oauthConfig("anthropic"),
43
+ defaultModel: oauthDefaultModel("anthropic"),
52
44
  },
53
45
  kimi: {
54
46
  login: (ctrl) => loginKimi(ctrl),
55
47
  refresh: refreshKimiToken,
56
- providerConfig: {
57
- adapter: "openai-chat",
58
- baseUrl: "https://api.kimi.com/coding/v1",
59
- authMode: "oauth",
60
- models: ["kimi-k2.6", "kimi-k2.5"],
61
- defaultModel: "kimi-k2.6",
62
- },
63
- defaultModel: "kimi-k2.6",
48
+ providerConfig: oauthConfig("kimi"),
49
+ defaultModel: oauthDefaultModel("kimi"),
64
50
  },
65
51
  };
66
52
 
@@ -1,4 +1,5 @@
1
1
  import type { OcxProviderConfig } from "../types";
2
+ import { deriveKeyLoginMap } from "../providers/derive";
2
3
 
3
4
  /**
4
5
  * API-key "login" providers: not OAuth — the flow opens the provider's dashboard so the user can
@@ -23,69 +24,7 @@ export interface KeyLoginProvider {
23
24
  noReasoningModels?: string[];
24
25
  }
25
26
 
26
- export const KEY_LOGIN_PROVIDERS: Record<string, KeyLoginProvider> = {
27
- deepseek: { label: "DeepSeek", baseUrl: "https://api.deepseek.com", adapter: "openai-chat", dashboardUrl: "https://platform.deepseek.com/api_keys", models: ["deepseek-chat", "deepseek-reasoner"], defaultModel: "deepseek-chat" },
28
- cerebras: { label: "Cerebras", baseUrl: "https://api.cerebras.ai/v1", adapter: "openai-chat", dashboardUrl: "https://cloud.cerebras.ai/platform/apikeys", defaultModel: "llama-3.3-70b" },
29
- together: { label: "Together", baseUrl: "https://api.together.xyz/v1", adapter: "openai-chat", dashboardUrl: "https://api.together.xyz/settings/api-keys" },
30
- fireworks: { label: "Fireworks", baseUrl: "https://api.fireworks.ai/inference/v1", adapter: "openai-chat", dashboardUrl: "https://fireworks.ai/account/api-keys" },
31
- firepass: { label: "Fire Pass (Fireworks Kimi)", baseUrl: "https://api.fireworks.ai/inference/v1", adapter: "openai-chat", dashboardUrl: "https://fireworks.ai/account/api-keys" },
32
- moonshot: { label: "Moonshot (Kimi API)", baseUrl: "https://api.moonshot.ai/v1", adapter: "openai-chat", dashboardUrl: "https://platform.moonshot.ai/console/api-keys", defaultModel: "kimi-k2-0905-preview" },
33
- huggingface: { label: "Hugging Face", baseUrl: "https://router.huggingface.co/v1", adapter: "openai-chat", dashboardUrl: "https://huggingface.co/settings/tokens" },
34
- nvidia: { label: "NVIDIA NIM", baseUrl: "https://integrate.api.nvidia.com/v1", adapter: "openai-chat", dashboardUrl: "https://build.nvidia.com" },
35
- venice: { label: "Venice", baseUrl: "https://api.venice.ai/api/v1", adapter: "openai-chat", dashboardUrl: "https://venice.ai/settings/api" },
36
- zai: { label: "Z.AI (GLM Coding)", baseUrl: "https://api.z.ai/api/coding/paas/v4", adapter: "openai-chat", dashboardUrl: "https://z.ai/manage-apikey/apikey-list", defaultModel: "glm-4.6" },
37
- nanogpt: { label: "NanoGPT", baseUrl: "https://nano-gpt.com/api/v1", adapter: "openai-chat", dashboardUrl: "https://nano-gpt.com/api" },
38
- synthetic: { label: "Synthetic", baseUrl: "https://api.synthetic.new/openai/v1", adapter: "openai-chat", dashboardUrl: "https://synthetic.new" },
39
- "qwen-portal": { label: "Qwen Portal", baseUrl: "https://portal.qwen.ai/v1", adapter: "openai-chat", dashboardUrl: "https://portal.qwen.ai" },
40
- qianfan: { label: "Qianfan (Baidu)", baseUrl: "https://qianfan.baidubce.com/v2", adapter: "openai-chat", dashboardUrl: "https://console.bce.baidu.com/iam/#/iam/apikey/list" },
41
- alibaba: { label: "Alibaba Coding Plan", baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1", adapter: "openai-chat", dashboardUrl: "https://dashscope.console.aliyun.com/apiKey" },
42
- parallel: { label: "Parallel", baseUrl: "https://platform.parallel.ai", adapter: "openai-chat", dashboardUrl: "https://platform.parallel.ai" },
43
- zenmux: { label: "ZenMux", baseUrl: "https://zenmux.ai/api/v1", adapter: "openai-chat", dashboardUrl: "https://zenmux.ai" },
44
- litellm: { label: "LiteLLM (self-hosted)", baseUrl: "http://localhost:4000/v1", adapter: "openai-chat", dashboardUrl: "https://docs.litellm.ai/docs/proxy/quick_start" },
45
- // Ollama Cloud — hosted (not local), OpenAI-compatible at /v1, Bearer key from ollama.com.
46
- // models/noVisionModels reflect the live ollama.com cloud lineup (the proxy still fetches /v1/models
47
- // live; this is the seed + the vision/text classification, web-verified against ollama.com search
48
- // filters). Vision-capable cloud models are EXCLUDED from noVisionModels: kimi-k2.5/.6/.7-code,
49
- // minimax-m3, gemma3/gemma4, qwen3.5, gemini-3-flash-preview, ministral-3, devstral-small-2,
50
- // mistral-large-3. gpt-oss is text-only despite a stale third-party list claiming otherwise.
51
- "ollama-cloud": {
52
- label: "Ollama Cloud",
53
- baseUrl: "https://ollama.com/v1",
54
- adapter: "openai-chat",
55
- dashboardUrl: "https://ollama.com/settings/keys",
56
- models: ["glm-5.2", "deepseek-v4-pro", "qwen3-coder", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5", "gemma4"],
57
- defaultModel: "glm-5.2",
58
- noVisionModels: [
59
- "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
60
- "minimax-m2.7", "minimax-m2.5", "minimax-m2.1",
61
- "nemotron-3-ultra", "nemotron-3-super",
62
- "deepseek-v4-pro", "deepseek-v4-flash",
63
- "gpt-oss", "qwen3-coder",
64
- ],
65
- },
66
- // ── Brought over from the jawcode provider registry ────────────────────────────────────
67
- // Real LLM API providers. CLI-agent integrations (cursor, github-copilot, gitlab-duo,
68
- // google-gemini-cli/antigravity, kilo, opencode, openai-codex) and native-cloud-auth providers
69
- // (amazon-bedrock, google-vertex) are intentionally excluded. baseUrls are taken from jawcode.
70
- mistral: { label: "Mistral", baseUrl: "https://api.mistral.ai/v1", adapter: "openai-chat", dashboardUrl: "https://console.mistral.ai/api-keys", defaultModel: "codestral-latest" },
71
- minimax: { label: "MiniMax", baseUrl: "https://api.minimax.io/v1", adapter: "openai-chat", dashboardUrl: "https://platform.minimax.io", defaultModel: "MiniMax-M2.5" },
72
- "minimax-cn": { label: "MiniMax (CN)", baseUrl: "https://api.minimaxi.com/v1", adapter: "openai-chat", dashboardUrl: "https://platform.minimaxi.com", defaultModel: "MiniMax-M2.5" },
73
- "kimi-code": { label: "Kimi (coding)", baseUrl: "https://api.kimi.com/coding/v1", adapter: "openai-chat", dashboardUrl: "https://platform.moonshot.cn/console/api-keys", defaultModel: "kimi-k2.5" },
74
- "opencode-zen": { label: "opencode zen", baseUrl: "https://opencode.ai/zen/v1", adapter: "openai-chat", dashboardUrl: "https://opencode.ai/auth" },
75
- "vercel-ai-gateway": { label: "Vercel AI Gateway", baseUrl: "https://ai-gateway.vercel.sh/v1", adapter: "openai-chat", dashboardUrl: "https://vercel.com/dashboard" },
76
- // Xiaomi MiMo exposes an Anthropic-compatible endpoint → anthropic adapter (x-api-key).
77
- xiaomi: { label: "Xiaomi MiMo", baseUrl: "https://api.xiaomimimo.com/anthropic", adapter: "anthropic", dashboardUrl: "https://xiaomimimo.com", defaultModel: "mimo-v2.5-pro" },
78
- // ── Gateways / multi-model proxies (standard wire; subscription-token auth) ──────────────
79
- // kilo: single-protocol OpenAI-compatible gateway (443 models). Cloudflare AI Gateway: anthropic
80
- // wire, URL is a template (fill in your account + gateway). github-copilot & gitlab-duo are
81
- // multi-model gateways whose models span 3 protocols on ONE host — mapped to their universal
82
- // OpenAI-compatible endpoint (one wire serves the whole lineup). Both need a Bearer subscription
83
- // token (not a plain API key), and copilot may need a `User-Agent` header via custom provider config.
84
- kilo: { label: "Kilo", baseUrl: "https://api.kilo.ai/api/gateway", adapter: "openai-chat", dashboardUrl: "https://kilo.ai" },
85
- "cloudflare-ai-gateway": { label: "Cloudflare AI Gateway", baseUrl: "https://gateway.ai.cloudflare.com/v1/{account-id}/{gateway}/anthropic", adapter: "anthropic", dashboardUrl: "https://dash.cloudflare.com/?to=/:account/ai/ai-gateway" },
86
- "github-copilot": { label: "GitHub Copilot", baseUrl: "https://api.githubcopilot.com", adapter: "openai-chat", dashboardUrl: "https://github.com/settings/copilot" },
87
- "gitlab-duo": { label: "GitLab Duo", baseUrl: "https://cloud.gitlab.com/ai/v1/proxy/openai/v1", adapter: "openai-chat", dashboardUrl: "https://gitlab.com/-/user_settings/personal_access_tokens" },
88
- };
27
+ export const KEY_LOGIN_PROVIDERS: Record<string, KeyLoginProvider> = deriveKeyLoginMap();
89
28
 
90
29
  /**
91
30
  * Copy a key-login catalog entry's seed/classification (`models`, `noVisionModels`,
@@ -0,0 +1,163 @@
1
+ import type { OcxProviderConfig } from "../types";
2
+ import { PROVIDER_REGISTRY, type ProviderRegistryEntry } from "./registry";
3
+
4
+ export interface DerivedKeyLoginProvider {
5
+ label: string;
6
+ baseUrl: string;
7
+ adapter: string;
8
+ dashboardUrl: string;
9
+ models?: string[];
10
+ defaultModel?: string;
11
+ noVisionModels?: string[];
12
+ noReasoningModels?: string[];
13
+ }
14
+
15
+ export interface DerivedInitProvider {
16
+ id: string;
17
+ label: string;
18
+ adapter: string;
19
+ baseUrl: string;
20
+ kind: "forward" | "oauth" | "key" | "local";
21
+ dashboardUrl?: string;
22
+ defaultModel?: string;
23
+ }
24
+
25
+ export interface DerivedProviderPreset {
26
+ id: string;
27
+ label: string;
28
+ adapter: string;
29
+ baseUrl: string;
30
+ defaultModel?: string;
31
+ auth: "oauth" | "forward" | "key";
32
+ oauthProvider?: string;
33
+ dashboardUrl?: string;
34
+ note?: string;
35
+ }
36
+
37
+ export function listRegistryEntries(): readonly ProviderRegistryEntry[] {
38
+ return PROVIDER_REGISTRY;
39
+ }
40
+
41
+ export function providerConfigSeed(entry: ProviderRegistryEntry): OcxProviderConfig {
42
+ return {
43
+ adapter: entry.adapter,
44
+ baseUrl: entry.baseUrl,
45
+ authMode: entry.authKind === "local" ? undefined : entry.authKind,
46
+ ...(entry.defaultModel ? { defaultModel: entry.defaultModel } : {}),
47
+ ...(entry.models ? { models: [...entry.models] } : {}),
48
+ ...(entry.noVisionModels ? { noVisionModels: [...entry.noVisionModels] } : {}),
49
+ ...(entry.noReasoningModels ? { noReasoningModels: [...entry.noReasoningModels] } : {}),
50
+ };
51
+ }
52
+
53
+ export function deriveKeyLoginMap(): Record<string, DerivedKeyLoginProvider> {
54
+ const out: Record<string, DerivedKeyLoginProvider> = {};
55
+ for (const entry of PROVIDER_REGISTRY) {
56
+ if (entry.authKind !== "key") continue;
57
+ if (!entry.dashboardUrl) throw new Error(`Registry key provider missing dashboardUrl: ${entry.id}`);
58
+ out[entry.id] = {
59
+ label: entry.label,
60
+ baseUrl: entry.baseUrl,
61
+ adapter: entry.adapter,
62
+ dashboardUrl: entry.dashboardUrl,
63
+ ...(entry.models ? { models: [...entry.models] } : {}),
64
+ ...(entry.defaultModel ? { defaultModel: entry.defaultModel } : {}),
65
+ ...(entry.noVisionModels ? { noVisionModels: [...entry.noVisionModels] } : {}),
66
+ ...(entry.noReasoningModels ? { noReasoningModels: [...entry.noReasoningModels] } : {}),
67
+ };
68
+ }
69
+ return out;
70
+ }
71
+
72
+ export function deriveInitProviders(): DerivedInitProvider[] {
73
+ return PROVIDER_REGISTRY.map(entry => ({
74
+ id: entry.id,
75
+ label: formatInitLabel(entry),
76
+ adapter: entry.adapter,
77
+ baseUrl: entry.baseUrl,
78
+ kind: entry.authKind,
79
+ ...(entry.dashboardUrl ? { dashboardUrl: entry.dashboardUrl } : {}),
80
+ ...(entry.defaultModel ? { defaultModel: entry.defaultModel } : {}),
81
+ }));
82
+ }
83
+
84
+ export function deriveOAuthProviderConfig(id: string): OcxProviderConfig | undefined {
85
+ const entry = PROVIDER_REGISTRY.find(row => row.id === id && row.authKind === "oauth");
86
+ return entry ? providerConfigSeed(entry) : undefined;
87
+ }
88
+
89
+ export function deriveOAuthDefaultModel(id: string): string | undefined {
90
+ return PROVIDER_REGISTRY.find(row => row.id === id && row.authKind === "oauth")?.defaultModel;
91
+ }
92
+
93
+ export function deriveOAuthIds(): string[] {
94
+ return PROVIDER_REGISTRY.filter(entry => entry.authKind === "oauth").map(entry => entry.oauthId ?? entry.id);
95
+ }
96
+
97
+ export function deriveProviderPresets(): DerivedProviderPreset[] {
98
+ const presets = PROVIDER_REGISTRY
99
+ .filter(entry => entry.featured || entry.authKind === "key")
100
+ .map(entryToPreset);
101
+ return [...dedupePresets(presets), customPreset()];
102
+ }
103
+
104
+ export function deriveFeaturedProviderIds(): string[] {
105
+ return PROVIDER_REGISTRY.filter(entry => entry.featured).map(entry => entry.id);
106
+ }
107
+
108
+ export function deriveJawcodeAliases(): Record<string, string> {
109
+ const aliases: Record<string, string> = {};
110
+ for (const entry of PROVIDER_REGISTRY) {
111
+ if (!entry.jawcodeBundle) continue;
112
+ aliases[entry.id] = entry.jawcodeBundle;
113
+ for (const alias of entry.extraMetadataAliases ?? []) {
114
+ aliases[alias] = entry.jawcodeBundle;
115
+ }
116
+ }
117
+ return aliases;
118
+ }
119
+
120
+ export function shouldCaseFoldMetadataModelId(providerId: string): boolean {
121
+ const entry = PROVIDER_REGISTRY.find(row => row.id === providerId);
122
+ return entry?.metadataModelIdNormalize === "case-insensitive";
123
+ }
124
+
125
+ function entryToPreset(entry: ProviderRegistryEntry): DerivedProviderPreset {
126
+ return {
127
+ id: entry.id,
128
+ label: entry.label,
129
+ adapter: entry.adapter,
130
+ baseUrl: entry.baseUrl,
131
+ auth: entry.authKind === "forward" ? "forward" : entry.authKind === "oauth" ? "oauth" : "key",
132
+ ...(entry.defaultModel ? { defaultModel: entry.defaultModel } : {}),
133
+ ...(entry.authKind === "oauth" ? { oauthProvider: entry.oauthId ?? entry.id } : {}),
134
+ ...(entry.dashboardUrl ? { dashboardUrl: entry.dashboardUrl } : {}),
135
+ ...(entry.note ? { note: entry.note } : {}),
136
+ };
137
+ }
138
+
139
+ function dedupePresets(presets: DerivedProviderPreset[]): DerivedProviderPreset[] {
140
+ const seen = new Set<string>();
141
+ const out: DerivedProviderPreset[] = [];
142
+ for (const preset of presets) {
143
+ if (seen.has(preset.id)) continue;
144
+ seen.add(preset.id);
145
+ out.push(preset);
146
+ }
147
+ return out;
148
+ }
149
+
150
+ function customPreset(): DerivedProviderPreset {
151
+ return { id: "custom", label: "Custom provider", adapter: "openai-chat", baseUrl: "", auth: "key" };
152
+ }
153
+
154
+ function formatInitLabel(entry: ProviderRegistryEntry): string {
155
+ if (entry.authKind === "forward") return "OpenAI — ChatGPT login (no key)";
156
+ if (entry.authKind === "oauth") {
157
+ if (entry.id === "xai") return "xAI (Grok) — account login";
158
+ if (entry.id === "anthropic") return "Anthropic (Claude) — account login";
159
+ if (entry.id === "kimi") return "Kimi (Moonshot) — account login";
160
+ return `${entry.label} — account login`;
161
+ }
162
+ return entry.label;
163
+ }