@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/README.md +4 -6
- package/dist/cli.js +440 -158
- package/dist/index.js +900 -407
- package/dist/tui.js +440 -158
- package/package.json +1 -1
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
|
|
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: "
|
|
2204
|
-
name: "
|
|
2205
|
-
description: "
|
|
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: "
|
|
2208
|
-
contextLength:
|
|
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.
|
|
2272
|
-
name: "GPT 5.
|
|
2273
|
-
description: "OpenAI's fast
|
|
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:
|
|
2399
|
+
contextLength: 400000
|
|
2277
2400
|
},
|
|
2278
2401
|
{
|
|
2279
|
-
id: "
|
|
2280
|
-
name: "
|
|
2281
|
-
description: "
|
|
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:
|
|
2407
|
+
contextLength: 400000
|
|
2285
2408
|
},
|
|
2286
2409
|
{
|
|
2287
|
-
id: "claude-sonnet-4",
|
|
2288
|
-
name: "Claude Sonnet 4",
|
|
2289
|
-
description: "Anthropic's
|
|
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:
|
|
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.
|
|
2304
|
-
name: "GPT 5.
|
|
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:
|
|
2431
|
+
contextLength: 1050000
|
|
2309
2432
|
},
|
|
2310
2433
|
{
|
|
2311
|
-
id: "gpt-5.
|
|
2312
|
-
name: "GPT 5.
|
|
2313
|
-
description: "OpenAI's
|
|
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:
|
|
2439
|
+
contextLength: 1050000
|
|
2317
2440
|
},
|
|
2318
2441
|
{
|
|
2319
|
-
id: "gpt-5.
|
|
2320
|
-
name: "GPT 5.
|
|
2321
|
-
description: "OpenAI's
|
|
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:
|
|
2447
|
+
contextLength: 400000
|
|
2325
2448
|
},
|
|
2326
2449
|
{
|
|
2327
|
-
id: "gpt-5.
|
|
2328
|
-
name: "GPT 5.
|
|
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:
|
|
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-
|
|
2392
|
-
name: "Claude
|
|
2393
|
-
description: "Anthropic's
|
|
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:
|
|
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 = [
|
|
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
|
|
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
|
-
|
|
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
|
|
3022
|
+
const keychainKey = getKeychainKey(provider);
|
|
2874
3023
|
const stored = await setSecret(keychainKey, key);
|
|
2875
3024
|
if (!stored) {
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
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
|
|
41494
|
+
var VERCEL_AI_GATEWAY_MODELS2 = [
|
|
41269
41495
|
{
|
|
41270
|
-
id: "
|
|
41271
|
-
name: "
|
|
41272
|
-
description: "
|
|
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: "
|
|
41275
|
-
contextLength:
|
|
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.
|
|
41339
|
-
name: "GPT 5.
|
|
41340
|
-
description: "OpenAI's fast
|
|
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:
|
|
41645
|
+
contextLength: 400000
|
|
41344
41646
|
},
|
|
41345
41647
|
{
|
|
41346
|
-
id: "
|
|
41347
|
-
name: "
|
|
41348
|
-
description: "
|
|
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:
|
|
41653
|
+
contextLength: 400000
|
|
41352
41654
|
},
|
|
41353
41655
|
{
|
|
41354
|
-
id: "claude-sonnet-4",
|
|
41355
|
-
name: "Claude Sonnet 4",
|
|
41356
|
-
description: "Anthropic's
|
|
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:
|
|
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.
|
|
41371
|
-
name: "GPT 5.
|
|
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:
|
|
41677
|
+
contextLength: 1050000
|
|
41376
41678
|
},
|
|
41377
41679
|
{
|
|
41378
|
-
id: "gpt-5.
|
|
41379
|
-
name: "GPT 5.
|
|
41380
|
-
description: "OpenAI's
|
|
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:
|
|
41685
|
+
contextLength: 1050000
|
|
41384
41686
|
},
|
|
41385
41687
|
{
|
|
41386
|
-
id: "gpt-5.
|
|
41387
|
-
name: "GPT 5.
|
|
41388
|
-
description: "OpenAI's
|
|
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:
|
|
41693
|
+
contextLength: 400000
|
|
41392
41694
|
},
|
|
41393
41695
|
{
|
|
41394
|
-
id: "gpt-5.
|
|
41395
|
-
name: "GPT 5.
|
|
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:
|
|
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-
|
|
41459
|
-
name: "Claude
|
|
41460
|
-
description: "Anthropic's
|
|
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:
|
|
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 = [
|
|
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
|
|
41514
|
-
import { homedir as
|
|
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 =
|
|
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 (
|
|
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
|
|
41712
|
-
import { join as
|
|
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 (
|
|
42207
|
+
if (existsSync4(join3(cwd, ".git"))) {
|
|
41719
42208
|
context2.hasGit = true;
|
|
41720
42209
|
detected = true;
|
|
41721
42210
|
}
|
|
41722
|
-
if (
|
|
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 =
|
|
41727
|
-
if (
|
|
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(
|
|
41732
|
-
if (
|
|
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 (
|
|
42223
|
+
} else if (existsSync4(join3(cwd, "pnpm-lock.yaml"))) {
|
|
41735
42224
|
context2.packageManager = "pnpm";
|
|
41736
|
-
} else if (
|
|
42225
|
+
} else if (existsSync4(join3(cwd, "yarn.lock"))) {
|
|
41737
42226
|
context2.packageManager = "yarn";
|
|
41738
|
-
} else if (
|
|
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 =
|
|
41750
|
-
if (
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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);
|