@austinthesing/magic-shell 0.2.18 → 0.2.19

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/dist/index.js CHANGED
@@ -2116,15 +2116,6 @@ import { cwd as getCwd } from "process";
2116
2116
 
2117
2117
  // src/lib/types.ts
2118
2118
  var OPENROUTER_MODELS = [
2119
- {
2120
- id: "minimax/minimax-m2.5:free",
2121
- name: "MiniMax M2.5 Free",
2122
- description: "Free MiniMax model for trying out open-source generation",
2123
- category: "smart",
2124
- provider: "openrouter",
2125
- contextLength: 196608,
2126
- free: true
2127
- },
2128
2119
  {
2129
2120
  id: "xiaomi/mimo-v2.5",
2130
2121
  name: "MiMo V2.5",
@@ -2157,6 +2148,38 @@ var OPENROUTER_MODELS = [
2157
2148
  provider: "openrouter",
2158
2149
  contextLength: 202752
2159
2150
  },
2151
+ {
2152
+ id: "openai/gpt-latest",
2153
+ name: "OpenAI GPT Latest",
2154
+ description: "OpenRouter alias that redirects to the latest OpenAI GPT model",
2155
+ category: "smart",
2156
+ provider: "openrouter",
2157
+ contextLength: 1050000
2158
+ },
2159
+ {
2160
+ id: "openai/gpt-5.5",
2161
+ name: "GPT 5.5",
2162
+ description: "OpenAI's latest flagship GPT model",
2163
+ category: "smart",
2164
+ provider: "openrouter",
2165
+ contextLength: 1050000
2166
+ },
2167
+ {
2168
+ id: "anthropic/claude-sonnet-latest",
2169
+ name: "Claude Sonnet Latest",
2170
+ description: "OpenRouter alias that redirects to the latest Claude Sonnet model",
2171
+ category: "smart",
2172
+ provider: "openrouter",
2173
+ contextLength: 1e6
2174
+ },
2175
+ {
2176
+ id: "anthropic/claude-sonnet-4.6",
2177
+ name: "Claude Sonnet 4.6",
2178
+ description: "Anthropic's latest Sonnet model",
2179
+ category: "smart",
2180
+ provider: "openrouter",
2181
+ contextLength: 1e6
2182
+ },
2160
2183
  {
2161
2184
  id: "moonshotai/kimi-k2.6",
2162
2185
  name: "Kimi K2.6",
@@ -2189,6 +2212,30 @@ var OPENROUTER_MODELS = [
2189
2212
  provider: "openrouter",
2190
2213
  contextLength: 196608
2191
2214
  },
2215
+ {
2216
+ id: "anthropic/claude-opus-latest",
2217
+ name: "Claude Opus Latest",
2218
+ description: "OpenRouter alias that redirects to the latest Claude Opus model",
2219
+ category: "reasoning",
2220
+ provider: "openrouter",
2221
+ contextLength: 1e6
2222
+ },
2223
+ {
2224
+ id: "anthropic/claude-opus-4.7",
2225
+ name: "Claude Opus 4.7",
2226
+ description: "Anthropic's latest Opus model",
2227
+ category: "reasoning",
2228
+ provider: "openrouter",
2229
+ contextLength: 1e6
2230
+ },
2231
+ {
2232
+ id: "openai/gpt-5.5-pro",
2233
+ name: "GPT 5.5 Pro",
2234
+ description: "OpenAI's latest high-capability reasoning model",
2235
+ category: "reasoning",
2236
+ provider: "openrouter",
2237
+ contextLength: 1050000
2238
+ },
2192
2239
  {
2193
2240
  id: "moonshotai/kimi-k2-thinking",
2194
2241
  name: "Kimi K2 Thinking",
@@ -2198,16 +2245,109 @@ var OPENROUTER_MODELS = [
2198
2245
  contextLength: 262144
2199
2246
  }
2200
2247
  ];
2201
- var OPENCODE_ZEN_MODELS = [
2248
+ var VERCEL_AI_GATEWAY_MODELS = [
2249
+ {
2250
+ id: "openai/gpt-latest",
2251
+ name: "OpenAI GPT Latest",
2252
+ description: "Vercel AI Gateway alias that redirects to the latest OpenAI GPT model",
2253
+ category: "smart",
2254
+ provider: "vercel-ai-gateway",
2255
+ contextLength: 1050000
2256
+ },
2202
2257
  {
2203
- id: "minimax-m2.5-free",
2204
- name: "MiniMax M2.5 Free",
2205
- description: "MiniMax's free model (limited time)",
2258
+ id: "openai/gpt-5.5",
2259
+ name: "GPT 5.5",
2260
+ description: "OpenAI's latest flagship GPT model",
2261
+ category: "smart",
2262
+ provider: "vercel-ai-gateway",
2263
+ contextLength: 1050000
2264
+ },
2265
+ {
2266
+ id: "anthropic/claude-sonnet-4.6",
2267
+ name: "Claude Sonnet 4.6",
2268
+ description: "Anthropic's latest Sonnet model",
2269
+ category: "smart",
2270
+ provider: "vercel-ai-gateway",
2271
+ contextLength: 1e6
2272
+ },
2273
+ {
2274
+ id: "anthropic/claude-opus-4.7",
2275
+ name: "Claude Opus 4.7",
2276
+ description: "Anthropic's latest Opus model",
2277
+ category: "reasoning",
2278
+ provider: "vercel-ai-gateway",
2279
+ contextLength: 1e6
2280
+ },
2281
+ {
2282
+ id: "openai/gpt-5.5-pro",
2283
+ name: "GPT 5.5 Pro",
2284
+ description: "OpenAI's latest high-capability reasoning model",
2285
+ category: "reasoning",
2286
+ provider: "vercel-ai-gateway",
2287
+ contextLength: 1050000
2288
+ }
2289
+ ];
2290
+ var CLOUDFLARE_AI_GATEWAY_MODELS = [
2291
+ {
2292
+ id: "openai/gpt-5.5",
2293
+ name: "GPT 5.5",
2294
+ description: "OpenAI's latest flagship GPT model through Cloudflare AI Gateway",
2295
+ category: "smart",
2296
+ provider: "cloudflare-ai-gateway",
2297
+ contextLength: 1050000
2298
+ },
2299
+ {
2300
+ id: "anthropic/claude-sonnet-4-6",
2301
+ name: "Claude Sonnet 4.6",
2302
+ description: "Anthropic's latest Sonnet model through Cloudflare AI Gateway",
2303
+ category: "smart",
2304
+ provider: "cloudflare-ai-gateway",
2305
+ contextLength: 1e6
2306
+ },
2307
+ {
2308
+ id: "anthropic/claude-opus-4-7",
2309
+ name: "Claude Opus 4.7",
2310
+ description: "Anthropic's latest Opus model through Cloudflare AI Gateway",
2311
+ category: "reasoning",
2312
+ provider: "cloudflare-ai-gateway",
2313
+ contextLength: 1e6
2314
+ },
2315
+ {
2316
+ id: "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast",
2317
+ name: "Workers AI Llama 3.3 70B Fast",
2318
+ description: "Cloudflare Workers AI fast Llama model routed through AI Gateway",
2319
+ category: "smart",
2320
+ provider: "cloudflare-ai-gateway",
2321
+ contextLength: 24000
2322
+ }
2323
+ ];
2324
+ var WORKERS_AI_MODELS = [
2325
+ {
2326
+ id: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
2327
+ name: "Llama 3.3 70B Fast",
2328
+ description: "Cloudflare Workers AI fast Llama instruct model",
2329
+ category: "smart",
2330
+ provider: "workers-ai",
2331
+ contextLength: 24000
2332
+ },
2333
+ {
2334
+ id: "@cf/meta/llama-3.1-8b-instruct",
2335
+ name: "Llama 3.1 8B Instruct",
2336
+ description: "Cloudflare Workers AI lightweight Llama instruct model",
2206
2337
  category: "fast",
2207
- provider: "opencode-zen",
2208
- contextLength: 196608,
2209
- free: true
2338
+ provider: "workers-ai",
2339
+ contextLength: 8000
2210
2340
  },
2341
+ {
2342
+ id: "@cf/openai/gpt-oss-120b",
2343
+ name: "GPT OSS 120B",
2344
+ description: "OpenAI open-weight model hosted by Cloudflare Workers AI",
2345
+ category: "reasoning",
2346
+ provider: "workers-ai",
2347
+ contextLength: 32000
2348
+ }
2349
+ ];
2350
+ var OPENCODE_ZEN_MODELS = [
2211
2351
  {
2212
2352
  id: "kimi-k2.6-free",
2213
2353
  name: "Kimi K2.6 Free",
@@ -2234,23 +2374,6 @@ var OPENCODE_ZEN_MODELS = [
2234
2374
  provider: "opencode-zen",
2235
2375
  contextLength: 1048576
2236
2376
  },
2237
- {
2238
- id: "gpt-5-nano",
2239
- name: "GPT 5 Nano",
2240
- description: "OpenAI's fastest GPT model (free)",
2241
- category: "fast",
2242
- provider: "opencode-zen",
2243
- contextLength: 200000,
2244
- free: true
2245
- },
2246
- {
2247
- id: "claude-3-5-haiku",
2248
- name: "Claude Haiku 3.5",
2249
- description: "Anthropic's fast and efficient model",
2250
- category: "fast",
2251
- provider: "opencode-zen",
2252
- contextLength: 200000
2253
- },
2254
2377
  {
2255
2378
  id: "claude-haiku-4-5",
2256
2379
  name: "Claude Haiku 4.5",
@@ -2268,84 +2391,68 @@ var OPENCODE_ZEN_MODELS = [
2268
2391
  contextLength: 200000
2269
2392
  },
2270
2393
  {
2271
- id: "gpt-5.1-codex-mini",
2272
- name: "GPT 5.1 Codex Mini",
2273
- description: "OpenAI's fast codex model",
2394
+ id: "gpt-5.4-mini",
2395
+ name: "GPT 5.4 Mini",
2396
+ description: "OpenAI's latest fast GPT mini model",
2274
2397
  category: "fast",
2275
2398
  provider: "opencode-zen",
2276
- contextLength: 200000
2399
+ contextLength: 400000
2277
2400
  },
2278
2401
  {
2279
- id: "deepseek-v4-flash",
2280
- name: "DeepSeek V4 Flash",
2281
- description: "DeepSeek's latest fast open-source model",
2402
+ id: "gpt-5.4-nano",
2403
+ name: "GPT 5.4 Nano",
2404
+ description: "OpenAI's latest lightweight GPT model",
2282
2405
  category: "fast",
2283
2406
  provider: "opencode-zen",
2284
- contextLength: 1048576
2407
+ contextLength: 400000
2285
2408
  },
2286
2409
  {
2287
- id: "claude-sonnet-4",
2288
- name: "Claude Sonnet 4",
2289
- description: "Anthropic's balanced model for complex tasks",
2410
+ id: "claude-sonnet-4-6",
2411
+ name: "Claude Sonnet 4.6",
2412
+ description: "Anthropic's latest Sonnet model",
2290
2413
  category: "smart",
2291
2414
  provider: "opencode-zen",
2292
- contextLength: 200000
2415
+ contextLength: 1e6
2293
2416
  },
2294
2417
  {
2295
- id: "gemini-3-pro",
2296
- name: "Gemini 3 Pro",
2418
+ id: "gemini-3.1-pro",
2419
+ name: "Gemini 3.1 Pro",
2297
2420
  description: "Google's high-end Gemini model",
2298
2421
  category: "smart",
2299
2422
  provider: "opencode-zen",
2300
2423
  contextLength: 200000
2301
2424
  },
2302
2425
  {
2303
- id: "gpt-5.2",
2304
- name: "GPT 5.2",
2305
- description: "OpenAI's flagship GPT model",
2426
+ id: "gpt-5.5",
2427
+ name: "GPT 5.5",
2428
+ description: "OpenAI's latest flagship GPT model",
2306
2429
  category: "smart",
2307
2430
  provider: "opencode-zen",
2308
- contextLength: 200000
2431
+ contextLength: 1050000
2309
2432
  },
2310
2433
  {
2311
- id: "gpt-5.2-codex",
2312
- name: "GPT 5.2 Codex",
2313
- description: "OpenAI's coding-focused GPT model",
2434
+ id: "gpt-5.5-pro",
2435
+ name: "GPT 5.5 Pro",
2436
+ description: "OpenAI's latest high-capability reasoning model",
2314
2437
  category: "smart",
2315
2438
  provider: "opencode-zen",
2316
- contextLength: 200000
2439
+ contextLength: 1050000
2317
2440
  },
2318
2441
  {
2319
- id: "gpt-5.1",
2320
- name: "GPT 5.1",
2321
- description: "OpenAI's balanced GPT model",
2442
+ id: "gpt-5.3-codex",
2443
+ name: "GPT 5.3 Codex",
2444
+ description: "OpenAI's latest coding-focused GPT model",
2322
2445
  category: "smart",
2323
2446
  provider: "opencode-zen",
2324
- contextLength: 200000
2447
+ contextLength: 400000
2325
2448
  },
2326
2449
  {
2327
- id: "gpt-5.1-codex",
2328
- name: "GPT 5.1 Codex",
2329
- description: "OpenAI's coding model",
2450
+ id: "gpt-5.3-codex-spark",
2451
+ name: "GPT 5.3 Codex Spark",
2452
+ description: "OpenAI's latest fast coding-focused GPT model",
2330
2453
  category: "smart",
2331
2454
  provider: "opencode-zen",
2332
- contextLength: 200000
2333
- },
2334
- {
2335
- id: "gpt-5",
2336
- name: "GPT 5",
2337
- description: "OpenAI's prior generation GPT model",
2338
- category: "smart",
2339
- provider: "opencode-zen",
2340
- contextLength: 200000
2341
- },
2342
- {
2343
- id: "gpt-5-codex",
2344
- name: "GPT 5 Codex",
2345
- description: "OpenAI's prior generation codex model",
2346
- category: "smart",
2347
- provider: "opencode-zen",
2348
- contextLength: 200000
2455
+ contextLength: 400000
2349
2456
  },
2350
2457
  {
2351
2458
  id: "minimax-m2.7",
@@ -2388,44 +2495,12 @@ var OPENCODE_ZEN_MODELS = [
2388
2495
  contextLength: 202752
2389
2496
  },
2390
2497
  {
2391
- id: "claude-sonnet-4-5",
2392
- name: "Claude Sonnet 4.5",
2393
- description: "Anthropic's hybrid reasoning model",
2498
+ id: "claude-opus-4-7",
2499
+ name: "Claude Opus 4.7",
2500
+ description: "Anthropic's latest Opus model",
2394
2501
  category: "reasoning",
2395
2502
  provider: "opencode-zen",
2396
- contextLength: 200000
2397
- },
2398
- {
2399
- id: "claude-opus-4-6",
2400
- name: "Claude Opus 4.6",
2401
- description: "Anthropic's newest Opus model",
2402
- category: "reasoning",
2403
- provider: "opencode-zen",
2404
- contextLength: 200000
2405
- },
2406
- {
2407
- id: "claude-opus-4-5",
2408
- name: "Claude Opus 4.5",
2409
- description: "Anthropic's most capable model",
2410
- category: "reasoning",
2411
- provider: "opencode-zen",
2412
- contextLength: 200000
2413
- },
2414
- {
2415
- id: "claude-opus-4-1",
2416
- name: "Claude Opus 4.1",
2417
- description: "Anthropic's powerful reasoning model",
2418
- category: "reasoning",
2419
- provider: "opencode-zen",
2420
- contextLength: 200000
2421
- },
2422
- {
2423
- id: "gpt-5.1-codex-max",
2424
- name: "GPT 5.1 Codex Max",
2425
- description: "OpenAI's largest coding model",
2426
- category: "reasoning",
2427
- provider: "opencode-zen",
2428
- contextLength: 200000
2503
+ contextLength: 1e6
2429
2504
  },
2430
2505
  {
2431
2506
  id: "kimi-k2-thinking",
@@ -2436,7 +2511,45 @@ var OPENCODE_ZEN_MODELS = [
2436
2511
  contextLength: 262144
2437
2512
  }
2438
2513
  ];
2439
- var ALL_MODELS = [...OPENCODE_ZEN_MODELS, ...OPENROUTER_MODELS];
2514
+ var ALL_MODELS = [
2515
+ ...OPENCODE_ZEN_MODELS,
2516
+ ...OPENROUTER_MODELS,
2517
+ ...VERCEL_AI_GATEWAY_MODELS,
2518
+ ...CLOUDFLARE_AI_GATEWAY_MODELS,
2519
+ ...WORKERS_AI_MODELS
2520
+ ];
2521
+ function getProviderModels(provider) {
2522
+ switch (provider) {
2523
+ case "opencode-zen":
2524
+ return OPENCODE_ZEN_MODELS;
2525
+ case "openrouter":
2526
+ return OPENROUTER_MODELS;
2527
+ case "vercel-ai-gateway":
2528
+ return VERCEL_AI_GATEWAY_MODELS;
2529
+ case "cloudflare-ai-gateway":
2530
+ return CLOUDFLARE_AI_GATEWAY_MODELS;
2531
+ case "workers-ai":
2532
+ return WORKERS_AI_MODELS;
2533
+ case "custom":
2534
+ return [];
2535
+ }
2536
+ }
2537
+ function getProviderDisplayName(provider) {
2538
+ switch (provider) {
2539
+ case "opencode-zen":
2540
+ return "OpenCode Zen";
2541
+ case "openrouter":
2542
+ return "OpenRouter";
2543
+ case "vercel-ai-gateway":
2544
+ return "Vercel AI Gateway";
2545
+ case "cloudflare-ai-gateway":
2546
+ return "Cloudflare AI Gateway";
2547
+ case "workers-ai":
2548
+ return "Cloudflare Workers AI";
2549
+ case "custom":
2550
+ return "Custom";
2551
+ }
2552
+ }
2440
2553
 
2441
2554
  // src/lib/config.ts
2442
2555
  import { homedir } from "os";
@@ -2804,10 +2917,18 @@ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
2804
2917
  var HISTORY_FILE = join(CONFIG_DIR, "history.json");
2805
2918
  var KEYCHAIN_OPENROUTER = "openrouter-api-key";
2806
2919
  var KEYCHAIN_OPENCODE_ZEN = "opencode-zen-api-key";
2920
+ var KEYCHAIN_VERCEL_AI_GATEWAY = "vercel-ai-gateway-api-key";
2921
+ var KEYCHAIN_CLOUDFLARE_AI_GATEWAY = "cloudflare-ai-gateway-api-key";
2922
+ var KEYCHAIN_WORKERS_AI = "workers-ai-api-key";
2807
2923
  var DEFAULT_CONFIG = {
2808
2924
  provider: "opencode-zen",
2809
2925
  openrouterApiKey: "",
2810
2926
  opencodeZenApiKey: "",
2927
+ vercelAiGatewayApiKey: "",
2928
+ cloudflareAiGatewayApiKey: "",
2929
+ workersAiApiKey: "",
2930
+ cloudflareAccountId: "",
2931
+ cloudflareAiGatewayId: "default",
2811
2932
  defaultModel: "kimi-k2.6-free",
2812
2933
  safetyLevel: "moderate",
2813
2934
  dryRunByDefault: false,
@@ -2847,6 +2968,9 @@ function saveConfig(config) {
2847
2968
  if (isSecureStorageAvailable()) {
2848
2969
  configToSave.openrouterApiKey = "";
2849
2970
  configToSave.opencodeZenApiKey = "";
2971
+ configToSave.vercelAiGatewayApiKey = "";
2972
+ configToSave.cloudflareAiGatewayApiKey = "";
2973
+ configToSave.workersAiApiKey = "";
2850
2974
  }
2851
2975
  writeFileSync(CONFIG_FILE, JSON.stringify(configToSave, null, 2));
2852
2976
  }
@@ -2859,28 +2983,83 @@ async function getApiKey(provider) {
2859
2983
  const envKey = process.env.OPENCODE_ZEN_API_KEY;
2860
2984
  if (envKey)
2861
2985
  return envKey;
2986
+ } else if (provider === "vercel-ai-gateway") {
2987
+ const envKey = process.env.AI_GATEWAY_API_KEY || process.env.VERCEL_AI_GATEWAY_API_KEY;
2988
+ if (envKey)
2989
+ return envKey;
2990
+ } else if (provider === "cloudflare-ai-gateway") {
2991
+ const envKey = process.env.CLOUDFLARE_AI_GATEWAY_API_KEY || process.env.CF_AIG_TOKEN;
2992
+ if (envKey)
2993
+ return envKey;
2994
+ } else if (provider === "workers-ai") {
2995
+ const envKey = process.env.CLOUDFLARE_API_TOKEN || process.env.CLOUDFLARE_API_KEY;
2996
+ if (envKey)
2997
+ return envKey;
2862
2998
  }
2863
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER : KEYCHAIN_OPENCODE_ZEN;
2999
+ const keychainKey = getKeychainKey(provider);
2864
3000
  const secureKey = await getSecret(keychainKey);
2865
3001
  if (secureKey)
2866
3002
  return secureKey;
2867
3003
  const config = loadConfig();
2868
- return provider === "openrouter" ? config.openrouterApiKey : config.opencodeZenApiKey;
3004
+ switch (provider) {
3005
+ case "openrouter":
3006
+ return config.openrouterApiKey;
3007
+ case "opencode-zen":
3008
+ return config.opencodeZenApiKey;
3009
+ case "vercel-ai-gateway":
3010
+ return config.vercelAiGatewayApiKey || "";
3011
+ case "cloudflare-ai-gateway":
3012
+ return config.cloudflareAiGatewayApiKey || "";
3013
+ case "workers-ai":
3014
+ return config.workersAiApiKey || "";
3015
+ case "custom":
3016
+ return "";
3017
+ }
2869
3018
  }
2870
3019
  async function setApiKey(provider, key) {
2871
3020
  const config = loadConfig();
2872
3021
  config.provider = provider;
2873
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER : KEYCHAIN_OPENCODE_ZEN;
3022
+ const keychainKey = getKeychainKey(provider);
2874
3023
  const stored = await setSecret(keychainKey, key);
2875
3024
  if (!stored) {
2876
- if (provider === "openrouter") {
2877
- config.openrouterApiKey = key;
2878
- } else {
2879
- config.opencodeZenApiKey = key;
3025
+ switch (provider) {
3026
+ case "openrouter":
3027
+ config.openrouterApiKey = key;
3028
+ break;
3029
+ case "opencode-zen":
3030
+ config.opencodeZenApiKey = key;
3031
+ break;
3032
+ case "vercel-ai-gateway":
3033
+ config.vercelAiGatewayApiKey = key;
3034
+ break;
3035
+ case "cloudflare-ai-gateway":
3036
+ config.cloudflareAiGatewayApiKey = key;
3037
+ break;
3038
+ case "workers-ai":
3039
+ config.workersAiApiKey = key;
3040
+ break;
3041
+ case "custom":
3042
+ break;
2880
3043
  }
2881
3044
  }
2882
3045
  saveConfig(config);
2883
3046
  }
3047
+ function getKeychainKey(provider) {
3048
+ switch (provider) {
3049
+ case "openrouter":
3050
+ return KEYCHAIN_OPENROUTER;
3051
+ case "opencode-zen":
3052
+ return KEYCHAIN_OPENCODE_ZEN;
3053
+ case "vercel-ai-gateway":
3054
+ return KEYCHAIN_VERCEL_AI_GATEWAY;
3055
+ case "cloudflare-ai-gateway":
3056
+ return KEYCHAIN_CLOUDFLARE_AI_GATEWAY;
3057
+ case "workers-ai":
3058
+ return KEYCHAIN_WORKERS_AI;
3059
+ case "custom":
3060
+ return "custom-api-key";
3061
+ }
3062
+ }
2884
3063
  function loadHistory() {
2885
3064
  ensureConfigDir();
2886
3065
  if (!existsSync(HISTORY_FILE)) {
@@ -41183,15 +41362,6 @@ var defaultDownload2 = createDownload();
41183
41362
 
41184
41363
  // src/lib/types.ts
41185
41364
  var OPENROUTER_MODELS2 = [
41186
- {
41187
- id: "minimax/minimax-m2.5:free",
41188
- name: "MiniMax M2.5 Free",
41189
- description: "Free MiniMax model for trying out open-source generation",
41190
- category: "smart",
41191
- provider: "openrouter",
41192
- contextLength: 196608,
41193
- free: true
41194
- },
41195
41365
  {
41196
41366
  id: "xiaomi/mimo-v2.5",
41197
41367
  name: "MiMo V2.5",
@@ -41224,6 +41394,38 @@ var OPENROUTER_MODELS2 = [
41224
41394
  provider: "openrouter",
41225
41395
  contextLength: 202752
41226
41396
  },
41397
+ {
41398
+ id: "openai/gpt-latest",
41399
+ name: "OpenAI GPT Latest",
41400
+ description: "OpenRouter alias that redirects to the latest OpenAI GPT model",
41401
+ category: "smart",
41402
+ provider: "openrouter",
41403
+ contextLength: 1050000
41404
+ },
41405
+ {
41406
+ id: "openai/gpt-5.5",
41407
+ name: "GPT 5.5",
41408
+ description: "OpenAI's latest flagship GPT model",
41409
+ category: "smart",
41410
+ provider: "openrouter",
41411
+ contextLength: 1050000
41412
+ },
41413
+ {
41414
+ id: "anthropic/claude-sonnet-latest",
41415
+ name: "Claude Sonnet Latest",
41416
+ description: "OpenRouter alias that redirects to the latest Claude Sonnet model",
41417
+ category: "smart",
41418
+ provider: "openrouter",
41419
+ contextLength: 1e6
41420
+ },
41421
+ {
41422
+ id: "anthropic/claude-sonnet-4.6",
41423
+ name: "Claude Sonnet 4.6",
41424
+ description: "Anthropic's latest Sonnet model",
41425
+ category: "smart",
41426
+ provider: "openrouter",
41427
+ contextLength: 1e6
41428
+ },
41227
41429
  {
41228
41430
  id: "moonshotai/kimi-k2.6",
41229
41431
  name: "Kimi K2.6",
@@ -41256,6 +41458,30 @@ var OPENROUTER_MODELS2 = [
41256
41458
  provider: "openrouter",
41257
41459
  contextLength: 196608
41258
41460
  },
41461
+ {
41462
+ id: "anthropic/claude-opus-latest",
41463
+ name: "Claude Opus Latest",
41464
+ description: "OpenRouter alias that redirects to the latest Claude Opus model",
41465
+ category: "reasoning",
41466
+ provider: "openrouter",
41467
+ contextLength: 1e6
41468
+ },
41469
+ {
41470
+ id: "anthropic/claude-opus-4.7",
41471
+ name: "Claude Opus 4.7",
41472
+ description: "Anthropic's latest Opus model",
41473
+ category: "reasoning",
41474
+ provider: "openrouter",
41475
+ contextLength: 1e6
41476
+ },
41477
+ {
41478
+ id: "openai/gpt-5.5-pro",
41479
+ name: "GPT 5.5 Pro",
41480
+ description: "OpenAI's latest high-capability reasoning model",
41481
+ category: "reasoning",
41482
+ provider: "openrouter",
41483
+ contextLength: 1050000
41484
+ },
41259
41485
  {
41260
41486
  id: "moonshotai/kimi-k2-thinking",
41261
41487
  name: "Kimi K2 Thinking",
@@ -41265,16 +41491,109 @@ var OPENROUTER_MODELS2 = [
41265
41491
  contextLength: 262144
41266
41492
  }
41267
41493
  ];
41268
- var OPENCODE_ZEN_MODELS2 = [
41494
+ var VERCEL_AI_GATEWAY_MODELS2 = [
41269
41495
  {
41270
- id: "minimax-m2.5-free",
41271
- name: "MiniMax M2.5 Free",
41272
- description: "MiniMax's free model (limited time)",
41496
+ id: "openai/gpt-latest",
41497
+ name: "OpenAI GPT Latest",
41498
+ description: "Vercel AI Gateway alias that redirects to the latest OpenAI GPT model",
41499
+ category: "smart",
41500
+ provider: "vercel-ai-gateway",
41501
+ contextLength: 1050000
41502
+ },
41503
+ {
41504
+ id: "openai/gpt-5.5",
41505
+ name: "GPT 5.5",
41506
+ description: "OpenAI's latest flagship GPT model",
41507
+ category: "smart",
41508
+ provider: "vercel-ai-gateway",
41509
+ contextLength: 1050000
41510
+ },
41511
+ {
41512
+ id: "anthropic/claude-sonnet-4.6",
41513
+ name: "Claude Sonnet 4.6",
41514
+ description: "Anthropic's latest Sonnet model",
41515
+ category: "smart",
41516
+ provider: "vercel-ai-gateway",
41517
+ contextLength: 1e6
41518
+ },
41519
+ {
41520
+ id: "anthropic/claude-opus-4.7",
41521
+ name: "Claude Opus 4.7",
41522
+ description: "Anthropic's latest Opus model",
41523
+ category: "reasoning",
41524
+ provider: "vercel-ai-gateway",
41525
+ contextLength: 1e6
41526
+ },
41527
+ {
41528
+ id: "openai/gpt-5.5-pro",
41529
+ name: "GPT 5.5 Pro",
41530
+ description: "OpenAI's latest high-capability reasoning model",
41531
+ category: "reasoning",
41532
+ provider: "vercel-ai-gateway",
41533
+ contextLength: 1050000
41534
+ }
41535
+ ];
41536
+ var CLOUDFLARE_AI_GATEWAY_MODELS2 = [
41537
+ {
41538
+ id: "openai/gpt-5.5",
41539
+ name: "GPT 5.5",
41540
+ description: "OpenAI's latest flagship GPT model through Cloudflare AI Gateway",
41541
+ category: "smart",
41542
+ provider: "cloudflare-ai-gateway",
41543
+ contextLength: 1050000
41544
+ },
41545
+ {
41546
+ id: "anthropic/claude-sonnet-4-6",
41547
+ name: "Claude Sonnet 4.6",
41548
+ description: "Anthropic's latest Sonnet model through Cloudflare AI Gateway",
41549
+ category: "smart",
41550
+ provider: "cloudflare-ai-gateway",
41551
+ contextLength: 1e6
41552
+ },
41553
+ {
41554
+ id: "anthropic/claude-opus-4-7",
41555
+ name: "Claude Opus 4.7",
41556
+ description: "Anthropic's latest Opus model through Cloudflare AI Gateway",
41557
+ category: "reasoning",
41558
+ provider: "cloudflare-ai-gateway",
41559
+ contextLength: 1e6
41560
+ },
41561
+ {
41562
+ id: "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast",
41563
+ name: "Workers AI Llama 3.3 70B Fast",
41564
+ description: "Cloudflare Workers AI fast Llama model routed through AI Gateway",
41565
+ category: "smart",
41566
+ provider: "cloudflare-ai-gateway",
41567
+ contextLength: 24000
41568
+ }
41569
+ ];
41570
+ var WORKERS_AI_MODELS2 = [
41571
+ {
41572
+ id: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
41573
+ name: "Llama 3.3 70B Fast",
41574
+ description: "Cloudflare Workers AI fast Llama instruct model",
41575
+ category: "smart",
41576
+ provider: "workers-ai",
41577
+ contextLength: 24000
41578
+ },
41579
+ {
41580
+ id: "@cf/meta/llama-3.1-8b-instruct",
41581
+ name: "Llama 3.1 8B Instruct",
41582
+ description: "Cloudflare Workers AI lightweight Llama instruct model",
41273
41583
  category: "fast",
41274
- provider: "opencode-zen",
41275
- contextLength: 196608,
41276
- free: true
41584
+ provider: "workers-ai",
41585
+ contextLength: 8000
41277
41586
  },
41587
+ {
41588
+ id: "@cf/openai/gpt-oss-120b",
41589
+ name: "GPT OSS 120B",
41590
+ description: "OpenAI open-weight model hosted by Cloudflare Workers AI",
41591
+ category: "reasoning",
41592
+ provider: "workers-ai",
41593
+ contextLength: 32000
41594
+ }
41595
+ ];
41596
+ var OPENCODE_ZEN_MODELS2 = [
41278
41597
  {
41279
41598
  id: "kimi-k2.6-free",
41280
41599
  name: "Kimi K2.6 Free",
@@ -41301,23 +41620,6 @@ var OPENCODE_ZEN_MODELS2 = [
41301
41620
  provider: "opencode-zen",
41302
41621
  contextLength: 1048576
41303
41622
  },
41304
- {
41305
- id: "gpt-5-nano",
41306
- name: "GPT 5 Nano",
41307
- description: "OpenAI's fastest GPT model (free)",
41308
- category: "fast",
41309
- provider: "opencode-zen",
41310
- contextLength: 200000,
41311
- free: true
41312
- },
41313
- {
41314
- id: "claude-3-5-haiku",
41315
- name: "Claude Haiku 3.5",
41316
- description: "Anthropic's fast and efficient model",
41317
- category: "fast",
41318
- provider: "opencode-zen",
41319
- contextLength: 200000
41320
- },
41321
41623
  {
41322
41624
  id: "claude-haiku-4-5",
41323
41625
  name: "Claude Haiku 4.5",
@@ -41335,84 +41637,68 @@ var OPENCODE_ZEN_MODELS2 = [
41335
41637
  contextLength: 200000
41336
41638
  },
41337
41639
  {
41338
- id: "gpt-5.1-codex-mini",
41339
- name: "GPT 5.1 Codex Mini",
41340
- description: "OpenAI's fast codex model",
41640
+ id: "gpt-5.4-mini",
41641
+ name: "GPT 5.4 Mini",
41642
+ description: "OpenAI's latest fast GPT mini model",
41341
41643
  category: "fast",
41342
41644
  provider: "opencode-zen",
41343
- contextLength: 200000
41645
+ contextLength: 400000
41344
41646
  },
41345
41647
  {
41346
- id: "deepseek-v4-flash",
41347
- name: "DeepSeek V4 Flash",
41348
- description: "DeepSeek's latest fast open-source model",
41648
+ id: "gpt-5.4-nano",
41649
+ name: "GPT 5.4 Nano",
41650
+ description: "OpenAI's latest lightweight GPT model",
41349
41651
  category: "fast",
41350
41652
  provider: "opencode-zen",
41351
- contextLength: 1048576
41653
+ contextLength: 400000
41352
41654
  },
41353
41655
  {
41354
- id: "claude-sonnet-4",
41355
- name: "Claude Sonnet 4",
41356
- description: "Anthropic's balanced model for complex tasks",
41656
+ id: "claude-sonnet-4-6",
41657
+ name: "Claude Sonnet 4.6",
41658
+ description: "Anthropic's latest Sonnet model",
41357
41659
  category: "smart",
41358
41660
  provider: "opencode-zen",
41359
- contextLength: 200000
41661
+ contextLength: 1e6
41360
41662
  },
41361
41663
  {
41362
- id: "gemini-3-pro",
41363
- name: "Gemini 3 Pro",
41664
+ id: "gemini-3.1-pro",
41665
+ name: "Gemini 3.1 Pro",
41364
41666
  description: "Google's high-end Gemini model",
41365
41667
  category: "smart",
41366
41668
  provider: "opencode-zen",
41367
41669
  contextLength: 200000
41368
41670
  },
41369
41671
  {
41370
- id: "gpt-5.2",
41371
- name: "GPT 5.2",
41372
- description: "OpenAI's flagship GPT model",
41672
+ id: "gpt-5.5",
41673
+ name: "GPT 5.5",
41674
+ description: "OpenAI's latest flagship GPT model",
41373
41675
  category: "smart",
41374
41676
  provider: "opencode-zen",
41375
- contextLength: 200000
41677
+ contextLength: 1050000
41376
41678
  },
41377
41679
  {
41378
- id: "gpt-5.2-codex",
41379
- name: "GPT 5.2 Codex",
41380
- description: "OpenAI's coding-focused GPT model",
41680
+ id: "gpt-5.5-pro",
41681
+ name: "GPT 5.5 Pro",
41682
+ description: "OpenAI's latest high-capability reasoning model",
41381
41683
  category: "smart",
41382
41684
  provider: "opencode-zen",
41383
- contextLength: 200000
41685
+ contextLength: 1050000
41384
41686
  },
41385
41687
  {
41386
- id: "gpt-5.1",
41387
- name: "GPT 5.1",
41388
- description: "OpenAI's balanced GPT model",
41688
+ id: "gpt-5.3-codex",
41689
+ name: "GPT 5.3 Codex",
41690
+ description: "OpenAI's latest coding-focused GPT model",
41389
41691
  category: "smart",
41390
41692
  provider: "opencode-zen",
41391
- contextLength: 200000
41693
+ contextLength: 400000
41392
41694
  },
41393
41695
  {
41394
- id: "gpt-5.1-codex",
41395
- name: "GPT 5.1 Codex",
41396
- description: "OpenAI's coding model",
41696
+ id: "gpt-5.3-codex-spark",
41697
+ name: "GPT 5.3 Codex Spark",
41698
+ description: "OpenAI's latest fast coding-focused GPT model",
41397
41699
  category: "smart",
41398
41700
  provider: "opencode-zen",
41399
- contextLength: 200000
41400
- },
41401
- {
41402
- id: "gpt-5",
41403
- name: "GPT 5",
41404
- description: "OpenAI's prior generation GPT model",
41405
- category: "smart",
41406
- provider: "opencode-zen",
41407
- contextLength: 200000
41408
- },
41409
- {
41410
- id: "gpt-5-codex",
41411
- name: "GPT 5 Codex",
41412
- description: "OpenAI's prior generation codex model",
41413
- category: "smart",
41414
- provider: "opencode-zen",
41415
- contextLength: 200000
41701
+ contextLength: 400000
41416
41702
  },
41417
41703
  {
41418
41704
  id: "minimax-m2.7",
@@ -41455,44 +41741,12 @@ var OPENCODE_ZEN_MODELS2 = [
41455
41741
  contextLength: 202752
41456
41742
  },
41457
41743
  {
41458
- id: "claude-sonnet-4-5",
41459
- name: "Claude Sonnet 4.5",
41460
- description: "Anthropic's hybrid reasoning model",
41744
+ id: "claude-opus-4-7",
41745
+ name: "Claude Opus 4.7",
41746
+ description: "Anthropic's latest Opus model",
41461
41747
  category: "reasoning",
41462
41748
  provider: "opencode-zen",
41463
- contextLength: 200000
41464
- },
41465
- {
41466
- id: "claude-opus-4-6",
41467
- name: "Claude Opus 4.6",
41468
- description: "Anthropic's newest Opus model",
41469
- category: "reasoning",
41470
- provider: "opencode-zen",
41471
- contextLength: 200000
41472
- },
41473
- {
41474
- id: "claude-opus-4-5",
41475
- name: "Claude Opus 4.5",
41476
- description: "Anthropic's most capable model",
41477
- category: "reasoning",
41478
- provider: "opencode-zen",
41479
- contextLength: 200000
41480
- },
41481
- {
41482
- id: "claude-opus-4-1",
41483
- name: "Claude Opus 4.1",
41484
- description: "Anthropic's powerful reasoning model",
41485
- category: "reasoning",
41486
- provider: "opencode-zen",
41487
- contextLength: 200000
41488
- },
41489
- {
41490
- id: "gpt-5.1-codex-max",
41491
- name: "GPT 5.1 Codex Max",
41492
- description: "OpenAI's largest coding model",
41493
- category: "reasoning",
41494
- provider: "opencode-zen",
41495
- contextLength: 200000
41749
+ contextLength: 1e6
41496
41750
  },
41497
41751
  {
41498
41752
  id: "kimi-k2-thinking",
@@ -41503,22 +41757,257 @@ var OPENCODE_ZEN_MODELS2 = [
41503
41757
  contextLength: 262144
41504
41758
  }
41505
41759
  ];
41506
- var ALL_MODELS2 = [...OPENCODE_ZEN_MODELS2, ...OPENROUTER_MODELS2];
41760
+ var ALL_MODELS2 = [
41761
+ ...OPENCODE_ZEN_MODELS2,
41762
+ ...OPENROUTER_MODELS2,
41763
+ ...VERCEL_AI_GATEWAY_MODELS2,
41764
+ ...CLOUDFLARE_AI_GATEWAY_MODELS2,
41765
+ ...WORKERS_AI_MODELS2
41766
+ ];
41767
+ function getProviderModels2(provider) {
41768
+ switch (provider) {
41769
+ case "opencode-zen":
41770
+ return OPENCODE_ZEN_MODELS2;
41771
+ case "openrouter":
41772
+ return OPENROUTER_MODELS2;
41773
+ case "vercel-ai-gateway":
41774
+ return VERCEL_AI_GATEWAY_MODELS2;
41775
+ case "cloudflare-ai-gateway":
41776
+ return CLOUDFLARE_AI_GATEWAY_MODELS2;
41777
+ case "workers-ai":
41778
+ return WORKERS_AI_MODELS2;
41779
+ case "custom":
41780
+ return [];
41781
+ }
41782
+ }
41783
+ function getProviderDisplayName2(provider) {
41784
+ switch (provider) {
41785
+ case "opencode-zen":
41786
+ return "OpenCode Zen";
41787
+ case "openrouter":
41788
+ return "OpenRouter";
41789
+ case "vercel-ai-gateway":
41790
+ return "Vercel AI Gateway";
41791
+ case "cloudflare-ai-gateway":
41792
+ return "Cloudflare AI Gateway";
41793
+ case "workers-ai":
41794
+ return "Cloudflare Workers AI";
41795
+ case "custom":
41796
+ return "Custom";
41797
+ }
41798
+ }
41507
41799
  function isCustomModel(model) {
41508
41800
  return "baseUrl" in model;
41509
41801
  }
41510
41802
 
41803
+ // src/lib/config.ts
41804
+ import { homedir as homedir2 } from "os";
41805
+ import { join as join2 } from "path";
41806
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
41807
+ var CONFIG_DIR2 = join2(homedir2(), ".magic-shell");
41808
+ var CONFIG_FILE2 = join2(CONFIG_DIR2, "config.json");
41809
+ var HISTORY_FILE2 = join2(CONFIG_DIR2, "history.json");
41810
+ var KEYCHAIN_OPENROUTER2 = "openrouter-api-key";
41811
+ var KEYCHAIN_OPENCODE_ZEN2 = "opencode-zen-api-key";
41812
+ var KEYCHAIN_VERCEL_AI_GATEWAY2 = "vercel-ai-gateway-api-key";
41813
+ var KEYCHAIN_CLOUDFLARE_AI_GATEWAY2 = "cloudflare-ai-gateway-api-key";
41814
+ var KEYCHAIN_WORKERS_AI2 = "workers-ai-api-key";
41815
+ var DEFAULT_CONFIG2 = {
41816
+ provider: "opencode-zen",
41817
+ openrouterApiKey: "",
41818
+ opencodeZenApiKey: "",
41819
+ vercelAiGatewayApiKey: "",
41820
+ cloudflareAiGatewayApiKey: "",
41821
+ workersAiApiKey: "",
41822
+ cloudflareAccountId: "",
41823
+ cloudflareAiGatewayId: "default",
41824
+ defaultModel: "kimi-k2.6-free",
41825
+ safetyLevel: "moderate",
41826
+ dryRunByDefault: false,
41827
+ blockedCommands: [
41828
+ ":(){ :|:& };:",
41829
+ "> /dev/sda",
41830
+ "mkfs",
41831
+ "dd if=/dev/zero",
41832
+ "chmod -R 777 /",
41833
+ "chown -R"
41834
+ ],
41835
+ confirmedDangerousPatterns: [],
41836
+ repoContext: false,
41837
+ customModels: []
41838
+ };
41839
+ function ensureConfigDir2() {
41840
+ if (!existsSync2(CONFIG_DIR2)) {
41841
+ mkdirSync2(CONFIG_DIR2, { recursive: true });
41842
+ }
41843
+ }
41844
+ function loadConfig2() {
41845
+ ensureConfigDir2();
41846
+ if (!existsSync2(CONFIG_FILE2)) {
41847
+ return { ...DEFAULT_CONFIG2 };
41848
+ }
41849
+ try {
41850
+ const data = readFileSync2(CONFIG_FILE2, "utf-8");
41851
+ const loaded = JSON.parse(data);
41852
+ return { ...DEFAULT_CONFIG2, ...loaded };
41853
+ } catch {
41854
+ return { ...DEFAULT_CONFIG2 };
41855
+ }
41856
+ }
41857
+ function saveConfig2(config2) {
41858
+ ensureConfigDir2();
41859
+ const configToSave = { ...config2 };
41860
+ if (isSecureStorageAvailable()) {
41861
+ configToSave.openrouterApiKey = "";
41862
+ configToSave.opencodeZenApiKey = "";
41863
+ configToSave.vercelAiGatewayApiKey = "";
41864
+ configToSave.cloudflareAiGatewayApiKey = "";
41865
+ configToSave.workersAiApiKey = "";
41866
+ }
41867
+ writeFileSync2(CONFIG_FILE2, JSON.stringify(configToSave, null, 2));
41868
+ }
41869
+ async function getApiKey2(provider) {
41870
+ if (provider === "openrouter") {
41871
+ const envKey = process.env.OPENROUTER_API_KEY;
41872
+ if (envKey)
41873
+ return envKey;
41874
+ } else if (provider === "opencode-zen") {
41875
+ const envKey = process.env.OPENCODE_ZEN_API_KEY;
41876
+ if (envKey)
41877
+ return envKey;
41878
+ } else if (provider === "vercel-ai-gateway") {
41879
+ const envKey = process.env.AI_GATEWAY_API_KEY || process.env.VERCEL_AI_GATEWAY_API_KEY;
41880
+ if (envKey)
41881
+ return envKey;
41882
+ } else if (provider === "cloudflare-ai-gateway") {
41883
+ const envKey = process.env.CLOUDFLARE_AI_GATEWAY_API_KEY || process.env.CF_AIG_TOKEN;
41884
+ if (envKey)
41885
+ return envKey;
41886
+ } else if (provider === "workers-ai") {
41887
+ const envKey = process.env.CLOUDFLARE_API_TOKEN || process.env.CLOUDFLARE_API_KEY;
41888
+ if (envKey)
41889
+ return envKey;
41890
+ }
41891
+ const keychainKey = getKeychainKey2(provider);
41892
+ const secureKey = await getSecret(keychainKey);
41893
+ if (secureKey)
41894
+ return secureKey;
41895
+ const config2 = loadConfig2();
41896
+ switch (provider) {
41897
+ case "openrouter":
41898
+ return config2.openrouterApiKey;
41899
+ case "opencode-zen":
41900
+ return config2.opencodeZenApiKey;
41901
+ case "vercel-ai-gateway":
41902
+ return config2.vercelAiGatewayApiKey || "";
41903
+ case "cloudflare-ai-gateway":
41904
+ return config2.cloudflareAiGatewayApiKey || "";
41905
+ case "workers-ai":
41906
+ return config2.workersAiApiKey || "";
41907
+ case "custom":
41908
+ return "";
41909
+ }
41910
+ }
41911
+ async function setApiKey2(provider, key) {
41912
+ const config2 = loadConfig2();
41913
+ config2.provider = provider;
41914
+ const keychainKey = getKeychainKey2(provider);
41915
+ const stored = await setSecret(keychainKey, key);
41916
+ if (!stored) {
41917
+ switch (provider) {
41918
+ case "openrouter":
41919
+ config2.openrouterApiKey = key;
41920
+ break;
41921
+ case "opencode-zen":
41922
+ config2.opencodeZenApiKey = key;
41923
+ break;
41924
+ case "vercel-ai-gateway":
41925
+ config2.vercelAiGatewayApiKey = key;
41926
+ break;
41927
+ case "cloudflare-ai-gateway":
41928
+ config2.cloudflareAiGatewayApiKey = key;
41929
+ break;
41930
+ case "workers-ai":
41931
+ config2.workersAiApiKey = key;
41932
+ break;
41933
+ case "custom":
41934
+ break;
41935
+ }
41936
+ }
41937
+ saveConfig2(config2);
41938
+ }
41939
+ function getKeychainKey2(provider) {
41940
+ switch (provider) {
41941
+ case "openrouter":
41942
+ return KEYCHAIN_OPENROUTER2;
41943
+ case "opencode-zen":
41944
+ return KEYCHAIN_OPENCODE_ZEN2;
41945
+ case "vercel-ai-gateway":
41946
+ return KEYCHAIN_VERCEL_AI_GATEWAY2;
41947
+ case "cloudflare-ai-gateway":
41948
+ return KEYCHAIN_CLOUDFLARE_AI_GATEWAY2;
41949
+ case "workers-ai":
41950
+ return KEYCHAIN_WORKERS_AI2;
41951
+ case "custom":
41952
+ return "custom-api-key";
41953
+ }
41954
+ }
41955
+ function loadHistory2() {
41956
+ ensureConfigDir2();
41957
+ if (!existsSync2(HISTORY_FILE2)) {
41958
+ return [];
41959
+ }
41960
+ try {
41961
+ const data = readFileSync2(HISTORY_FILE2, "utf-8");
41962
+ return JSON.parse(data);
41963
+ } catch {
41964
+ return [];
41965
+ }
41966
+ }
41967
+ function saveHistory(history) {
41968
+ ensureConfigDir2();
41969
+ const trimmed = history.slice(-100);
41970
+ writeFileSync2(HISTORY_FILE2, JSON.stringify(trimmed, null, 2));
41971
+ }
41972
+ function addToHistory(entry) {
41973
+ const history = loadHistory2();
41974
+ history.push(entry);
41975
+ saveHistory(history);
41976
+ }
41977
+ function getCustomModels2() {
41978
+ const config2 = loadConfig2();
41979
+ return config2.customModels || [];
41980
+ }
41981
+ async function getCustomModel2(id) {
41982
+ const customModels = getCustomModels2();
41983
+ const model = customModels.find((m) => m.id === id);
41984
+ if (!model) {
41985
+ return;
41986
+ }
41987
+ const keychainKey = `customModel:${model.id}:apiKey`;
41988
+ try {
41989
+ const apiKey = await getSecret(keychainKey);
41990
+ return apiKey ? { ...model, apiKey } : model;
41991
+ } catch (error40) {
41992
+ if (process.env.DEBUG_API === "1") {
41993
+ const message = error40 instanceof Error ? error40.message : String(error40);
41994
+ console.error(`[DEBUG] Custom model keychain get error: ${message}`);
41995
+ }
41996
+ return model;
41997
+ }
41998
+ }
41999
+
41511
42000
  // src/lib/shell.ts
41512
42001
  import { execSync } from "child_process";
41513
- import { existsSync as existsSync2 } from "fs";
41514
- import { homedir as homedir2 } from "os";
42002
+ import { existsSync as existsSync3 } from "fs";
42003
+ import { homedir as homedir3 } from "os";
41515
42004
  function detectShell() {
41516
42005
  const platform = detectPlatform();
41517
42006
  const isWSL = detectWSL();
41518
42007
  const shellPath = getShellPath();
41519
42008
  const shell2 = parseShellType(shellPath);
41520
42009
  const terminalEmulator = detectTerminalEmulator();
41521
- const homeDir = homedir2();
42010
+ const homeDir = homedir3();
41522
42011
  return {
41523
42012
  shell: shell2,
41524
42013
  shellPath,
@@ -41548,7 +42037,7 @@ function detectWSL() {
41548
42037
  if (release.includes("microsoft") || release.includes("wsl")) {
41549
42038
  return true;
41550
42039
  }
41551
- if (existsSync2("/proc/sys/fs/binfmt_misc/WSLInterop")) {
42040
+ if (existsSync3("/proc/sys/fs/binfmt_misc/WSLInterop")) {
41552
42041
  return true;
41553
42042
  }
41554
42043
  return false;
@@ -41708,34 +42197,34 @@ function getPlatformPaths(platform) {
41708
42197
  }
41709
42198
 
41710
42199
  // src/lib/repo-context.ts
41711
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
41712
- import { join as join2 } from "path";
42200
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
42201
+ import { join as join3 } from "path";
41713
42202
  function detectRepoContext(cwd) {
41714
42203
  const context2 = {
41715
42204
  type: "unknown"
41716
42205
  };
41717
42206
  let detected = false;
41718
- if (existsSync3(join2(cwd, ".git"))) {
42207
+ if (existsSync4(join3(cwd, ".git"))) {
41719
42208
  context2.hasGit = true;
41720
42209
  detected = true;
41721
42210
  }
41722
- if (existsSync3(join2(cwd, "Dockerfile")) || existsSync3(join2(cwd, "docker-compose.yml")) || existsSync3(join2(cwd, "docker-compose.yaml"))) {
42211
+ if (existsSync4(join3(cwd, "Dockerfile")) || existsSync4(join3(cwd, "docker-compose.yml")) || existsSync4(join3(cwd, "docker-compose.yaml"))) {
41723
42212
  context2.hasDocker = true;
41724
42213
  detected = true;
41725
42214
  }
41726
- const packageJsonPath = join2(cwd, "package.json");
41727
- if (existsSync3(packageJsonPath)) {
42215
+ const packageJsonPath = join3(cwd, "package.json");
42216
+ if (existsSync4(packageJsonPath)) {
41728
42217
  detected = true;
41729
42218
  context2.type = "node";
41730
42219
  try {
41731
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
41732
- if (existsSync3(join2(cwd, "bun.lockb")) || existsSync3(join2(cwd, "bun.lock"))) {
42220
+ const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
42221
+ if (existsSync4(join3(cwd, "bun.lockb")) || existsSync4(join3(cwd, "bun.lock"))) {
41733
42222
  context2.packageManager = "bun";
41734
- } else if (existsSync3(join2(cwd, "pnpm-lock.yaml"))) {
42223
+ } else if (existsSync4(join3(cwd, "pnpm-lock.yaml"))) {
41735
42224
  context2.packageManager = "pnpm";
41736
- } else if (existsSync3(join2(cwd, "yarn.lock"))) {
42225
+ } else if (existsSync4(join3(cwd, "yarn.lock"))) {
41737
42226
  context2.packageManager = "yarn";
41738
- } else if (existsSync3(join2(cwd, "package-lock.json"))) {
42227
+ } else if (existsSync4(join3(cwd, "package-lock.json"))) {
41739
42228
  context2.packageManager = "npm";
41740
42229
  } else if (packageJson.packageManager) {
41741
42230
  const pm = packageJson.packageManager.split("@")[0];
@@ -41746,13 +42235,13 @@ function detectRepoContext(cwd) {
41746
42235
  }
41747
42236
  } catch {}
41748
42237
  }
41749
- const makefilePath = join2(cwd, "Makefile");
41750
- if (existsSync3(makefilePath)) {
42238
+ const makefilePath = join3(cwd, "Makefile");
42239
+ if (existsSync4(makefilePath)) {
41751
42240
  detected = true;
41752
42241
  if (context2.type === "unknown")
41753
42242
  context2.type = "make";
41754
42243
  try {
41755
- const makefile = readFileSync2(makefilePath, "utf-8");
42244
+ const makefile = readFileSync3(makefilePath, "utf-8");
41756
42245
  const targetRegex = /^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:/gm;
41757
42246
  const targets = [];
41758
42247
  let match;
@@ -41766,17 +42255,17 @@ function detectRepoContext(cwd) {
41766
42255
  }
41767
42256
  } catch {}
41768
42257
  }
41769
- if (existsSync3(join2(cwd, "Cargo.toml"))) {
42258
+ if (existsSync4(join3(cwd, "Cargo.toml"))) {
41770
42259
  detected = true;
41771
42260
  context2.type = "rust";
41772
42261
  context2.cargoCommands = ["build", "run", "test", "check", "clippy", "fmt", "doc"];
41773
42262
  }
41774
- if (existsSync3(join2(cwd, "pyproject.toml")) || existsSync3(join2(cwd, "setup.py")) || existsSync3(join2(cwd, "requirements.txt"))) {
42263
+ if (existsSync4(join3(cwd, "pyproject.toml")) || existsSync4(join3(cwd, "setup.py")) || existsSync4(join3(cwd, "requirements.txt"))) {
41775
42264
  detected = true;
41776
42265
  if (context2.type === "unknown")
41777
42266
  context2.type = "python";
41778
42267
  }
41779
- if (existsSync3(join2(cwd, "go.mod"))) {
42268
+ if (existsSync4(join3(cwd, "go.mod"))) {
41780
42269
  detected = true;
41781
42270
  if (context2.type === "unknown")
41782
42271
  context2.type = "go";
@@ -41814,9 +42303,6 @@ function formatRepoContext(context2) {
41814
42303
 
41815
42304
  // src/lib/api.ts
41816
42305
  function getZenApiType(modelId) {
41817
- if (modelId === "minimax-m2.5-free") {
41818
- return "anthropic";
41819
- }
41820
42306
  if (modelId.startsWith("gpt-")) {
41821
42307
  return "openai-responses";
41822
42308
  }
@@ -41946,6 +42432,81 @@ async function callOpenRouter(apiKey, modelId, systemPrompt, userInput) {
41946
42432
  }
41947
42433
  return data.choices[0]?.message?.content?.trim() || "";
41948
42434
  }
42435
+ async function callOpenAICompatibleFetch(baseURL, apiKey, modelId, systemPrompt, userInput, headers = {}, includeAuthorization = true) {
42436
+ const requestHeaders = {
42437
+ "Content-Type": "application/json",
42438
+ ...headers
42439
+ };
42440
+ if (includeAuthorization) {
42441
+ requestHeaders.Authorization = `Bearer ${apiKey}`;
42442
+ }
42443
+ const response = await fetch(`${baseURL.replace(/\/$/, "")}/chat/completions`, {
42444
+ method: "POST",
42445
+ headers: requestHeaders,
42446
+ body: JSON.stringify({
42447
+ model: modelId,
42448
+ messages: [
42449
+ { role: "system", content: systemPrompt },
42450
+ { role: "user", content: userInput }
42451
+ ],
42452
+ max_tokens: 500,
42453
+ temperature: 0.1,
42454
+ stream: false
42455
+ })
42456
+ });
42457
+ if (!response.ok) {
42458
+ const errorText = await response.text();
42459
+ let errorMessage = `API request failed: ${response.status}`;
42460
+ try {
42461
+ const errorData = JSON.parse(errorText);
42462
+ if (errorData.error?.message) {
42463
+ errorMessage = errorData.error.message;
42464
+ } else if (errorData.errors?.[0]?.message) {
42465
+ errorMessage = errorData.errors[0].message;
42466
+ }
42467
+ } catch {}
42468
+ throw new Error(errorMessage);
42469
+ }
42470
+ const data = await response.json();
42471
+ if (data.error) {
42472
+ throw new Error(data.error.message);
42473
+ }
42474
+ if (data.errors?.[0]?.message) {
42475
+ throw new Error(data.errors[0].message);
42476
+ }
42477
+ const choices = data.choices || data.result?.choices;
42478
+ return choices?.[0]?.message?.content?.trim() || "";
42479
+ }
42480
+ function getCloudflareAccountId(config2) {
42481
+ return config2.cloudflareAccountId || process.env.CLOUDFLARE_ACCOUNT_ID || process.env.CF_ACCOUNT_ID || "";
42482
+ }
42483
+ function getCloudflareGatewayId(config2) {
42484
+ return config2.cloudflareAiGatewayId || process.env.CLOUDFLARE_AI_GATEWAY_ID || process.env.CF_AIG_GATEWAY_ID || "default";
42485
+ }
42486
+ async function callGatewayProvider(provider, apiKey, modelId, systemPrompt, userInput) {
42487
+ const config2 = loadConfig2();
42488
+ switch (provider) {
42489
+ case "vercel-ai-gateway":
42490
+ return await callOpenAICompatibleFetch("https://ai-gateway.vercel.sh/v1", apiKey, modelId, systemPrompt, userInput);
42491
+ case "cloudflare-ai-gateway": {
42492
+ const accountId = getCloudflareAccountId(config2);
42493
+ if (!accountId) {
42494
+ throw new Error("Cloudflare account ID is required. Set cloudflareAccountId in config or CLOUDFLARE_ACCOUNT_ID.");
42495
+ }
42496
+ const gatewayId = getCloudflareGatewayId(config2);
42497
+ return await callOpenAICompatibleFetch(`https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/compat`, apiKey, modelId, systemPrompt, userInput, { "cf-aig-authorization": `Bearer ${apiKey}` }, false);
42498
+ }
42499
+ case "workers-ai": {
42500
+ const accountId = getCloudflareAccountId(config2);
42501
+ if (!accountId) {
42502
+ throw new Error("Cloudflare account ID is required. Set cloudflareAccountId in config or CLOUDFLARE_ACCOUNT_ID.");
42503
+ }
42504
+ return await callOpenAICompatibleFetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`, apiKey, modelId, systemPrompt, userInput);
42505
+ }
42506
+ default:
42507
+ throw new Error(`Unsupported gateway provider: ${provider}`);
42508
+ }
42509
+ }
41949
42510
  var DEBUG_API = process.env.DEBUG_API === "1";
41950
42511
  async function generateZenText(model, systemPrompt, userInput) {
41951
42512
  const { text: text2 } = await generateText({
@@ -42072,6 +42633,8 @@ async function translateToCommand(apiKey, model, userInput, cwd, history = [], r
42072
42633
  rawCommand = await callCustomModel(model, systemPrompt, userInput);
42073
42634
  } else if (model.provider === "openrouter") {
42074
42635
  rawCommand = await callOpenRouter(apiKey, model.id, systemPrompt, userInput);
42636
+ } else if (model.provider === "vercel-ai-gateway" || model.provider === "cloudflare-ai-gateway" || model.provider === "workers-ai") {
42637
+ rawCommand = await callGatewayProvider(model.provider, apiKey, model.id, systemPrompt, userInput);
42075
42638
  } else {
42076
42639
  const apiType = getZenApiType(model.id);
42077
42640
  switch (apiType) {
@@ -42096,137 +42659,6 @@ async function translateToCommand(apiKey, model, userInput, cwd, history = [], r
42096
42659
  return cleaned;
42097
42660
  }
42098
42661
 
42099
- // src/lib/config.ts
42100
- import { homedir as homedir3 } from "os";
42101
- import { join as join3 } from "path";
42102
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
42103
- var CONFIG_DIR2 = join3(homedir3(), ".magic-shell");
42104
- var CONFIG_FILE2 = join3(CONFIG_DIR2, "config.json");
42105
- var HISTORY_FILE2 = join3(CONFIG_DIR2, "history.json");
42106
- var KEYCHAIN_OPENROUTER2 = "openrouter-api-key";
42107
- var KEYCHAIN_OPENCODE_ZEN2 = "opencode-zen-api-key";
42108
- var DEFAULT_CONFIG2 = {
42109
- provider: "opencode-zen",
42110
- openrouterApiKey: "",
42111
- opencodeZenApiKey: "",
42112
- defaultModel: "kimi-k2.6-free",
42113
- safetyLevel: "moderate",
42114
- dryRunByDefault: false,
42115
- blockedCommands: [
42116
- ":(){ :|:& };:",
42117
- "> /dev/sda",
42118
- "mkfs",
42119
- "dd if=/dev/zero",
42120
- "chmod -R 777 /",
42121
- "chown -R"
42122
- ],
42123
- confirmedDangerousPatterns: [],
42124
- repoContext: false,
42125
- customModels: []
42126
- };
42127
- function ensureConfigDir2() {
42128
- if (!existsSync4(CONFIG_DIR2)) {
42129
- mkdirSync2(CONFIG_DIR2, { recursive: true });
42130
- }
42131
- }
42132
- function loadConfig2() {
42133
- ensureConfigDir2();
42134
- if (!existsSync4(CONFIG_FILE2)) {
42135
- return { ...DEFAULT_CONFIG2 };
42136
- }
42137
- try {
42138
- const data = readFileSync3(CONFIG_FILE2, "utf-8");
42139
- const loaded = JSON.parse(data);
42140
- return { ...DEFAULT_CONFIG2, ...loaded };
42141
- } catch {
42142
- return { ...DEFAULT_CONFIG2 };
42143
- }
42144
- }
42145
- function saveConfig2(config2) {
42146
- ensureConfigDir2();
42147
- const configToSave = { ...config2 };
42148
- if (isSecureStorageAvailable()) {
42149
- configToSave.openrouterApiKey = "";
42150
- configToSave.opencodeZenApiKey = "";
42151
- }
42152
- writeFileSync2(CONFIG_FILE2, JSON.stringify(configToSave, null, 2));
42153
- }
42154
- async function getApiKey2(provider) {
42155
- if (provider === "openrouter") {
42156
- const envKey = process.env.OPENROUTER_API_KEY;
42157
- if (envKey)
42158
- return envKey;
42159
- } else if (provider === "opencode-zen") {
42160
- const envKey = process.env.OPENCODE_ZEN_API_KEY;
42161
- if (envKey)
42162
- return envKey;
42163
- }
42164
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER2 : KEYCHAIN_OPENCODE_ZEN2;
42165
- const secureKey = await getSecret(keychainKey);
42166
- if (secureKey)
42167
- return secureKey;
42168
- const config2 = loadConfig2();
42169
- return provider === "openrouter" ? config2.openrouterApiKey : config2.opencodeZenApiKey;
42170
- }
42171
- async function setApiKey2(provider, key) {
42172
- const config2 = loadConfig2();
42173
- config2.provider = provider;
42174
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER2 : KEYCHAIN_OPENCODE_ZEN2;
42175
- const stored = await setSecret(keychainKey, key);
42176
- if (!stored) {
42177
- if (provider === "openrouter") {
42178
- config2.openrouterApiKey = key;
42179
- } else {
42180
- config2.opencodeZenApiKey = key;
42181
- }
42182
- }
42183
- saveConfig2(config2);
42184
- }
42185
- function loadHistory2() {
42186
- ensureConfigDir2();
42187
- if (!existsSync4(HISTORY_FILE2)) {
42188
- return [];
42189
- }
42190
- try {
42191
- const data = readFileSync3(HISTORY_FILE2, "utf-8");
42192
- return JSON.parse(data);
42193
- } catch {
42194
- return [];
42195
- }
42196
- }
42197
- function saveHistory(history) {
42198
- ensureConfigDir2();
42199
- const trimmed = history.slice(-100);
42200
- writeFileSync2(HISTORY_FILE2, JSON.stringify(trimmed, null, 2));
42201
- }
42202
- function addToHistory(entry) {
42203
- const history = loadHistory2();
42204
- history.push(entry);
42205
- saveHistory(history);
42206
- }
42207
- function getCustomModels2() {
42208
- const config2 = loadConfig2();
42209
- return config2.customModels || [];
42210
- }
42211
- async function getCustomModel2(id) {
42212
- const customModels = getCustomModels2();
42213
- const model = customModels.find((m) => m.id === id);
42214
- if (!model) {
42215
- return;
42216
- }
42217
- const keychainKey = `customModel:${model.id}:apiKey`;
42218
- try {
42219
- const apiKey = await getSecret(keychainKey);
42220
- return apiKey ? { ...model, apiKey } : model;
42221
- } catch (error40) {
42222
- if (process.env.DEBUG_API === "1") {
42223
- const message = error40 instanceof Error ? error40.message : String(error40);
42224
- console.error(`[DEBUG] Custom model keychain get error: ${message}`);
42225
- }
42226
- return model;
42227
- }
42228
- }
42229
-
42230
42662
  // src/lib/theme.ts
42231
42663
  var opencode = {
42232
42664
  name: "opencode",
@@ -42595,7 +43027,7 @@ ${colors.bold}USAGE${colors.reset}
42595
43027
  msh --add-model Add custom model (LM Studio, Ollama, etc.)
42596
43028
  msh --list-custom List custom models
42597
43029
  msh --remove-model <id> Remove custom model
42598
- msh --provider <name> Set provider (opencode-zen or openrouter)
43030
+ msh --provider <name> Set provider
42599
43031
  msh --themes List available themes
42600
43032
  msh --theme <name> Set color theme
42601
43033
  msh --repo-context Enable project context detection
@@ -42630,6 +43062,10 @@ ${colors.bold}THEMES${colors.reset}
42630
43062
  ${colors.bold}ENVIRONMENT${colors.reset}
42631
43063
  OPENCODE_ZEN_API_KEY API key for OpenCode Zen
42632
43064
  OPENROUTER_API_KEY API key for OpenRouter
43065
+ AI_GATEWAY_API_KEY API key for Vercel AI Gateway
43066
+ CLOUDFLARE_API_TOKEN API token for Cloudflare Workers AI
43067
+ CLOUDFLARE_ACCOUNT_ID Account ID for Cloudflare providers
43068
+ CLOUDFLARE_AI_GATEWAY_API_KEY API key/token for Cloudflare AI Gateway
42633
43069
 
42634
43070
  ${colors.bold}CONFIG${colors.reset}
42635
43071
  ~/.magic-shell/config.json
@@ -42675,6 +43111,24 @@ ${colors.bold}OpenRouter Models${colors.reset}
42675
43111
  console.log(` ${colors.dim}${model.description}${colors.reset}`);
42676
43112
  }
42677
43113
  }
43114
+ const providerSections = [
43115
+ ["Vercel AI Gateway Models", VERCEL_AI_GATEWAY_MODELS, "vercel-ai-gateway"],
43116
+ ["Cloudflare AI Gateway Models", CLOUDFLARE_AI_GATEWAY_MODELS, "cloudflare-ai-gateway"],
43117
+ ["Cloudflare Workers AI Models", WORKERS_AI_MODELS, "workers-ai"]
43118
+ ];
43119
+ for (const [title, models, provider] of providerSections) {
43120
+ console.log(`
43121
+ ${colors.bold}${title}${colors.reset}
43122
+ `);
43123
+ const sortedModels = [...models].sort((a, b) => a.name.localeCompare(b.name));
43124
+ for (const model of sortedModels) {
43125
+ const isCurrent = config2.provider === provider && config2.defaultModel === model.id;
43126
+ const marker21 = isCurrent ? colors.success + "\u2192 " : " ";
43127
+ const category = colors.dim + `[${model.category}]` + colors.reset;
43128
+ console.log(`${marker21}${model.id} ${category}`);
43129
+ console.log(` ${colors.dim}${model.description}${colors.reset}`);
43130
+ }
43131
+ }
42678
43132
  if (customModels.length > 0) {
42679
43133
  console.log(`
42680
43134
  ${colors.bold}Custom Models${colors.reset} ${colors.info}(custom)${colors.reset}
@@ -42698,8 +43152,8 @@ function validateApiKey(key, provider) {
42698
43152
  if (trimmed.length < 20) {
42699
43153
  return "API key seems too short (expected at least 20 characters)";
42700
43154
  }
42701
- if (!trimmed.startsWith("sk-")) {
42702
- const providerName = provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
43155
+ if ((provider === "opencode-zen" || provider === "openrouter") && !trimmed.startsWith("sk-")) {
43156
+ const providerName = getProviderDisplayName(provider);
42703
43157
  return `${providerName} API keys typically start with 'sk-'`;
42704
43158
  }
42705
43159
  if (trimmed.includes(" ")) {
@@ -42711,6 +43165,22 @@ function validateApiKey(key, provider) {
42711
43165
  }
42712
43166
  return null;
42713
43167
  }
43168
+ function getApiKeyUrl(provider) {
43169
+ switch (provider) {
43170
+ case "opencode-zen":
43171
+ return "https://opencode.ai/auth";
43172
+ case "openrouter":
43173
+ return "https://openrouter.ai/keys";
43174
+ case "vercel-ai-gateway":
43175
+ return "https://vercel.com/docs/ai-gateway";
43176
+ case "cloudflare-ai-gateway":
43177
+ return "https://developers.cloudflare.com/ai-gateway/";
43178
+ case "workers-ai":
43179
+ return "https://dash.cloudflare.com/profile/api-tokens";
43180
+ case "custom":
43181
+ return "";
43182
+ }
43183
+ }
42714
43184
  async function setup() {
42715
43185
  const readline = await import("readline");
42716
43186
  const rl = readline.createInterface({
@@ -42728,9 +43198,18 @@ ${colors.bold}${colors.cyan}Magic Shell Setup${colors.reset}
42728
43198
  console.log("Select provider:");
42729
43199
  console.log(" 1. OpenCode Zen (recommended, has free models)");
42730
43200
  console.log(" 2. OpenRouter");
43201
+ console.log(" 3. Vercel AI Gateway");
43202
+ console.log(" 4. Cloudflare AI Gateway");
43203
+ console.log(" 5. Cloudflare Workers AI");
42731
43204
  const providerChoice = await question(`
42732
43205
  Choice [1]: `);
42733
- const provider = providerChoice === "2" ? "openrouter" : "opencode-zen";
43206
+ const providerChoices = {
43207
+ "2": "openrouter",
43208
+ "3": "vercel-ai-gateway",
43209
+ "4": "cloudflare-ai-gateway",
43210
+ "5": "workers-ai"
43211
+ };
43212
+ const provider = providerChoices[providerChoice] || "opencode-zen";
42734
43213
  const existingKey = await getApiKey(provider);
42735
43214
  if (existingKey) {
42736
43215
  const useExisting = await question(`
@@ -42738,7 +43217,7 @@ API key already configured. Keep it? [Y/n]: `);
42738
43217
  if (useExisting.toLowerCase() !== "n") {
42739
43218
  console.log(`${colors.green}\u2713 Using existing API key${colors.reset}`);
42740
43219
  } else {
42741
- const url2 = provider === "opencode-zen" ? "https://opencode.ai/auth" : "https://openrouter.ai/keys";
43220
+ const url2 = getApiKeyUrl(provider);
42742
43221
  console.log(`
42743
43222
  Get your API key from: ${colors.cyan}${url2}${colors.reset}`);
42744
43223
  let validKey = false;
@@ -42762,7 +43241,7 @@ Get your API key from: ${colors.cyan}${url2}${colors.reset}`);
42762
43241
  }
42763
43242
  }
42764
43243
  } else {
42765
- const url2 = provider === "opencode-zen" ? "https://opencode.ai/auth" : "https://openrouter.ai/keys";
43244
+ const url2 = getApiKeyUrl(provider);
42766
43245
  console.log(`
42767
43246
  Get your API key from: ${colors.cyan}${url2}${colors.reset}`);
42768
43247
  let validKey = false;
@@ -42786,7 +43265,20 @@ Get your API key from: ${colors.cyan}${url2}${colors.reset}`);
42786
43265
  validKey = true;
42787
43266
  }
42788
43267
  }
42789
- const models = provider === "opencode-zen" ? OPENCODE_ZEN_MODELS : OPENROUTER_MODELS;
43268
+ const config2 = loadConfig();
43269
+ if (provider === "cloudflare-ai-gateway" || provider === "workers-ai") {
43270
+ const existingAccountId = config2.cloudflareAccountId || process.env.CLOUDFLARE_ACCOUNT_ID || "";
43271
+ if (!existingAccountId) {
43272
+ const accountId = await question(`
43273
+ Cloudflare account ID: `);
43274
+ config2.cloudflareAccountId = accountId.trim();
43275
+ }
43276
+ }
43277
+ if (provider === "cloudflare-ai-gateway") {
43278
+ const gatewayId = await question(`Cloudflare AI Gateway ID [${config2.cloudflareAiGatewayId || "default"}]: `);
43279
+ config2.cloudflareAiGatewayId = gatewayId.trim() || config2.cloudflareAiGatewayId || "default";
43280
+ }
43281
+ const models = getProviderModels(provider);
42790
43282
  const freeModels = models.filter((m) => m.free);
42791
43283
  console.log(`
42792
43284
  Recommended models:`);
@@ -42799,13 +43291,12 @@ Recommended models:`);
42799
43291
  Choice [1]: `);
42800
43292
  const modelIndex = parseInt(modelChoice || "1") - 1;
42801
43293
  const selectedModel = displayModels[modelIndex] || displayModels[0];
42802
- const config2 = loadConfig();
42803
43294
  config2.provider = provider;
42804
43295
  config2.defaultModel = selectedModel.id;
42805
43296
  saveConfig(config2);
42806
43297
  console.log(`
42807
43298
  ${colors.green}\u2713 Setup complete!${colors.reset}`);
42808
- console.log(` Provider: ${provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter"}`);
43299
+ console.log(` Provider: ${getProviderDisplayName(provider)}`);
42809
43300
  console.log(` Model: ${selectedModel.name}`);
42810
43301
  console.log(`
42811
43302
  Try: ${colors.cyan}msh "list all files"${colors.reset}
@@ -42949,7 +43440,8 @@ async function translate(query, options) {
42949
43440
  const apiKey = await getApiKey(config2.provider);
42950
43441
  const customModel = await getCustomModel(config2.defaultModel);
42951
43442
  const builtInModel = ALL_MODELS.find((m) => m.id === config2.defaultModel);
42952
- const model = customModel || builtInModel || (config2.provider === "opencode-zen" ? OPENCODE_ZEN_MODELS[0] : OPENROUTER_MODELS[0]);
43443
+ const fallbackModels = getProviderModels(config2.provider);
43444
+ const model = customModel || builtInModel || fallbackModels[0] || OPENCODE_ZEN_MODELS[0];
42953
43445
  if (!customModel && !apiKey) {
42954
43446
  console.error(`${colors.red}Error: No API key configured.${colors.reset}`);
42955
43447
  console.error(`Run: ${colors.cyan}msh --setup${colors.reset}`);
@@ -43128,14 +43620,15 @@ ${colors.bold}Custom Models${colors.reset}
43128
43620
  }
43129
43621
  if (args[0] === "--provider" && args[1]) {
43130
43622
  const provider = args[1];
43131
- if (provider !== "opencode-zen" && provider !== "openrouter") {
43623
+ const validProviders = ["opencode-zen", "openrouter", "vercel-ai-gateway", "cloudflare-ai-gateway", "workers-ai"];
43624
+ if (!validProviders.includes(provider)) {
43132
43625
  console.error(`${colors.error}Unknown provider: ${provider}${colors.reset}`);
43133
- console.error(`Valid providers: opencode-zen, openrouter`);
43626
+ console.error(`Valid providers: ${validProviders.join(", ")}`);
43134
43627
  process.exit(1);
43135
43628
  }
43136
43629
  const config2 = loadConfig();
43137
43630
  config2.provider = provider;
43138
- const models = provider === "opencode-zen" ? OPENCODE_ZEN_MODELS : OPENROUTER_MODELS;
43631
+ const models = getProviderModels(provider);
43139
43632
  const firstAvailable = models.find((m) => !m.disabled) || models[0];
43140
43633
  config2.defaultModel = firstAvailable.id;
43141
43634
  saveConfig(config2);