@austinthesing/magic-shell 0.2.18 → 0.2.20

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.
Files changed (5) hide show
  1. package/README.md +14 -9
  2. package/dist/cli.js +640 -183
  3. package/dist/index.js +1119 -424
  4. package/dist/tui.js +640 -183
  5. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -36767,15 +36767,6 @@ import { cwd as getCwd } from "process";
36767
36767
 
36768
36768
  // src/lib/types.ts
36769
36769
  var OPENROUTER_MODELS = [
36770
- {
36771
- id: "minimax/minimax-m2.5:free",
36772
- name: "MiniMax M2.5 Free",
36773
- description: "Free MiniMax model for trying out open-source generation",
36774
- category: "smart",
36775
- provider: "openrouter",
36776
- contextLength: 196608,
36777
- free: true
36778
- },
36779
36770
  {
36780
36771
  id: "xiaomi/mimo-v2.5",
36781
36772
  name: "MiMo V2.5",
@@ -36808,6 +36799,38 @@ var OPENROUTER_MODELS = [
36808
36799
  provider: "openrouter",
36809
36800
  contextLength: 202752
36810
36801
  },
36802
+ {
36803
+ id: "openai/gpt-latest",
36804
+ name: "OpenAI GPT Latest",
36805
+ description: "OpenRouter alias that redirects to the latest OpenAI GPT model",
36806
+ category: "smart",
36807
+ provider: "openrouter",
36808
+ contextLength: 1050000
36809
+ },
36810
+ {
36811
+ id: "openai/gpt-5.5",
36812
+ name: "GPT 5.5",
36813
+ description: "OpenAI's latest flagship GPT model",
36814
+ category: "smart",
36815
+ provider: "openrouter",
36816
+ contextLength: 1050000
36817
+ },
36818
+ {
36819
+ id: "anthropic/claude-sonnet-latest",
36820
+ name: "Claude Sonnet Latest",
36821
+ description: "OpenRouter alias that redirects to the latest Claude Sonnet model",
36822
+ category: "smart",
36823
+ provider: "openrouter",
36824
+ contextLength: 1e6
36825
+ },
36826
+ {
36827
+ id: "anthropic/claude-sonnet-4.6",
36828
+ name: "Claude Sonnet 4.6",
36829
+ description: "Anthropic's latest Sonnet model",
36830
+ category: "smart",
36831
+ provider: "openrouter",
36832
+ contextLength: 1e6
36833
+ },
36811
36834
  {
36812
36835
  id: "moonshotai/kimi-k2.6",
36813
36836
  name: "Kimi K2.6",
@@ -36840,6 +36863,30 @@ var OPENROUTER_MODELS = [
36840
36863
  provider: "openrouter",
36841
36864
  contextLength: 196608
36842
36865
  },
36866
+ {
36867
+ id: "anthropic/claude-opus-latest",
36868
+ name: "Claude Opus Latest",
36869
+ description: "OpenRouter alias that redirects to the latest Claude Opus model",
36870
+ category: "reasoning",
36871
+ provider: "openrouter",
36872
+ contextLength: 1e6
36873
+ },
36874
+ {
36875
+ id: "anthropic/claude-opus-4.7",
36876
+ name: "Claude Opus 4.7",
36877
+ description: "Anthropic's latest Opus model",
36878
+ category: "reasoning",
36879
+ provider: "openrouter",
36880
+ contextLength: 1e6
36881
+ },
36882
+ {
36883
+ id: "openai/gpt-5.5-pro",
36884
+ name: "GPT 5.5 Pro",
36885
+ description: "OpenAI's latest high-capability reasoning model",
36886
+ category: "reasoning",
36887
+ provider: "openrouter",
36888
+ contextLength: 1050000
36889
+ },
36843
36890
  {
36844
36891
  id: "moonshotai/kimi-k2-thinking",
36845
36892
  name: "Kimi K2 Thinking",
@@ -36849,58 +36896,179 @@ var OPENROUTER_MODELS = [
36849
36896
  contextLength: 262144
36850
36897
  }
36851
36898
  ];
36899
+ var VERCEL_AI_GATEWAY_MODELS = [
36900
+ {
36901
+ id: "openai/gpt-latest",
36902
+ name: "OpenAI GPT Latest",
36903
+ description: "Vercel AI Gateway alias that redirects to the latest OpenAI GPT model",
36904
+ category: "smart",
36905
+ provider: "vercel-ai-gateway",
36906
+ contextLength: 1050000
36907
+ },
36908
+ {
36909
+ id: "openai/gpt-5.5",
36910
+ name: "GPT 5.5",
36911
+ description: "OpenAI's latest flagship GPT model",
36912
+ category: "smart",
36913
+ provider: "vercel-ai-gateway",
36914
+ contextLength: 1050000
36915
+ },
36916
+ {
36917
+ id: "anthropic/claude-sonnet-4.6",
36918
+ name: "Claude Sonnet 4.6",
36919
+ description: "Anthropic's latest Sonnet model",
36920
+ category: "smart",
36921
+ provider: "vercel-ai-gateway",
36922
+ contextLength: 1e6
36923
+ },
36924
+ {
36925
+ id: "anthropic/claude-opus-4.7",
36926
+ name: "Claude Opus 4.7",
36927
+ description: "Anthropic's latest Opus model",
36928
+ category: "reasoning",
36929
+ provider: "vercel-ai-gateway",
36930
+ contextLength: 1e6
36931
+ },
36932
+ {
36933
+ id: "openai/gpt-5.5-pro",
36934
+ name: "GPT 5.5 Pro",
36935
+ description: "OpenAI's latest high-capability reasoning model",
36936
+ category: "reasoning",
36937
+ provider: "vercel-ai-gateway",
36938
+ contextLength: 1050000
36939
+ }
36940
+ ];
36941
+ var CLOUDFLARE_AI_GATEWAY_MODELS = [
36942
+ {
36943
+ id: "openai/gpt-5.5",
36944
+ name: "GPT 5.5",
36945
+ description: "OpenAI's latest flagship GPT model through Cloudflare AI Gateway",
36946
+ category: "smart",
36947
+ provider: "cloudflare-ai-gateway",
36948
+ contextLength: 1050000
36949
+ },
36950
+ {
36951
+ id: "anthropic/claude-sonnet-4-6",
36952
+ name: "Claude Sonnet 4.6",
36953
+ description: "Anthropic's latest Sonnet model through Cloudflare AI Gateway",
36954
+ category: "smart",
36955
+ provider: "cloudflare-ai-gateway",
36956
+ contextLength: 1e6
36957
+ },
36958
+ {
36959
+ id: "anthropic/claude-opus-4-7",
36960
+ name: "Claude Opus 4.7",
36961
+ description: "Anthropic's latest Opus model through Cloudflare AI Gateway",
36962
+ category: "reasoning",
36963
+ provider: "cloudflare-ai-gateway",
36964
+ contextLength: 1e6
36965
+ },
36966
+ {
36967
+ id: "workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast",
36968
+ name: "Workers AI Llama 3.3 70B Fast",
36969
+ description: "Cloudflare Workers AI fast Llama model routed through AI Gateway",
36970
+ category: "smart",
36971
+ provider: "cloudflare-ai-gateway",
36972
+ contextLength: 24000
36973
+ }
36974
+ ];
36975
+ var WORKERS_AI_MODELS = [
36976
+ {
36977
+ id: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
36978
+ name: "Llama 3.3 70B Fast",
36979
+ description: "Cloudflare Workers AI fast Llama instruct model",
36980
+ category: "smart",
36981
+ provider: "workers-ai",
36982
+ contextLength: 24000
36983
+ },
36984
+ {
36985
+ id: "@cf/meta/llama-3.1-8b-instruct",
36986
+ name: "Llama 3.1 8B Instruct",
36987
+ description: "Cloudflare Workers AI lightweight Llama instruct model",
36988
+ category: "fast",
36989
+ provider: "workers-ai",
36990
+ contextLength: 8000
36991
+ },
36992
+ {
36993
+ id: "@cf/openai/gpt-oss-120b",
36994
+ name: "GPT OSS 120B",
36995
+ description: "OpenAI open-weight model hosted by Cloudflare Workers AI",
36996
+ category: "reasoning",
36997
+ provider: "workers-ai",
36998
+ contextLength: 32000
36999
+ }
37000
+ ];
36852
37001
  var OPENCODE_ZEN_MODELS = [
36853
37002
  {
36854
37003
  id: "minimax-m2.5-free",
36855
37004
  name: "MiniMax M2.5 Free",
36856
37005
  description: "MiniMax's free model (limited time)",
36857
- category: "fast",
37006
+ category: "smart",
36858
37007
  provider: "opencode-zen",
36859
37008
  contextLength: 196608,
36860
37009
  free: true
36861
37010
  },
36862
37011
  {
36863
- id: "kimi-k2.6-free",
36864
- name: "Kimi K2.6 Free",
36865
- description: "Moonshot's latest model (free, limited time)",
37012
+ id: "ling-2.6-flash-free",
37013
+ name: "Ling 2.6 Flash Free",
37014
+ description: "Ling's free flash model (limited time)",
37015
+ category: "fast",
37016
+ provider: "opencode-zen",
37017
+ contextLength: 131072,
37018
+ free: true
37019
+ },
37020
+ {
37021
+ id: "hy3-preview-free",
37022
+ name: "Hy3 Preview Free",
37023
+ description: "Hy3 preview model (free, limited time)",
36866
37024
  category: "smart",
36867
37025
  provider: "opencode-zen",
36868
- contextLength: 262144,
37026
+ contextLength: 131072,
36869
37027
  free: true
36870
37028
  },
36871
37029
  {
36872
- id: "deepseek-v4-flash-free",
36873
- name: "DeepSeek V4 Flash Free",
36874
- description: "DeepSeek's latest fast model (free, limited time)",
36875
- category: "fast",
37030
+ id: "nemotron-3-super-free",
37031
+ name: "Nemotron 3 Super Free",
37032
+ description: "NVIDIA Nemotron free trial model",
37033
+ category: "smart",
36876
37034
  provider: "opencode-zen",
36877
- contextLength: 1048576,
37035
+ contextLength: 131072,
36878
37036
  free: true
36879
37037
  },
36880
37038
  {
36881
- id: "mimo-v2.5",
36882
- name: "MiMo V2.5",
36883
- description: "Xiaomi's latest long-context MiMo model",
36884
- category: "fast",
37039
+ id: "trinity-large-preview-free",
37040
+ name: "Trinity Large Preview Free",
37041
+ description: "Trinity large preview model (free, limited time)",
37042
+ category: "smart",
36885
37043
  provider: "opencode-zen",
36886
- contextLength: 1048576
37044
+ contextLength: 131072,
37045
+ free: true
37046
+ },
37047
+ {
37048
+ id: "big-pickle",
37049
+ name: "Big Pickle",
37050
+ description: "OpenCode stealth model (free, limited time)",
37051
+ category: "smart",
37052
+ provider: "opencode-zen",
37053
+ contextLength: 131072,
37054
+ free: true
36887
37055
  },
36888
37056
  {
36889
37057
  id: "gpt-5-nano",
36890
37058
  name: "GPT 5 Nano",
36891
- description: "OpenAI's fastest GPT model (free)",
37059
+ description: "OpenAI's free lightweight GPT model",
36892
37060
  category: "fast",
36893
37061
  provider: "opencode-zen",
36894
- contextLength: 200000,
37062
+ contextLength: 400000,
36895
37063
  free: true
36896
37064
  },
36897
37065
  {
36898
- id: "claude-3-5-haiku",
36899
- name: "Claude Haiku 3.5",
36900
- description: "Anthropic's fast and efficient model",
37066
+ id: "mimo-v2.5",
37067
+ name: "MiMo V2.5",
37068
+ description: "Xiaomi's latest long-context MiMo model",
36901
37069
  category: "fast",
36902
37070
  provider: "opencode-zen",
36903
- contextLength: 200000
37071
+ contextLength: 1048576
36904
37072
  },
36905
37073
  {
36906
37074
  id: "claude-haiku-4-5",
@@ -36919,84 +37087,68 @@ var OPENCODE_ZEN_MODELS = [
36919
37087
  contextLength: 200000
36920
37088
  },
36921
37089
  {
36922
- id: "gpt-5.1-codex-mini",
36923
- name: "GPT 5.1 Codex Mini",
36924
- description: "OpenAI's fast codex model",
37090
+ id: "gpt-5.4-mini",
37091
+ name: "GPT 5.4 Mini",
37092
+ description: "OpenAI's latest fast GPT mini model",
36925
37093
  category: "fast",
36926
37094
  provider: "opencode-zen",
36927
- contextLength: 200000
37095
+ contextLength: 400000
36928
37096
  },
36929
37097
  {
36930
- id: "deepseek-v4-flash",
36931
- name: "DeepSeek V4 Flash",
36932
- description: "DeepSeek's latest fast open-source model",
37098
+ id: "gpt-5.4-nano",
37099
+ name: "GPT 5.4 Nano",
37100
+ description: "OpenAI's latest lightweight GPT model",
36933
37101
  category: "fast",
36934
37102
  provider: "opencode-zen",
36935
- contextLength: 1048576
37103
+ contextLength: 400000
36936
37104
  },
36937
37105
  {
36938
- id: "claude-sonnet-4",
36939
- name: "Claude Sonnet 4",
36940
- description: "Anthropic's balanced model for complex tasks",
37106
+ id: "claude-sonnet-4-6",
37107
+ name: "Claude Sonnet 4.6",
37108
+ description: "Anthropic's latest Sonnet model",
36941
37109
  category: "smart",
36942
37110
  provider: "opencode-zen",
36943
- contextLength: 200000
37111
+ contextLength: 1e6
36944
37112
  },
36945
37113
  {
36946
- id: "gemini-3-pro",
36947
- name: "Gemini 3 Pro",
37114
+ id: "gemini-3.1-pro",
37115
+ name: "Gemini 3.1 Pro",
36948
37116
  description: "Google's high-end Gemini model",
36949
37117
  category: "smart",
36950
37118
  provider: "opencode-zen",
36951
37119
  contextLength: 200000
36952
37120
  },
36953
37121
  {
36954
- id: "gpt-5.2",
36955
- name: "GPT 5.2",
36956
- description: "OpenAI's flagship GPT model",
36957
- category: "smart",
36958
- provider: "opencode-zen",
36959
- contextLength: 200000
36960
- },
36961
- {
36962
- id: "gpt-5.2-codex",
36963
- name: "GPT 5.2 Codex",
36964
- description: "OpenAI's coding-focused GPT model",
37122
+ id: "gpt-5.5",
37123
+ name: "GPT 5.5",
37124
+ description: "OpenAI's latest flagship GPT model",
36965
37125
  category: "smart",
36966
37126
  provider: "opencode-zen",
36967
- contextLength: 200000
37127
+ contextLength: 1050000
36968
37128
  },
36969
37129
  {
36970
- id: "gpt-5.1",
36971
- name: "GPT 5.1",
36972
- description: "OpenAI's balanced GPT model",
37130
+ id: "gpt-5.5-pro",
37131
+ name: "GPT 5.5 Pro",
37132
+ description: "OpenAI's latest high-capability reasoning model",
36973
37133
  category: "smart",
36974
37134
  provider: "opencode-zen",
36975
- contextLength: 200000
36976
- },
36977
- {
36978
- id: "gpt-5.1-codex",
36979
- name: "GPT 5.1 Codex",
36980
- description: "OpenAI's coding model",
36981
- category: "smart",
36982
- provider: "opencode-zen",
36983
- contextLength: 200000
37135
+ contextLength: 1050000
36984
37136
  },
36985
37137
  {
36986
- id: "gpt-5",
36987
- name: "GPT 5",
36988
- description: "OpenAI's prior generation GPT model",
37138
+ id: "gpt-5.3-codex",
37139
+ name: "GPT 5.3 Codex",
37140
+ description: "OpenAI's latest coding-focused GPT model",
36989
37141
  category: "smart",
36990
37142
  provider: "opencode-zen",
36991
- contextLength: 200000
37143
+ contextLength: 400000
36992
37144
  },
36993
37145
  {
36994
- id: "gpt-5-codex",
36995
- name: "GPT 5 Codex",
36996
- description: "OpenAI's prior generation codex model",
37146
+ id: "gpt-5.3-codex-spark",
37147
+ name: "GPT 5.3 Codex Spark",
37148
+ description: "OpenAI's latest fast coding-focused GPT model",
36997
37149
  category: "smart",
36998
37150
  provider: "opencode-zen",
36999
- contextLength: 200000
37151
+ contextLength: 400000
37000
37152
  },
37001
37153
  {
37002
37154
  id: "minimax-m2.7",
@@ -37039,44 +37191,12 @@ var OPENCODE_ZEN_MODELS = [
37039
37191
  contextLength: 202752
37040
37192
  },
37041
37193
  {
37042
- id: "claude-sonnet-4-5",
37043
- name: "Claude Sonnet 4.5",
37044
- description: "Anthropic's hybrid reasoning model",
37045
- category: "reasoning",
37046
- provider: "opencode-zen",
37047
- contextLength: 200000
37048
- },
37049
- {
37050
- id: "claude-opus-4-6",
37051
- name: "Claude Opus 4.6",
37052
- description: "Anthropic's newest Opus model",
37053
- category: "reasoning",
37054
- provider: "opencode-zen",
37055
- contextLength: 200000
37056
- },
37057
- {
37058
- id: "claude-opus-4-5",
37059
- name: "Claude Opus 4.5",
37060
- description: "Anthropic's most capable model",
37061
- category: "reasoning",
37062
- provider: "opencode-zen",
37063
- contextLength: 200000
37064
- },
37065
- {
37066
- id: "claude-opus-4-1",
37067
- name: "Claude Opus 4.1",
37068
- description: "Anthropic's powerful reasoning model",
37069
- category: "reasoning",
37070
- provider: "opencode-zen",
37071
- contextLength: 200000
37072
- },
37073
- {
37074
- id: "gpt-5.1-codex-max",
37075
- name: "GPT 5.1 Codex Max",
37076
- description: "OpenAI's largest coding model",
37194
+ id: "claude-opus-4-7",
37195
+ name: "Claude Opus 4.7",
37196
+ description: "Anthropic's latest Opus model",
37077
37197
  category: "reasoning",
37078
37198
  provider: "opencode-zen",
37079
- contextLength: 200000
37199
+ contextLength: 1e6
37080
37200
  },
37081
37201
  {
37082
37202
  id: "kimi-k2-thinking",
@@ -37087,7 +37207,45 @@ var OPENCODE_ZEN_MODELS = [
37087
37207
  contextLength: 262144
37088
37208
  }
37089
37209
  ];
37090
- var ALL_MODELS = [...OPENCODE_ZEN_MODELS, ...OPENROUTER_MODELS];
37210
+ var ALL_MODELS = [
37211
+ ...OPENCODE_ZEN_MODELS,
37212
+ ...OPENROUTER_MODELS,
37213
+ ...VERCEL_AI_GATEWAY_MODELS,
37214
+ ...CLOUDFLARE_AI_GATEWAY_MODELS,
37215
+ ...WORKERS_AI_MODELS
37216
+ ];
37217
+ function getProviderModels(provider) {
37218
+ switch (provider) {
37219
+ case "opencode-zen":
37220
+ return OPENCODE_ZEN_MODELS;
37221
+ case "openrouter":
37222
+ return OPENROUTER_MODELS;
37223
+ case "vercel-ai-gateway":
37224
+ return VERCEL_AI_GATEWAY_MODELS;
37225
+ case "cloudflare-ai-gateway":
37226
+ return CLOUDFLARE_AI_GATEWAY_MODELS;
37227
+ case "workers-ai":
37228
+ return WORKERS_AI_MODELS;
37229
+ case "custom":
37230
+ return [];
37231
+ }
37232
+ }
37233
+ function getProviderDisplayName(provider) {
37234
+ switch (provider) {
37235
+ case "opencode-zen":
37236
+ return "OpenCode Zen";
37237
+ case "openrouter":
37238
+ return "OpenRouter";
37239
+ case "vercel-ai-gateway":
37240
+ return "Vercel AI Gateway";
37241
+ case "cloudflare-ai-gateway":
37242
+ return "Cloudflare AI Gateway";
37243
+ case "workers-ai":
37244
+ return "Cloudflare Workers AI";
37245
+ case "custom":
37246
+ return "Custom";
37247
+ }
37248
+ }
37091
37249
  function isCustomModel(model) {
37092
37250
  return "baseUrl" in model;
37093
37251
  }
@@ -37458,11 +37616,20 @@ var CONFIG_FILE = join4(CONFIG_DIR, "config.json");
37458
37616
  var HISTORY_FILE = join4(CONFIG_DIR, "history.json");
37459
37617
  var KEYCHAIN_OPENROUTER = "openrouter-api-key";
37460
37618
  var KEYCHAIN_OPENCODE_ZEN = "opencode-zen-api-key";
37619
+ var KEYCHAIN_VERCEL_AI_GATEWAY = "vercel-ai-gateway-api-key";
37620
+ var KEYCHAIN_CLOUDFLARE_AI_GATEWAY = "cloudflare-ai-gateway-api-key";
37621
+ var KEYCHAIN_WORKERS_AI = "workers-ai-api-key";
37461
37622
  var DEFAULT_CONFIG = {
37462
37623
  provider: "opencode-zen",
37463
37624
  openrouterApiKey: "",
37464
37625
  opencodeZenApiKey: "",
37465
- defaultModel: "kimi-k2.6-free",
37626
+ vercelAiGatewayApiKey: "",
37627
+ cloudflareAiGatewayApiKey: "",
37628
+ workersAiApiKey: "",
37629
+ cloudflareAccountId: "",
37630
+ cloudflareAiGatewayId: "default",
37631
+ defaultModel: "minimax-m2.5-free",
37632
+ thinkingLevel: "low",
37466
37633
  safetyLevel: "moderate",
37467
37634
  dryRunByDefault: false,
37468
37635
  blockedCommands: [
@@ -37501,6 +37668,9 @@ function saveConfig(config) {
37501
37668
  if (isSecureStorageAvailable()) {
37502
37669
  configToSave.openrouterApiKey = "";
37503
37670
  configToSave.opencodeZenApiKey = "";
37671
+ configToSave.vercelAiGatewayApiKey = "";
37672
+ configToSave.cloudflareAiGatewayApiKey = "";
37673
+ configToSave.workersAiApiKey = "";
37504
37674
  }
37505
37675
  writeFileSync2(CONFIG_FILE, JSON.stringify(configToSave, null, 2));
37506
37676
  }
@@ -37513,28 +37683,83 @@ async function getApiKey(provider) {
37513
37683
  const envKey = process.env.OPENCODE_ZEN_API_KEY;
37514
37684
  if (envKey)
37515
37685
  return envKey;
37686
+ } else if (provider === "vercel-ai-gateway") {
37687
+ const envKey = process.env.AI_GATEWAY_API_KEY || process.env.VERCEL_AI_GATEWAY_API_KEY;
37688
+ if (envKey)
37689
+ return envKey;
37690
+ } else if (provider === "cloudflare-ai-gateway") {
37691
+ const envKey = process.env.CLOUDFLARE_AI_GATEWAY_API_KEY || process.env.CF_AIG_TOKEN;
37692
+ if (envKey)
37693
+ return envKey;
37694
+ } else if (provider === "workers-ai") {
37695
+ const envKey = process.env.CLOUDFLARE_API_TOKEN || process.env.CLOUDFLARE_API_KEY;
37696
+ if (envKey)
37697
+ return envKey;
37516
37698
  }
37517
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER : KEYCHAIN_OPENCODE_ZEN;
37699
+ const keychainKey = getKeychainKey(provider);
37518
37700
  const secureKey = await getSecret(keychainKey);
37519
37701
  if (secureKey)
37520
37702
  return secureKey;
37521
37703
  const config = loadConfig2();
37522
- return provider === "openrouter" ? config.openrouterApiKey : config.opencodeZenApiKey;
37704
+ switch (provider) {
37705
+ case "openrouter":
37706
+ return config.openrouterApiKey;
37707
+ case "opencode-zen":
37708
+ return config.opencodeZenApiKey;
37709
+ case "vercel-ai-gateway":
37710
+ return config.vercelAiGatewayApiKey || "";
37711
+ case "cloudflare-ai-gateway":
37712
+ return config.cloudflareAiGatewayApiKey || "";
37713
+ case "workers-ai":
37714
+ return config.workersAiApiKey || "";
37715
+ case "custom":
37716
+ return "";
37717
+ }
37523
37718
  }
37524
37719
  async function setApiKey(provider, key) {
37525
37720
  const config = loadConfig2();
37526
37721
  config.provider = provider;
37527
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER : KEYCHAIN_OPENCODE_ZEN;
37722
+ const keychainKey = getKeychainKey(provider);
37528
37723
  const stored = await setSecret(keychainKey, key);
37529
37724
  if (!stored) {
37530
- if (provider === "openrouter") {
37531
- config.openrouterApiKey = key;
37532
- } else {
37533
- config.opencodeZenApiKey = key;
37725
+ switch (provider) {
37726
+ case "openrouter":
37727
+ config.openrouterApiKey = key;
37728
+ break;
37729
+ case "opencode-zen":
37730
+ config.opencodeZenApiKey = key;
37731
+ break;
37732
+ case "vercel-ai-gateway":
37733
+ config.vercelAiGatewayApiKey = key;
37734
+ break;
37735
+ case "cloudflare-ai-gateway":
37736
+ config.cloudflareAiGatewayApiKey = key;
37737
+ break;
37738
+ case "workers-ai":
37739
+ config.workersAiApiKey = key;
37740
+ break;
37741
+ case "custom":
37742
+ break;
37534
37743
  }
37535
37744
  }
37536
37745
  saveConfig(config);
37537
37746
  }
37747
+ function getKeychainKey(provider) {
37748
+ switch (provider) {
37749
+ case "openrouter":
37750
+ return KEYCHAIN_OPENROUTER;
37751
+ case "opencode-zen":
37752
+ return KEYCHAIN_OPENCODE_ZEN;
37753
+ case "vercel-ai-gateway":
37754
+ return KEYCHAIN_VERCEL_AI_GATEWAY;
37755
+ case "cloudflare-ai-gateway":
37756
+ return KEYCHAIN_CLOUDFLARE_AI_GATEWAY;
37757
+ case "workers-ai":
37758
+ return KEYCHAIN_WORKERS_AI;
37759
+ case "custom":
37760
+ return "custom-api-key";
37761
+ }
37762
+ }
37538
37763
  function loadHistory() {
37539
37764
  ensureConfigDir();
37540
37765
  if (!existsSync3(HISTORY_FILE)) {
@@ -76080,9 +76305,6 @@ function formatRepoContext(context2) {
76080
76305
 
76081
76306
  // src/lib/api.ts
76082
76307
  function getZenApiType(modelId) {
76083
- if (modelId === "minimax-m2.5-free") {
76084
- return "anthropic";
76085
- }
76086
76308
  if (modelId.startsWith("gpt-")) {
76087
76309
  return "openai-responses";
76088
76310
  }
@@ -76176,7 +76398,94 @@ function cleanCommand(command) {
76176
76398
  }
76177
76399
  return cleaned.trim();
76178
76400
  }
76179
- async function callOpenRouter(apiKey, modelId, systemPrompt, userInput) {
76401
+ function getThinkingLevel(config2) {
76402
+ return config2?.thinkingLevel || "low";
76403
+ }
76404
+ function supportsThinkingControl(modelId) {
76405
+ return modelId.includes("thinking") || modelId.includes("gpt-5") || modelId.includes("claude-") || modelId.includes("gemini-");
76406
+ }
76407
+ function buildOpenRouterThinkingOptions(modelId, thinkingLevel) {
76408
+ if (thinkingLevel === "off" || !supportsThinkingControl(modelId)) {
76409
+ return {};
76410
+ }
76411
+ return {
76412
+ reasoning: {
76413
+ effort: thinkingLevel
76414
+ }
76415
+ };
76416
+ }
76417
+ function buildOpenAICompatibleThinkingOptions(modelId, thinkingLevel) {
76418
+ if (thinkingLevel === "off" || !supportsThinkingControl(modelId)) {
76419
+ return {};
76420
+ }
76421
+ return {
76422
+ reasoning_effort: thinkingLevel
76423
+ };
76424
+ }
76425
+ function buildAiSdkProviderOptions(modelId, thinkingLevel, providerOptionsName) {
76426
+ if (thinkingLevel === "off" || !supportsThinkingControl(modelId)) {
76427
+ return;
76428
+ }
76429
+ if (modelId.startsWith("gpt-")) {
76430
+ const compatibleOptions = providerOptionsName && providerOptionsName !== "openai" ? {
76431
+ [providerOptionsName]: {
76432
+ reasoningEffort: thinkingLevel
76433
+ }
76434
+ } : {};
76435
+ return {
76436
+ openai: {
76437
+ reasoningEffort: thinkingLevel
76438
+ },
76439
+ openaiCompatible: {
76440
+ reasoningEffort: thinkingLevel
76441
+ },
76442
+ ...compatibleOptions
76443
+ };
76444
+ }
76445
+ if (modelId.startsWith("gemini-")) {
76446
+ return {
76447
+ google: {
76448
+ thinkingConfig: {
76449
+ thinkingLevel
76450
+ }
76451
+ }
76452
+ };
76453
+ }
76454
+ if (modelId.startsWith("claude-")) {
76455
+ const budgetByLevel = {
76456
+ low: 1024,
76457
+ medium: 4096,
76458
+ high: 8192
76459
+ };
76460
+ return {
76461
+ anthropic: {
76462
+ thinking: {
76463
+ type: "enabled",
76464
+ budgetTokens: budgetByLevel[thinkingLevel]
76465
+ }
76466
+ }
76467
+ };
76468
+ }
76469
+ if (modelId.includes("thinking")) {
76470
+ return {
76471
+ openaiCompatible: {
76472
+ reasoningEffort: thinkingLevel
76473
+ },
76474
+ ...providerOptionsName ? {
76475
+ [providerOptionsName]: {
76476
+ reasoningEffort: thinkingLevel
76477
+ }
76478
+ } : {}
76479
+ };
76480
+ }
76481
+ return;
76482
+ }
76483
+ function shouldOmitTemperature(modelId, thinkingLevel) {
76484
+ return thinkingLevel !== "off" && (modelId.startsWith("claude-") || modelId.includes("gpt-5"));
76485
+ }
76486
+ async function callOpenRouter(apiKey, modelId, systemPrompt, userInput, thinkingLevel) {
76487
+ const thinkingOptions = buildOpenRouterThinkingOptions(modelId, thinkingLevel);
76488
+ const temperatureOptions = shouldOmitTemperature(modelId, thinkingLevel) ? {} : { temperature: 0.1 };
76180
76489
  const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
76181
76490
  method: "POST",
76182
76491
  headers: {
@@ -76192,7 +76501,8 @@ async function callOpenRouter(apiKey, modelId, systemPrompt, userInput) {
76192
76501
  { role: "user", content: userInput }
76193
76502
  ],
76194
76503
  max_tokens: 500,
76195
- temperature: 0.1
76504
+ ...temperatureOptions,
76505
+ ...thinkingOptions
76196
76506
  })
76197
76507
  });
76198
76508
  if (!response.ok) {
@@ -76212,18 +76522,99 @@ async function callOpenRouter(apiKey, modelId, systemPrompt, userInput) {
76212
76522
  }
76213
76523
  return data.choices[0]?.message?.content?.trim() || "";
76214
76524
  }
76525
+ async function callOpenAICompatibleFetch(baseURL, apiKey, modelId, systemPrompt, userInput, thinkingLevel, headers = {}, includeAuthorization = true) {
76526
+ const thinkingOptions = buildOpenAICompatibleThinkingOptions(modelId, thinkingLevel);
76527
+ const temperatureOptions = shouldOmitTemperature(modelId, thinkingLevel) ? {} : { temperature: 0.1 };
76528
+ const requestHeaders = {
76529
+ "Content-Type": "application/json",
76530
+ ...headers
76531
+ };
76532
+ if (includeAuthorization) {
76533
+ requestHeaders.Authorization = `Bearer ${apiKey}`;
76534
+ }
76535
+ const response = await fetch(`${baseURL.replace(/\/$/, "")}/chat/completions`, {
76536
+ method: "POST",
76537
+ headers: requestHeaders,
76538
+ body: JSON.stringify({
76539
+ model: modelId,
76540
+ messages: [
76541
+ { role: "system", content: systemPrompt },
76542
+ { role: "user", content: userInput }
76543
+ ],
76544
+ max_tokens: 500,
76545
+ stream: false,
76546
+ ...temperatureOptions,
76547
+ ...thinkingOptions
76548
+ })
76549
+ });
76550
+ if (!response.ok) {
76551
+ const errorText = await response.text();
76552
+ let errorMessage = `API request failed: ${response.status}`;
76553
+ try {
76554
+ const errorData = JSON.parse(errorText);
76555
+ if (errorData.error?.message) {
76556
+ errorMessage = errorData.error.message;
76557
+ } else if (errorData.errors?.[0]?.message) {
76558
+ errorMessage = errorData.errors[0].message;
76559
+ }
76560
+ } catch {}
76561
+ throw new Error(errorMessage);
76562
+ }
76563
+ const data = await response.json();
76564
+ if (data.error) {
76565
+ throw new Error(data.error.message);
76566
+ }
76567
+ if (data.errors?.[0]?.message) {
76568
+ throw new Error(data.errors[0].message);
76569
+ }
76570
+ const choices = data.choices || data.result?.choices;
76571
+ return choices?.[0]?.message?.content?.trim() || "";
76572
+ }
76573
+ function getCloudflareAccountId(config2) {
76574
+ return config2.cloudflareAccountId || process.env.CLOUDFLARE_ACCOUNT_ID || process.env.CF_ACCOUNT_ID || "";
76575
+ }
76576
+ function getCloudflareGatewayId(config2) {
76577
+ return config2.cloudflareAiGatewayId || process.env.CLOUDFLARE_AI_GATEWAY_ID || process.env.CF_AIG_GATEWAY_ID || "default";
76578
+ }
76579
+ async function callGatewayProvider(provider, apiKey, modelId, systemPrompt, userInput, thinkingLevel) {
76580
+ const config2 = loadConfig2();
76581
+ switch (provider) {
76582
+ case "vercel-ai-gateway":
76583
+ return await callOpenAICompatibleFetch("https://ai-gateway.vercel.sh/v1", apiKey, modelId, systemPrompt, userInput, thinkingLevel);
76584
+ case "cloudflare-ai-gateway": {
76585
+ const accountId = getCloudflareAccountId(config2);
76586
+ if (!accountId) {
76587
+ throw new Error("Cloudflare account ID is required. Set cloudflareAccountId in config or CLOUDFLARE_ACCOUNT_ID.");
76588
+ }
76589
+ const gatewayId = getCloudflareGatewayId(config2);
76590
+ return await callOpenAICompatibleFetch(`https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/compat`, apiKey, modelId, systemPrompt, userInput, thinkingLevel, { "cf-aig-authorization": `Bearer ${apiKey}` }, false);
76591
+ }
76592
+ case "workers-ai": {
76593
+ const accountId = getCloudflareAccountId(config2);
76594
+ if (!accountId) {
76595
+ throw new Error("Cloudflare account ID is required. Set cloudflareAccountId in config or CLOUDFLARE_ACCOUNT_ID.");
76596
+ }
76597
+ return await callOpenAICompatibleFetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`, apiKey, modelId, systemPrompt, userInput, thinkingLevel);
76598
+ }
76599
+ default:
76600
+ throw new Error(`Unsupported gateway provider: ${provider}`);
76601
+ }
76602
+ }
76215
76603
  var DEBUG_API = process.env.DEBUG_API === "1";
76216
- async function generateZenText(model, systemPrompt, userInput) {
76604
+ async function generateZenText(model, modelId, systemPrompt, userInput, thinkingLevel, providerOptionsName) {
76605
+ const providerOptions = buildAiSdkProviderOptions(modelId, thinkingLevel, providerOptionsName);
76606
+ const temperatureOptions = shouldOmitTemperature(modelId, thinkingLevel) ? {} : { temperature: 0.1 };
76217
76607
  const { text: text2 } = await generateText({
76218
76608
  model,
76219
76609
  system: systemPrompt,
76220
76610
  prompt: userInput,
76221
76611
  maxOutputTokens: 500,
76222
- temperature: 0.1
76612
+ ...temperatureOptions,
76613
+ ...providerOptions ? { providerOptions } : {}
76223
76614
  });
76224
76615
  return text2.trim();
76225
76616
  }
76226
- async function callZenOpenAIResponses(apiKey, modelId, systemPrompt, userInput) {
76617
+ async function callZenOpenAIResponses(apiKey, modelId, systemPrompt, userInput, thinkingLevel) {
76227
76618
  if (DEBUG_API) {
76228
76619
  console.error(`[DEBUG] Calling OpenAI Responses API`);
76229
76620
  console.error(`[DEBUG] Model: ${modelId}`);
@@ -76234,7 +76625,7 @@ async function callZenOpenAIResponses(apiKey, modelId, systemPrompt, userInput)
76234
76625
  baseURL: ZEN_BASE_URL
76235
76626
  });
76236
76627
  try {
76237
- return await generateZenText(openai2(modelId), systemPrompt, userInput);
76628
+ return await generateZenText(openai2(modelId), modelId, systemPrompt, userInput, thinkingLevel);
76238
76629
  } catch (error40) {
76239
76630
  const message = error40 instanceof Error ? error40.message : String(error40);
76240
76631
  if (DEBUG_API) {
@@ -76243,7 +76634,7 @@ async function callZenOpenAIResponses(apiKey, modelId, systemPrompt, userInput)
76243
76634
  throw new Error(message);
76244
76635
  }
76245
76636
  }
76246
- async function callZenAnthropic(apiKey, modelId, systemPrompt, userInput) {
76637
+ async function callZenAnthropic(apiKey, modelId, systemPrompt, userInput, thinkingLevel) {
76247
76638
  if (DEBUG_API) {
76248
76639
  console.error(`[DEBUG] Calling Anthropic Messages API`);
76249
76640
  console.error(`[DEBUG] Model: ${modelId}`);
@@ -76254,7 +76645,7 @@ async function callZenAnthropic(apiKey, modelId, systemPrompt, userInput) {
76254
76645
  baseURL: ZEN_BASE_URL
76255
76646
  });
76256
76647
  try {
76257
- return await generateZenText(anthropic2(modelId), systemPrompt, userInput);
76648
+ return await generateZenText(anthropic2(modelId), modelId, systemPrompt, userInput, thinkingLevel);
76258
76649
  } catch (error40) {
76259
76650
  const message = error40 instanceof Error ? error40.message : String(error40);
76260
76651
  if (DEBUG_API) {
@@ -76263,7 +76654,7 @@ async function callZenAnthropic(apiKey, modelId, systemPrompt, userInput) {
76263
76654
  throw new Error(message);
76264
76655
  }
76265
76656
  }
76266
- async function callZenOpenAICompatible(apiKey, modelId, systemPrompt, userInput) {
76657
+ async function callZenOpenAICompatible(apiKey, modelId, systemPrompt, userInput, thinkingLevel) {
76267
76658
  if (DEBUG_API) {
76268
76659
  console.error(`[DEBUG] Calling OpenAI-compatible Chat Completions API`);
76269
76660
  console.error(`[DEBUG] Model: ${modelId}`);
@@ -76274,7 +76665,7 @@ async function callZenOpenAICompatible(apiKey, modelId, systemPrompt, userInput)
76274
76665
  baseURL: ZEN_BASE_URL
76275
76666
  });
76276
76667
  try {
76277
- return await generateZenText(openaiCompatible(modelId), systemPrompt, userInput);
76668
+ return await generateZenText(openaiCompatible(modelId), modelId, systemPrompt, userInput, thinkingLevel, "opencodeZen");
76278
76669
  } catch (error40) {
76279
76670
  const message = error40 instanceof Error ? error40.message : String(error40);
76280
76671
  if (DEBUG_API) {
@@ -76283,7 +76674,7 @@ async function callZenOpenAICompatible(apiKey, modelId, systemPrompt, userInput)
76283
76674
  throw new Error(message);
76284
76675
  }
76285
76676
  }
76286
- async function callCustomModel(model, systemPrompt, userInput) {
76677
+ async function callCustomModel(model, systemPrompt, userInput, thinkingLevel) {
76287
76678
  if (DEBUG_API) {
76288
76679
  console.error(`[DEBUG] Calling Custom Model`);
76289
76680
  console.error(`[DEBUG] Model: ${model.modelId}`);
@@ -76295,7 +76686,7 @@ async function callCustomModel(model, systemPrompt, userInput) {
76295
76686
  baseURL: model.baseUrl
76296
76687
  });
76297
76688
  try {
76298
- return await generateZenText(openaiCompatible(model.modelId), systemPrompt, userInput);
76689
+ return await generateZenText(openaiCompatible(model.modelId), model.modelId, systemPrompt, userInput, thinkingLevel, "custom");
76299
76690
  } catch (error40) {
76300
76691
  const message = error40 instanceof Error ? error40.message : String(error40);
76301
76692
  if (DEBUG_API) {
@@ -76304,7 +76695,7 @@ async function callCustomModel(model, systemPrompt, userInput) {
76304
76695
  throw new Error(message);
76305
76696
  }
76306
76697
  }
76307
- async function callZenGoogle(apiKey, modelId, systemPrompt, userInput) {
76698
+ async function callZenGoogle(apiKey, modelId, systemPrompt, userInput, thinkingLevel) {
76308
76699
  if (DEBUG_API) {
76309
76700
  console.error(`[DEBUG] Calling Google Gemini API`);
76310
76701
  console.error(`[DEBUG] Model: ${modelId}`);
@@ -76314,7 +76705,7 @@ async function callZenGoogle(apiKey, modelId, systemPrompt, userInput) {
76314
76705
  baseURL: `https://opencode.ai/zen/v1/models/${modelId}`
76315
76706
  });
76316
76707
  try {
76317
- return await generateZenText(google2(modelId), systemPrompt, userInput);
76708
+ return await generateZenText(google2(modelId), modelId, systemPrompt, userInput, thinkingLevel);
76318
76709
  } catch (error40) {
76319
76710
  const message = error40 instanceof Error ? error40.message : String(error40);
76320
76711
  if (DEBUG_API) {
@@ -76330,28 +76721,31 @@ function getShellInfo() {
76330
76721
  }
76331
76722
  return cachedShellInfo;
76332
76723
  }
76333
- async function translateToCommand(apiKey, model, userInput, cwd, history = [], repoContextEnabled) {
76724
+ async function translateToCommand(apiKey, model, userInput, cwd, history = [], repoContextEnabled, config2) {
76334
76725
  const shellInfo = getShellInfo();
76335
76726
  const systemPrompt = buildSystemPrompt(cwd, history, shellInfo, repoContextEnabled);
76727
+ const thinkingLevel = getThinkingLevel(config2);
76336
76728
  let rawCommand;
76337
76729
  if (isCustomModel(model)) {
76338
- rawCommand = await callCustomModel(model, systemPrompt, userInput);
76730
+ rawCommand = await callCustomModel(model, systemPrompt, userInput, "off");
76339
76731
  } else if (model.provider === "openrouter") {
76340
- rawCommand = await callOpenRouter(apiKey, model.id, systemPrompt, userInput);
76732
+ rawCommand = await callOpenRouter(apiKey, model.id, systemPrompt, userInput, thinkingLevel);
76733
+ } else if (model.provider === "vercel-ai-gateway" || model.provider === "cloudflare-ai-gateway" || model.provider === "workers-ai") {
76734
+ rawCommand = await callGatewayProvider(model.provider, apiKey, model.id, systemPrompt, userInput, thinkingLevel);
76341
76735
  } else {
76342
76736
  const apiType = getZenApiType(model.id);
76343
76737
  switch (apiType) {
76344
76738
  case "openai-responses":
76345
- rawCommand = await callZenOpenAIResponses(apiKey, model.id, systemPrompt, userInput);
76739
+ rawCommand = await callZenOpenAIResponses(apiKey, model.id, systemPrompt, userInput, thinkingLevel);
76346
76740
  break;
76347
76741
  case "anthropic":
76348
- rawCommand = await callZenAnthropic(apiKey, model.id, systemPrompt, userInput);
76742
+ rawCommand = await callZenAnthropic(apiKey, model.id, systemPrompt, userInput, thinkingLevel);
76349
76743
  break;
76350
76744
  case "google":
76351
- rawCommand = await callZenGoogle(apiKey, model.id, systemPrompt, userInput);
76745
+ rawCommand = await callZenGoogle(apiKey, model.id, systemPrompt, userInput, thinkingLevel);
76352
76746
  break;
76353
76747
  case "openai-compatible":
76354
- rawCommand = await callZenOpenAICompatible(apiKey, model.id, systemPrompt, userInput);
76748
+ rawCommand = await callZenOpenAICompatible(apiKey, model.id, systemPrompt, userInput, thinkingLevel);
76355
76749
  break;
76356
76750
  }
76357
76751
  }
@@ -76570,6 +76964,22 @@ var statusBarText;
76570
76964
  var chatScrollBox;
76571
76965
  var inputField;
76572
76966
  var inputContainer;
76967
+ function getApiKeyUrl(provider) {
76968
+ switch (provider) {
76969
+ case "opencode-zen":
76970
+ return "https://opencode.ai/auth";
76971
+ case "openrouter":
76972
+ return "https://openrouter.ai/keys";
76973
+ case "vercel-ai-gateway":
76974
+ return "https://vercel.com/docs/ai-gateway";
76975
+ case "cloudflare-ai-gateway":
76976
+ return "https://developers.cloudflare.com/ai-gateway/";
76977
+ case "workers-ai":
76978
+ return "https://dash.cloudflare.com/profile/api-tokens";
76979
+ case "custom":
76980
+ return "";
76981
+ }
76982
+ }
76573
76983
  var inputHintText;
76574
76984
  var helpBarText;
76575
76985
  var modelSelector = null;
@@ -76640,12 +77050,27 @@ async function showProviderSetup() {
76640
77050
  name: "OpenRouter",
76641
77051
  description: "Access to many models from various providers",
76642
77052
  value: "openrouter"
77053
+ },
77054
+ {
77055
+ name: "Vercel AI Gateway",
77056
+ description: "Unified model gateway using AI_GATEWAY_API_KEY",
77057
+ value: "vercel-ai-gateway"
77058
+ },
77059
+ {
77060
+ name: "Cloudflare AI Gateway",
77061
+ description: "Cloudflare gateway for provider and Workers AI models",
77062
+ value: "cloudflare-ai-gateway"
77063
+ },
77064
+ {
77065
+ name: "Cloudflare Workers AI",
77066
+ description: "Cloudflare-hosted models via Workers AI",
77067
+ value: "workers-ai"
76643
77068
  }
76644
77069
  ];
76645
77070
  providerSelector = new SelectRenderable(renderer, {
76646
77071
  id: "provider-select",
76647
77072
  width: 60,
76648
- height: 6,
77073
+ height: 10,
76649
77074
  options,
76650
77075
  backgroundColor: "#1e293b",
76651
77076
  focusedBackgroundColor: "#1e293b",
@@ -76692,11 +77117,11 @@ async function showApiKeyInput(provider) {
76692
77117
  renderer.root.add(container);
76693
77118
  const title = new TextRenderable(renderer, {
76694
77119
  id: "apikey-title",
76695
- content: t`${bold(fg("#60a5fa")(`${provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter"} Setup`))}`,
77120
+ content: t`${bold(fg("#60a5fa")(`${getProviderDisplayName(provider)} Setup`))}`,
76696
77121
  marginBottom: 1
76697
77122
  });
76698
77123
  container.add(title);
76699
- const url2 = provider === "opencode-zen" ? "https://opencode.ai/auth" : "https://openrouter.ai/keys";
77124
+ const url2 = getApiKeyUrl(provider);
76700
77125
  const instructions = new TextRenderable(renderer, {
76701
77126
  id: "apikey-instructions",
76702
77127
  content: t`Get your API key from: ${fg("#22c55e")(url2)}
@@ -76708,7 +77133,7 @@ Enter your API key below:`,
76708
77133
  const input = new InputRenderable(renderer, {
76709
77134
  id: "api-key-input",
76710
77135
  width: 70,
76711
- placeholder: provider === "opencode-zen" ? "zen_..." : "sk-or-v1-...",
77136
+ placeholder: provider === "openrouter" ? "sk-or-v1-..." : "API key or token",
76712
77137
  backgroundColor: "#1e293b",
76713
77138
  focusedBackgroundColor: "#334155",
76714
77139
  textColor: "#f8fafc",
@@ -76734,11 +77159,8 @@ ${fg("#64748b")("Press Enter to save | Ctrl+C to exit")}`,
76734
77159
  input.on(InputRenderableEvents.ENTER, (value) => {
76735
77160
  if (value.trim()) {
76736
77161
  setApiKey(provider, value.trim());
76737
- if (provider === "opencode-zen") {
76738
- currentModel = OPENCODE_ZEN_MODELS.find((m2) => m2.id === "kimi-k2.6-free") || OPENCODE_ZEN_MODELS[0];
76739
- } else {
76740
- currentModel = OPENROUTER_MODELS[0];
76741
- }
77162
+ const providerModels = getProviderModels(provider);
77163
+ currentModel = providerModels.find((m2) => !m2.disabled) || providerModels[0] || OPENCODE_ZEN_MODELS[0];
76742
77164
  config2.defaultModel = currentModel.id;
76743
77165
  saveConfig(config2);
76744
77166
  renderer.root.remove("apikey-container");
@@ -76856,26 +77278,27 @@ function createMainUI() {
76856
77278
  }
76857
77279
  function getStatusBarContent() {
76858
77280
  const theme = getTheme();
76859
- const providerName = config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
77281
+ const providerName = getProviderDisplayName(config2.provider);
76860
77282
  const safeModeIndicator = dryRunMode ? fg(theme.colors.warning)("[DRY RUN]") : "";
76861
77283
  const safetyLevelColor = config2.safetyLevel === "strict" ? theme.colors.warning : config2.safetyLevel === "relaxed" ? theme.colors.error : theme.colors.success;
76862
77284
  const safetyIndicator = fg(safetyLevelColor)(`[${config2.safetyLevel}]`);
77285
+ const thinkingIndicator = config2.thinkingLevel !== "off" ? fg(theme.colors.secondary)(`[Think:${config2.thinkingLevel}]`) : "";
76863
77286
  const repoContextIndicator = config2.repoContext ? fg(theme.colors.info)("[Repo]") : "";
76864
- return t`${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)} ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)} ${safetyIndicator}${safeModeIndicator ? " " : ""}${safeModeIndicator}${repoContextIndicator ? " " : ""}${repoContextIndicator}`;
77287
+ return t`${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)} ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)} ${safetyIndicator}${thinkingIndicator ? " " : ""}${thinkingIndicator}${safeModeIndicator ? " " : ""}${safeModeIndicator}${repoContextIndicator ? " " : ""}${repoContextIndicator}`;
76865
77288
  }
76866
77289
  function getHelpBarContent() {
76867
77290
  const theme = getTheme();
76868
77291
  if (awaitingConfirmation) {
76869
77292
  return t`${fg(theme.colors.warning)(">>> Cmd+Enter or Enter to execute <<<")} ${fg(theme.colors.textMuted)("|")} ${fg(theme.colors.error)("Esc")}${fg(theme.colors.textMuted)(" Cancel")} ${fg(theme.colors.primary)("e")}${fg(theme.colors.textMuted)(" Edit")} ${fg(theme.colors.primary)("c")}${fg(theme.colors.textMuted)(" Copy")}`;
76870
77293
  }
76871
- return t`${fg(theme.colors.primary)("Ctrl+X P")}${fg(theme.colors.textMuted)(" Commands")} ${fg(theme.colors.primary)("Ctrl+Y")}${fg(theme.colors.textMuted)(" Safety")} ${fg(theme.colors.primary)("Ctrl+Z")}${fg(theme.colors.textMuted)(" Exit")}`;
77294
+ return t`${fg(theme.colors.primary)("Ctrl+X P")}${fg(theme.colors.textMuted)(" Commands")} ${fg(theme.colors.primary)("Ctrl+Y")}${fg(theme.colors.textMuted)(" Safety")} ${fg(theme.colors.primary)("Ctrl+K")}${fg(theme.colors.textMuted)(" Think")} ${fg(theme.colors.primary)("Ctrl+Z")}${fg(theme.colors.textMuted)(" Exit")}`;
76872
77295
  }
76873
77296
  function getInputHintContent() {
76874
77297
  const theme = getTheme();
76875
77298
  return t`${fg(theme.colors.primary)("Enter")} ${fg(theme.colors.textMuted)("to send")}`;
76876
77299
  }
76877
77300
  function getWelcomeMessage() {
76878
- const providerName = config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
77301
+ const providerName = getProviderDisplayName(config2.provider);
76879
77302
  return `Ready. Using ${providerName}.
76880
77303
  Type what you want to do, or press Ctrl+X P for command palette.`;
76881
77304
  }
@@ -77191,7 +77614,7 @@ async function translateAndProcess(input) {
77191
77614
  }
77192
77615
  const loadingMsg = addSystemMessage("Translating...");
77193
77616
  try {
77194
- const command = await translateToCommand(apiKey, currentModel, input, currentCwd, history, config2.repoContext);
77617
+ const command = await translateToCommand(apiKey, currentModel, input, currentCwd, history, config2.repoContext, config2);
77195
77618
  chatScrollBox.remove(`msg-${loadingMsg.id}`);
77196
77619
  chatMessages = chatMessages.filter((m2) => m2.id !== loadingMsg.id);
77197
77620
  const safety = analyzeCommand(command, config2);
@@ -77321,6 +77744,10 @@ async function handleSpecialCommand(input) {
77321
77744
  statusBarText.content = getStatusBarContent();
77322
77745
  addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
77323
77746
  break;
77747
+ case "thinking":
77748
+ case "think":
77749
+ cycleThinkingLevel();
77750
+ break;
77324
77751
  case "config":
77325
77752
  await showConfig();
77326
77753
  break;
@@ -77347,6 +77774,7 @@ function clearChat() {
77347
77774
  function showHelp() {
77348
77775
  const helpText = `Direct Shortcuts:
77349
77776
  Ctrl+Y Cycle safety level (strict/moderate/relaxed)
77777
+ Ctrl+K Cycle thinking level (low/medium/high/off)
77350
77778
  Ctrl+Z Exit magic-shell
77351
77779
  Ctrl+C Cancel / Close popup
77352
77780
 
@@ -77355,19 +77783,26 @@ P Command palette M Change model
77355
77783
  S Switch provider D Toggle dry-run
77356
77784
  T Change theme R Toggle repo context
77357
77785
  H Show history L Clear chat
77358
- C Show config ? This help
77786
+ C Show config K Thinking level
77787
+ ? This help
77359
77788
 
77360
77789
  Commands (type ! or / followed by):
77361
77790
  help Show this help model Change model
77362
77791
  provider Switch provider dry Toggle dry-run
77363
- config Show configuration history Show history
77364
- clear Clear chat
77792
+ thinking Cycle thinking config Show configuration
77793
+ history Show history clear Clear chat
77365
77794
 
77366
77795
  Safety Levels:
77367
77796
  - strict: Confirm ALL potentially dangerous commands
77368
77797
  - moderate: Confirm high/critical severity commands (default)
77369
77798
  - relaxed: Only confirm critical commands
77370
77799
 
77800
+ Thinking Levels:
77801
+ - low: Default low-cost reasoning for supported models
77802
+ - medium: More reasoning for harder translations
77803
+ - high: Maximum reasoning for supported models
77804
+ - off: Do not request provider thinking controls
77805
+
77371
77806
  Tips:
77372
77807
  - Type naturally: "list all files" -> ls -la
77373
77808
  - Use ! or / commands: !help or /help
@@ -77377,7 +77812,7 @@ Tips:
77377
77812
  }
77378
77813
  async function showConfig() {
77379
77814
  const theme = getTheme();
77380
- const providerName = config2.provider === "opencode-zen" ? "OpenCode Zen" : config2.provider === "openrouter" ? "OpenRouter" : "Custom";
77815
+ const providerName = getProviderDisplayName(config2.provider);
77381
77816
  const apiKey = await getApiKey(config2.provider);
77382
77817
  const apiKeyStatus = apiKey ? "configured" : "not set";
77383
77818
  const isCustom = isCustomModel(currentModel);
@@ -77394,6 +77829,7 @@ Theme: ${theme.name}
77394
77829
  Shell: ${shellInfo.shell} (${shellInfo.shellPath})
77395
77830
  Platform: ${shellInfo.platform}${shellInfo.isWSL ? " (WSL)" : ""}
77396
77831
  Safety: ${config2.safetyLevel}
77832
+ Thinking: ${config2.thinkingLevel}
77397
77833
  Dry-run: ${dryRunMode ? "ON" : "OFF"}
77398
77834
  Repo context: ${config2.repoContext ? "ON" : "OFF"}
77399
77835
  API Key: ${apiKeyStatus}
@@ -77422,7 +77858,7 @@ async function switchProvider() {
77422
77858
  left: 2,
77423
77859
  top: 4,
77424
77860
  width: 65,
77425
- height: 12,
77861
+ height: 15,
77426
77862
  backgroundColor: "#1e293b",
77427
77863
  border: true,
77428
77864
  borderColor: "#60a5fa",
@@ -77433,24 +77869,25 @@ async function switchProvider() {
77433
77869
  padding: 1
77434
77870
  });
77435
77871
  renderer.root.add(container);
77436
- const zenKey = await getApiKey("opencode-zen");
77437
- const orKey = await getApiKey("openrouter");
77438
- const options = [
77439
- {
77440
- name: `OpenCode Zen${zenKey ? " (configured)" : ""}`,
77441
- description: "Curated models optimized for coding. Has free models!",
77442
- value: "opencode-zen"
77443
- },
77444
- {
77445
- name: `OpenRouter${orKey ? " (configured)" : ""}`,
77446
- description: "Access to many models from various providers",
77447
- value: "openrouter"
77448
- }
77872
+ const providers = [
77873
+ { provider: "opencode-zen", description: "Curated models optimized for coding. Has free models!" },
77874
+ { provider: "openrouter", description: "Access to many models from various providers" },
77875
+ { provider: "vercel-ai-gateway", description: "Unified model gateway using AI_GATEWAY_API_KEY" },
77876
+ { provider: "cloudflare-ai-gateway", description: "Cloudflare gateway for provider and Workers AI models" },
77877
+ { provider: "workers-ai", description: "Cloudflare-hosted models via Workers AI" }
77449
77878
  ];
77879
+ const options = await Promise.all(providers.map(async ({ provider, description }) => {
77880
+ const key = await getApiKey(provider);
77881
+ return {
77882
+ name: `${getProviderDisplayName(provider)}${key ? " (configured)" : ""}`,
77883
+ description,
77884
+ value: provider
77885
+ };
77886
+ }));
77450
77887
  const selector = new SelectRenderable(renderer, {
77451
77888
  id: "provider-switch-select",
77452
77889
  width: "100%",
77453
- height: 6,
77890
+ height: 9,
77454
77891
  options,
77455
77892
  backgroundColor: "transparent",
77456
77893
  focusedBackgroundColor: "transparent",
@@ -77472,13 +77909,13 @@ async function switchProvider() {
77472
77909
  const existingKey = await getApiKey(newProvider);
77473
77910
  if (existingKey) {
77474
77911
  config2.provider = newProvider;
77475
- const models = newProvider === "opencode-zen" ? OPENCODE_ZEN_MODELS : OPENROUTER_MODELS;
77912
+ const models = getProviderModels(newProvider);
77476
77913
  currentModel = models.find((m2) => m2.id === config2.defaultModel) || models[0];
77477
77914
  config2.defaultModel = currentModel.id;
77478
77915
  saveConfig(config2);
77479
77916
  statusBarText.content = getStatusBarContent();
77480
77917
  closeSelector();
77481
- const providerName = newProvider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
77918
+ const providerName = getProviderDisplayName(newProvider);
77482
77919
  addSystemMessage(`Switched to ${providerName}. Model: ${currentModel.name}`);
77483
77920
  } else {
77484
77921
  closeSelector();
@@ -77510,13 +77947,13 @@ function showModelSelector() {
77510
77947
  border: true,
77511
77948
  borderColor: "#60a5fa",
77512
77949
  borderStyle: "single",
77513
- title: `Select Model (${config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter"})`,
77950
+ title: `Select Model (${getProviderDisplayName(config2.provider)})`,
77514
77951
  titleAlignment: "center",
77515
77952
  zIndex: 100,
77516
77953
  padding: 1
77517
77954
  });
77518
77955
  renderer.root.add(container);
77519
- const allModels = config2.provider === "opencode-zen" ? OPENCODE_ZEN_MODELS : OPENROUTER_MODELS;
77956
+ const allModels = getProviderModels(config2.provider);
77520
77957
  const availableModels = allModels.filter((m2) => !m2.disabled).sort((a, b2) => a.name.localeCompare(b2.name));
77521
77958
  const customModels = getCustomModels().sort((a, b2) => a.name.localeCompare(b2.name));
77522
77959
  const options = [
@@ -77632,6 +78069,15 @@ function showThemeSelector() {
77632
78069
  }
77633
78070
  var commandPalette = null;
77634
78071
  var chordMode = "none";
78072
+ function cycleThinkingLevel() {
78073
+ const levels = ["low", "medium", "high", "off"];
78074
+ const currentIndex = levels.indexOf(config2.thinkingLevel);
78075
+ const nextIndex = (currentIndex + 1) % levels.length;
78076
+ config2.thinkingLevel = levels[nextIndex];
78077
+ saveConfig(config2);
78078
+ statusBarText.content = getStatusBarContent();
78079
+ addSystemMessage(`Thinking level: ${config2.thinkingLevel}`);
78080
+ }
77635
78081
  function getCommandPaletteOptions() {
77636
78082
  return [
77637
78083
  {
@@ -77650,7 +78096,7 @@ function getCommandPaletteOptions() {
77650
78096
  },
77651
78097
  {
77652
78098
  name: "Switch Provider",
77653
- description: `Current: ${config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter"}`,
78099
+ description: `Current: ${getProviderDisplayName(config2.provider)}`,
77654
78100
  key: "s",
77655
78101
  chord: "s",
77656
78102
  action: () => switchProvider()
@@ -77686,6 +78132,13 @@ function getCommandPaletteOptions() {
77686
78132
  addSystemMessage(`Safety level: ${config2.safetyLevel} (${descriptions[config2.safetyLevel]})`);
77687
78133
  }
77688
78134
  },
78135
+ {
78136
+ name: "Cycle Thinking Level",
78137
+ description: `Current: ${config2.thinkingLevel}`,
78138
+ key: "k",
78139
+ chord: "k",
78140
+ action: () => cycleThinkingLevel()
78141
+ },
77689
78142
  {
77690
78143
  name: "Toggle Project Context",
77691
78144
  description: config2.repoContext ? "Currently ON (sends script names to AI)" : "Currently OFF",
@@ -77822,6 +78275,10 @@ function handleKeypress(key) {
77822
78275
  addSystemMessage(`Safety level: ${config2.safetyLevel} (${descriptions[config2.safetyLevel]})`);
77823
78276
  return;
77824
78277
  }
78278
+ if (key.ctrl && key.name === "k") {
78279
+ cycleThinkingLevel();
78280
+ return;
78281
+ }
77825
78282
  if (key.ctrl && key.name === "z") {
77826
78283
  renderer.destroy();
77827
78284
  process.exit(0);