@austinthesing/magic-shell 0.2.18 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui.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,16 +36896,109 @@ var OPENROUTER_MODELS = [
36849
36896
  contextLength: 262144
36850
36897
  }
36851
36898
  ];
36852
- var OPENCODE_ZEN_MODELS = [
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
+ },
36853
36950
  {
36854
- id: "minimax-m2.5-free",
36855
- name: "MiniMax M2.5 Free",
36856
- description: "MiniMax's free model (limited time)",
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",
36857
36988
  category: "fast",
36858
- provider: "opencode-zen",
36859
- contextLength: 196608,
36860
- free: true
36989
+ provider: "workers-ai",
36990
+ contextLength: 8000
36861
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
+ ];
37001
+ var OPENCODE_ZEN_MODELS = [
36862
37002
  {
36863
37003
  id: "kimi-k2.6-free",
36864
37004
  name: "Kimi K2.6 Free",
@@ -36885,23 +37025,6 @@ var OPENCODE_ZEN_MODELS = [
36885
37025
  provider: "opencode-zen",
36886
37026
  contextLength: 1048576
36887
37027
  },
36888
- {
36889
- id: "gpt-5-nano",
36890
- name: "GPT 5 Nano",
36891
- description: "OpenAI's fastest GPT model (free)",
36892
- category: "fast",
36893
- provider: "opencode-zen",
36894
- contextLength: 200000,
36895
- free: true
36896
- },
36897
- {
36898
- id: "claude-3-5-haiku",
36899
- name: "Claude Haiku 3.5",
36900
- description: "Anthropic's fast and efficient model",
36901
- category: "fast",
36902
- provider: "opencode-zen",
36903
- contextLength: 200000
36904
- },
36905
37028
  {
36906
37029
  id: "claude-haiku-4-5",
36907
37030
  name: "Claude Haiku 4.5",
@@ -36919,84 +37042,68 @@ var OPENCODE_ZEN_MODELS = [
36919
37042
  contextLength: 200000
36920
37043
  },
36921
37044
  {
36922
- id: "gpt-5.1-codex-mini",
36923
- name: "GPT 5.1 Codex Mini",
36924
- description: "OpenAI's fast codex model",
37045
+ id: "gpt-5.4-mini",
37046
+ name: "GPT 5.4 Mini",
37047
+ description: "OpenAI's latest fast GPT mini model",
36925
37048
  category: "fast",
36926
37049
  provider: "opencode-zen",
36927
- contextLength: 200000
37050
+ contextLength: 400000
36928
37051
  },
36929
37052
  {
36930
- id: "deepseek-v4-flash",
36931
- name: "DeepSeek V4 Flash",
36932
- description: "DeepSeek's latest fast open-source model",
37053
+ id: "gpt-5.4-nano",
37054
+ name: "GPT 5.4 Nano",
37055
+ description: "OpenAI's latest lightweight GPT model",
36933
37056
  category: "fast",
36934
37057
  provider: "opencode-zen",
36935
- contextLength: 1048576
37058
+ contextLength: 400000
36936
37059
  },
36937
37060
  {
36938
- id: "claude-sonnet-4",
36939
- name: "Claude Sonnet 4",
36940
- description: "Anthropic's balanced model for complex tasks",
37061
+ id: "claude-sonnet-4-6",
37062
+ name: "Claude Sonnet 4.6",
37063
+ description: "Anthropic's latest Sonnet model",
36941
37064
  category: "smart",
36942
37065
  provider: "opencode-zen",
36943
- contextLength: 200000
37066
+ contextLength: 1e6
36944
37067
  },
36945
37068
  {
36946
- id: "gemini-3-pro",
36947
- name: "Gemini 3 Pro",
37069
+ id: "gemini-3.1-pro",
37070
+ name: "Gemini 3.1 Pro",
36948
37071
  description: "Google's high-end Gemini model",
36949
37072
  category: "smart",
36950
37073
  provider: "opencode-zen",
36951
37074
  contextLength: 200000
36952
37075
  },
36953
37076
  {
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",
37077
+ id: "gpt-5.5",
37078
+ name: "GPT 5.5",
37079
+ description: "OpenAI's latest flagship GPT model",
36965
37080
  category: "smart",
36966
37081
  provider: "opencode-zen",
36967
- contextLength: 200000
37082
+ contextLength: 1050000
36968
37083
  },
36969
37084
  {
36970
- id: "gpt-5.1",
36971
- name: "GPT 5.1",
36972
- description: "OpenAI's balanced GPT model",
37085
+ id: "gpt-5.5-pro",
37086
+ name: "GPT 5.5 Pro",
37087
+ description: "OpenAI's latest high-capability reasoning model",
36973
37088
  category: "smart",
36974
37089
  provider: "opencode-zen",
36975
- contextLength: 200000
37090
+ contextLength: 1050000
36976
37091
  },
36977
37092
  {
36978
- id: "gpt-5.1-codex",
36979
- name: "GPT 5.1 Codex",
36980
- description: "OpenAI's coding model",
37093
+ id: "gpt-5.3-codex",
37094
+ name: "GPT 5.3 Codex",
37095
+ description: "OpenAI's latest coding-focused GPT model",
36981
37096
  category: "smart",
36982
37097
  provider: "opencode-zen",
36983
- contextLength: 200000
37098
+ contextLength: 400000
36984
37099
  },
36985
37100
  {
36986
- id: "gpt-5",
36987
- name: "GPT 5",
36988
- description: "OpenAI's prior generation GPT model",
37101
+ id: "gpt-5.3-codex-spark",
37102
+ name: "GPT 5.3 Codex Spark",
37103
+ description: "OpenAI's latest fast coding-focused GPT model",
36989
37104
  category: "smart",
36990
37105
  provider: "opencode-zen",
36991
- contextLength: 200000
36992
- },
36993
- {
36994
- id: "gpt-5-codex",
36995
- name: "GPT 5 Codex",
36996
- description: "OpenAI's prior generation codex model",
36997
- category: "smart",
36998
- provider: "opencode-zen",
36999
- contextLength: 200000
37106
+ contextLength: 400000
37000
37107
  },
37001
37108
  {
37002
37109
  id: "minimax-m2.7",
@@ -37039,44 +37146,12 @@ var OPENCODE_ZEN_MODELS = [
37039
37146
  contextLength: 202752
37040
37147
  },
37041
37148
  {
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",
37149
+ id: "claude-opus-4-7",
37150
+ name: "Claude Opus 4.7",
37151
+ description: "Anthropic's latest Opus model",
37069
37152
  category: "reasoning",
37070
37153
  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",
37077
- category: "reasoning",
37078
- provider: "opencode-zen",
37079
- contextLength: 200000
37154
+ contextLength: 1e6
37080
37155
  },
37081
37156
  {
37082
37157
  id: "kimi-k2-thinking",
@@ -37087,7 +37162,45 @@ var OPENCODE_ZEN_MODELS = [
37087
37162
  contextLength: 262144
37088
37163
  }
37089
37164
  ];
37090
- var ALL_MODELS = [...OPENCODE_ZEN_MODELS, ...OPENROUTER_MODELS];
37165
+ var ALL_MODELS = [
37166
+ ...OPENCODE_ZEN_MODELS,
37167
+ ...OPENROUTER_MODELS,
37168
+ ...VERCEL_AI_GATEWAY_MODELS,
37169
+ ...CLOUDFLARE_AI_GATEWAY_MODELS,
37170
+ ...WORKERS_AI_MODELS
37171
+ ];
37172
+ function getProviderModels(provider) {
37173
+ switch (provider) {
37174
+ case "opencode-zen":
37175
+ return OPENCODE_ZEN_MODELS;
37176
+ case "openrouter":
37177
+ return OPENROUTER_MODELS;
37178
+ case "vercel-ai-gateway":
37179
+ return VERCEL_AI_GATEWAY_MODELS;
37180
+ case "cloudflare-ai-gateway":
37181
+ return CLOUDFLARE_AI_GATEWAY_MODELS;
37182
+ case "workers-ai":
37183
+ return WORKERS_AI_MODELS;
37184
+ case "custom":
37185
+ return [];
37186
+ }
37187
+ }
37188
+ function getProviderDisplayName(provider) {
37189
+ switch (provider) {
37190
+ case "opencode-zen":
37191
+ return "OpenCode Zen";
37192
+ case "openrouter":
37193
+ return "OpenRouter";
37194
+ case "vercel-ai-gateway":
37195
+ return "Vercel AI Gateway";
37196
+ case "cloudflare-ai-gateway":
37197
+ return "Cloudflare AI Gateway";
37198
+ case "workers-ai":
37199
+ return "Cloudflare Workers AI";
37200
+ case "custom":
37201
+ return "Custom";
37202
+ }
37203
+ }
37091
37204
  function isCustomModel(model) {
37092
37205
  return "baseUrl" in model;
37093
37206
  }
@@ -37458,10 +37571,18 @@ var CONFIG_FILE = join4(CONFIG_DIR, "config.json");
37458
37571
  var HISTORY_FILE = join4(CONFIG_DIR, "history.json");
37459
37572
  var KEYCHAIN_OPENROUTER = "openrouter-api-key";
37460
37573
  var KEYCHAIN_OPENCODE_ZEN = "opencode-zen-api-key";
37574
+ var KEYCHAIN_VERCEL_AI_GATEWAY = "vercel-ai-gateway-api-key";
37575
+ var KEYCHAIN_CLOUDFLARE_AI_GATEWAY = "cloudflare-ai-gateway-api-key";
37576
+ var KEYCHAIN_WORKERS_AI = "workers-ai-api-key";
37461
37577
  var DEFAULT_CONFIG = {
37462
37578
  provider: "opencode-zen",
37463
37579
  openrouterApiKey: "",
37464
37580
  opencodeZenApiKey: "",
37581
+ vercelAiGatewayApiKey: "",
37582
+ cloudflareAiGatewayApiKey: "",
37583
+ workersAiApiKey: "",
37584
+ cloudflareAccountId: "",
37585
+ cloudflareAiGatewayId: "default",
37465
37586
  defaultModel: "kimi-k2.6-free",
37466
37587
  safetyLevel: "moderate",
37467
37588
  dryRunByDefault: false,
@@ -37501,6 +37622,9 @@ function saveConfig(config) {
37501
37622
  if (isSecureStorageAvailable()) {
37502
37623
  configToSave.openrouterApiKey = "";
37503
37624
  configToSave.opencodeZenApiKey = "";
37625
+ configToSave.vercelAiGatewayApiKey = "";
37626
+ configToSave.cloudflareAiGatewayApiKey = "";
37627
+ configToSave.workersAiApiKey = "";
37504
37628
  }
37505
37629
  writeFileSync2(CONFIG_FILE, JSON.stringify(configToSave, null, 2));
37506
37630
  }
@@ -37513,28 +37637,83 @@ async function getApiKey(provider) {
37513
37637
  const envKey = process.env.OPENCODE_ZEN_API_KEY;
37514
37638
  if (envKey)
37515
37639
  return envKey;
37640
+ } else if (provider === "vercel-ai-gateway") {
37641
+ const envKey = process.env.AI_GATEWAY_API_KEY || process.env.VERCEL_AI_GATEWAY_API_KEY;
37642
+ if (envKey)
37643
+ return envKey;
37644
+ } else if (provider === "cloudflare-ai-gateway") {
37645
+ const envKey = process.env.CLOUDFLARE_AI_GATEWAY_API_KEY || process.env.CF_AIG_TOKEN;
37646
+ if (envKey)
37647
+ return envKey;
37648
+ } else if (provider === "workers-ai") {
37649
+ const envKey = process.env.CLOUDFLARE_API_TOKEN || process.env.CLOUDFLARE_API_KEY;
37650
+ if (envKey)
37651
+ return envKey;
37516
37652
  }
37517
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER : KEYCHAIN_OPENCODE_ZEN;
37653
+ const keychainKey = getKeychainKey(provider);
37518
37654
  const secureKey = await getSecret(keychainKey);
37519
37655
  if (secureKey)
37520
37656
  return secureKey;
37521
37657
  const config = loadConfig2();
37522
- return provider === "openrouter" ? config.openrouterApiKey : config.opencodeZenApiKey;
37658
+ switch (provider) {
37659
+ case "openrouter":
37660
+ return config.openrouterApiKey;
37661
+ case "opencode-zen":
37662
+ return config.opencodeZenApiKey;
37663
+ case "vercel-ai-gateway":
37664
+ return config.vercelAiGatewayApiKey || "";
37665
+ case "cloudflare-ai-gateway":
37666
+ return config.cloudflareAiGatewayApiKey || "";
37667
+ case "workers-ai":
37668
+ return config.workersAiApiKey || "";
37669
+ case "custom":
37670
+ return "";
37671
+ }
37523
37672
  }
37524
37673
  async function setApiKey(provider, key) {
37525
37674
  const config = loadConfig2();
37526
37675
  config.provider = provider;
37527
- const keychainKey = provider === "openrouter" ? KEYCHAIN_OPENROUTER : KEYCHAIN_OPENCODE_ZEN;
37676
+ const keychainKey = getKeychainKey(provider);
37528
37677
  const stored = await setSecret(keychainKey, key);
37529
37678
  if (!stored) {
37530
- if (provider === "openrouter") {
37531
- config.openrouterApiKey = key;
37532
- } else {
37533
- config.opencodeZenApiKey = key;
37679
+ switch (provider) {
37680
+ case "openrouter":
37681
+ config.openrouterApiKey = key;
37682
+ break;
37683
+ case "opencode-zen":
37684
+ config.opencodeZenApiKey = key;
37685
+ break;
37686
+ case "vercel-ai-gateway":
37687
+ config.vercelAiGatewayApiKey = key;
37688
+ break;
37689
+ case "cloudflare-ai-gateway":
37690
+ config.cloudflareAiGatewayApiKey = key;
37691
+ break;
37692
+ case "workers-ai":
37693
+ config.workersAiApiKey = key;
37694
+ break;
37695
+ case "custom":
37696
+ break;
37534
37697
  }
37535
37698
  }
37536
37699
  saveConfig(config);
37537
37700
  }
37701
+ function getKeychainKey(provider) {
37702
+ switch (provider) {
37703
+ case "openrouter":
37704
+ return KEYCHAIN_OPENROUTER;
37705
+ case "opencode-zen":
37706
+ return KEYCHAIN_OPENCODE_ZEN;
37707
+ case "vercel-ai-gateway":
37708
+ return KEYCHAIN_VERCEL_AI_GATEWAY;
37709
+ case "cloudflare-ai-gateway":
37710
+ return KEYCHAIN_CLOUDFLARE_AI_GATEWAY;
37711
+ case "workers-ai":
37712
+ return KEYCHAIN_WORKERS_AI;
37713
+ case "custom":
37714
+ return "custom-api-key";
37715
+ }
37716
+ }
37538
37717
  function loadHistory() {
37539
37718
  ensureConfigDir();
37540
37719
  if (!existsSync3(HISTORY_FILE)) {
@@ -76080,9 +76259,6 @@ function formatRepoContext(context2) {
76080
76259
 
76081
76260
  // src/lib/api.ts
76082
76261
  function getZenApiType(modelId) {
76083
- if (modelId === "minimax-m2.5-free") {
76084
- return "anthropic";
76085
- }
76086
76262
  if (modelId.startsWith("gpt-")) {
76087
76263
  return "openai-responses";
76088
76264
  }
@@ -76212,6 +76388,81 @@ async function callOpenRouter(apiKey, modelId, systemPrompt, userInput) {
76212
76388
  }
76213
76389
  return data.choices[0]?.message?.content?.trim() || "";
76214
76390
  }
76391
+ async function callOpenAICompatibleFetch(baseURL, apiKey, modelId, systemPrompt, userInput, headers = {}, includeAuthorization = true) {
76392
+ const requestHeaders = {
76393
+ "Content-Type": "application/json",
76394
+ ...headers
76395
+ };
76396
+ if (includeAuthorization) {
76397
+ requestHeaders.Authorization = `Bearer ${apiKey}`;
76398
+ }
76399
+ const response = await fetch(`${baseURL.replace(/\/$/, "")}/chat/completions`, {
76400
+ method: "POST",
76401
+ headers: requestHeaders,
76402
+ body: JSON.stringify({
76403
+ model: modelId,
76404
+ messages: [
76405
+ { role: "system", content: systemPrompt },
76406
+ { role: "user", content: userInput }
76407
+ ],
76408
+ max_tokens: 500,
76409
+ temperature: 0.1,
76410
+ stream: false
76411
+ })
76412
+ });
76413
+ if (!response.ok) {
76414
+ const errorText = await response.text();
76415
+ let errorMessage = `API request failed: ${response.status}`;
76416
+ try {
76417
+ const errorData = JSON.parse(errorText);
76418
+ if (errorData.error?.message) {
76419
+ errorMessage = errorData.error.message;
76420
+ } else if (errorData.errors?.[0]?.message) {
76421
+ errorMessage = errorData.errors[0].message;
76422
+ }
76423
+ } catch {}
76424
+ throw new Error(errorMessage);
76425
+ }
76426
+ const data = await response.json();
76427
+ if (data.error) {
76428
+ throw new Error(data.error.message);
76429
+ }
76430
+ if (data.errors?.[0]?.message) {
76431
+ throw new Error(data.errors[0].message);
76432
+ }
76433
+ const choices = data.choices || data.result?.choices;
76434
+ return choices?.[0]?.message?.content?.trim() || "";
76435
+ }
76436
+ function getCloudflareAccountId(config2) {
76437
+ return config2.cloudflareAccountId || process.env.CLOUDFLARE_ACCOUNT_ID || process.env.CF_ACCOUNT_ID || "";
76438
+ }
76439
+ function getCloudflareGatewayId(config2) {
76440
+ return config2.cloudflareAiGatewayId || process.env.CLOUDFLARE_AI_GATEWAY_ID || process.env.CF_AIG_GATEWAY_ID || "default";
76441
+ }
76442
+ async function callGatewayProvider(provider, apiKey, modelId, systemPrompt, userInput) {
76443
+ const config2 = loadConfig2();
76444
+ switch (provider) {
76445
+ case "vercel-ai-gateway":
76446
+ return await callOpenAICompatibleFetch("https://ai-gateway.vercel.sh/v1", apiKey, modelId, systemPrompt, userInput);
76447
+ case "cloudflare-ai-gateway": {
76448
+ const accountId = getCloudflareAccountId(config2);
76449
+ if (!accountId) {
76450
+ throw new Error("Cloudflare account ID is required. Set cloudflareAccountId in config or CLOUDFLARE_ACCOUNT_ID.");
76451
+ }
76452
+ const gatewayId = getCloudflareGatewayId(config2);
76453
+ return await callOpenAICompatibleFetch(`https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/compat`, apiKey, modelId, systemPrompt, userInput, { "cf-aig-authorization": `Bearer ${apiKey}` }, false);
76454
+ }
76455
+ case "workers-ai": {
76456
+ const accountId = getCloudflareAccountId(config2);
76457
+ if (!accountId) {
76458
+ throw new Error("Cloudflare account ID is required. Set cloudflareAccountId in config or CLOUDFLARE_ACCOUNT_ID.");
76459
+ }
76460
+ return await callOpenAICompatibleFetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`, apiKey, modelId, systemPrompt, userInput);
76461
+ }
76462
+ default:
76463
+ throw new Error(`Unsupported gateway provider: ${provider}`);
76464
+ }
76465
+ }
76215
76466
  var DEBUG_API = process.env.DEBUG_API === "1";
76216
76467
  async function generateZenText(model, systemPrompt, userInput) {
76217
76468
  const { text: text2 } = await generateText({
@@ -76338,6 +76589,8 @@ async function translateToCommand(apiKey, model, userInput, cwd, history = [], r
76338
76589
  rawCommand = await callCustomModel(model, systemPrompt, userInput);
76339
76590
  } else if (model.provider === "openrouter") {
76340
76591
  rawCommand = await callOpenRouter(apiKey, model.id, systemPrompt, userInput);
76592
+ } else if (model.provider === "vercel-ai-gateway" || model.provider === "cloudflare-ai-gateway" || model.provider === "workers-ai") {
76593
+ rawCommand = await callGatewayProvider(model.provider, apiKey, model.id, systemPrompt, userInput);
76341
76594
  } else {
76342
76595
  const apiType = getZenApiType(model.id);
76343
76596
  switch (apiType) {
@@ -76570,6 +76823,22 @@ var statusBarText;
76570
76823
  var chatScrollBox;
76571
76824
  var inputField;
76572
76825
  var inputContainer;
76826
+ function getApiKeyUrl(provider) {
76827
+ switch (provider) {
76828
+ case "opencode-zen":
76829
+ return "https://opencode.ai/auth";
76830
+ case "openrouter":
76831
+ return "https://openrouter.ai/keys";
76832
+ case "vercel-ai-gateway":
76833
+ return "https://vercel.com/docs/ai-gateway";
76834
+ case "cloudflare-ai-gateway":
76835
+ return "https://developers.cloudflare.com/ai-gateway/";
76836
+ case "workers-ai":
76837
+ return "https://dash.cloudflare.com/profile/api-tokens";
76838
+ case "custom":
76839
+ return "";
76840
+ }
76841
+ }
76573
76842
  var inputHintText;
76574
76843
  var helpBarText;
76575
76844
  var modelSelector = null;
@@ -76640,12 +76909,27 @@ async function showProviderSetup() {
76640
76909
  name: "OpenRouter",
76641
76910
  description: "Access to many models from various providers",
76642
76911
  value: "openrouter"
76912
+ },
76913
+ {
76914
+ name: "Vercel AI Gateway",
76915
+ description: "Unified model gateway using AI_GATEWAY_API_KEY",
76916
+ value: "vercel-ai-gateway"
76917
+ },
76918
+ {
76919
+ name: "Cloudflare AI Gateway",
76920
+ description: "Cloudflare gateway for provider and Workers AI models",
76921
+ value: "cloudflare-ai-gateway"
76922
+ },
76923
+ {
76924
+ name: "Cloudflare Workers AI",
76925
+ description: "Cloudflare-hosted models via Workers AI",
76926
+ value: "workers-ai"
76643
76927
  }
76644
76928
  ];
76645
76929
  providerSelector = new SelectRenderable(renderer, {
76646
76930
  id: "provider-select",
76647
76931
  width: 60,
76648
- height: 6,
76932
+ height: 10,
76649
76933
  options,
76650
76934
  backgroundColor: "#1e293b",
76651
76935
  focusedBackgroundColor: "#1e293b",
@@ -76692,11 +76976,11 @@ async function showApiKeyInput(provider) {
76692
76976
  renderer.root.add(container);
76693
76977
  const title = new TextRenderable(renderer, {
76694
76978
  id: "apikey-title",
76695
- content: t`${bold(fg("#60a5fa")(`${provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter"} Setup`))}`,
76979
+ content: t`${bold(fg("#60a5fa")(`${getProviderDisplayName(provider)} Setup`))}`,
76696
76980
  marginBottom: 1
76697
76981
  });
76698
76982
  container.add(title);
76699
- const url2 = provider === "opencode-zen" ? "https://opencode.ai/auth" : "https://openrouter.ai/keys";
76983
+ const url2 = getApiKeyUrl(provider);
76700
76984
  const instructions = new TextRenderable(renderer, {
76701
76985
  id: "apikey-instructions",
76702
76986
  content: t`Get your API key from: ${fg("#22c55e")(url2)}
@@ -76708,7 +76992,7 @@ Enter your API key below:`,
76708
76992
  const input = new InputRenderable(renderer, {
76709
76993
  id: "api-key-input",
76710
76994
  width: 70,
76711
- placeholder: provider === "opencode-zen" ? "zen_..." : "sk-or-v1-...",
76995
+ placeholder: provider === "openrouter" ? "sk-or-v1-..." : "API key or token",
76712
76996
  backgroundColor: "#1e293b",
76713
76997
  focusedBackgroundColor: "#334155",
76714
76998
  textColor: "#f8fafc",
@@ -76734,11 +77018,8 @@ ${fg("#64748b")("Press Enter to save | Ctrl+C to exit")}`,
76734
77018
  input.on(InputRenderableEvents.ENTER, (value) => {
76735
77019
  if (value.trim()) {
76736
77020
  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
- }
77021
+ const providerModels = getProviderModels(provider);
77022
+ currentModel = providerModels.find((m2) => !m2.disabled) || providerModels[0] || OPENCODE_ZEN_MODELS[0];
76742
77023
  config2.defaultModel = currentModel.id;
76743
77024
  saveConfig(config2);
76744
77025
  renderer.root.remove("apikey-container");
@@ -76856,7 +77137,7 @@ function createMainUI() {
76856
77137
  }
76857
77138
  function getStatusBarContent() {
76858
77139
  const theme = getTheme();
76859
- const providerName = config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
77140
+ const providerName = getProviderDisplayName(config2.provider);
76860
77141
  const safeModeIndicator = dryRunMode ? fg(theme.colors.warning)("[DRY RUN]") : "";
76861
77142
  const safetyLevelColor = config2.safetyLevel === "strict" ? theme.colors.warning : config2.safetyLevel === "relaxed" ? theme.colors.error : theme.colors.success;
76862
77143
  const safetyIndicator = fg(safetyLevelColor)(`[${config2.safetyLevel}]`);
@@ -76875,7 +77156,7 @@ function getInputHintContent() {
76875
77156
  return t`${fg(theme.colors.primary)("Enter")} ${fg(theme.colors.textMuted)("to send")}`;
76876
77157
  }
76877
77158
  function getWelcomeMessage() {
76878
- const providerName = config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
77159
+ const providerName = getProviderDisplayName(config2.provider);
76879
77160
  return `Ready. Using ${providerName}.
76880
77161
  Type what you want to do, or press Ctrl+X P for command palette.`;
76881
77162
  }
@@ -77377,7 +77658,7 @@ Tips:
77377
77658
  }
77378
77659
  async function showConfig() {
77379
77660
  const theme = getTheme();
77380
- const providerName = config2.provider === "opencode-zen" ? "OpenCode Zen" : config2.provider === "openrouter" ? "OpenRouter" : "Custom";
77661
+ const providerName = getProviderDisplayName(config2.provider);
77381
77662
  const apiKey = await getApiKey(config2.provider);
77382
77663
  const apiKeyStatus = apiKey ? "configured" : "not set";
77383
77664
  const isCustom = isCustomModel(currentModel);
@@ -77422,7 +77703,7 @@ async function switchProvider() {
77422
77703
  left: 2,
77423
77704
  top: 4,
77424
77705
  width: 65,
77425
- height: 12,
77706
+ height: 15,
77426
77707
  backgroundColor: "#1e293b",
77427
77708
  border: true,
77428
77709
  borderColor: "#60a5fa",
@@ -77433,24 +77714,25 @@ async function switchProvider() {
77433
77714
  padding: 1
77434
77715
  });
77435
77716
  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
- }
77717
+ const providers = [
77718
+ { provider: "opencode-zen", description: "Curated models optimized for coding. Has free models!" },
77719
+ { provider: "openrouter", description: "Access to many models from various providers" },
77720
+ { provider: "vercel-ai-gateway", description: "Unified model gateway using AI_GATEWAY_API_KEY" },
77721
+ { provider: "cloudflare-ai-gateway", description: "Cloudflare gateway for provider and Workers AI models" },
77722
+ { provider: "workers-ai", description: "Cloudflare-hosted models via Workers AI" }
77449
77723
  ];
77724
+ const options = await Promise.all(providers.map(async ({ provider, description }) => {
77725
+ const key = await getApiKey(provider);
77726
+ return {
77727
+ name: `${getProviderDisplayName(provider)}${key ? " (configured)" : ""}`,
77728
+ description,
77729
+ value: provider
77730
+ };
77731
+ }));
77450
77732
  const selector = new SelectRenderable(renderer, {
77451
77733
  id: "provider-switch-select",
77452
77734
  width: "100%",
77453
- height: 6,
77735
+ height: 9,
77454
77736
  options,
77455
77737
  backgroundColor: "transparent",
77456
77738
  focusedBackgroundColor: "transparent",
@@ -77472,13 +77754,13 @@ async function switchProvider() {
77472
77754
  const existingKey = await getApiKey(newProvider);
77473
77755
  if (existingKey) {
77474
77756
  config2.provider = newProvider;
77475
- const models = newProvider === "opencode-zen" ? OPENCODE_ZEN_MODELS : OPENROUTER_MODELS;
77757
+ const models = getProviderModels(newProvider);
77476
77758
  currentModel = models.find((m2) => m2.id === config2.defaultModel) || models[0];
77477
77759
  config2.defaultModel = currentModel.id;
77478
77760
  saveConfig(config2);
77479
77761
  statusBarText.content = getStatusBarContent();
77480
77762
  closeSelector();
77481
- const providerName = newProvider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
77763
+ const providerName = getProviderDisplayName(newProvider);
77482
77764
  addSystemMessage(`Switched to ${providerName}. Model: ${currentModel.name}`);
77483
77765
  } else {
77484
77766
  closeSelector();
@@ -77510,13 +77792,13 @@ function showModelSelector() {
77510
77792
  border: true,
77511
77793
  borderColor: "#60a5fa",
77512
77794
  borderStyle: "single",
77513
- title: `Select Model (${config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter"})`,
77795
+ title: `Select Model (${getProviderDisplayName(config2.provider)})`,
77514
77796
  titleAlignment: "center",
77515
77797
  zIndex: 100,
77516
77798
  padding: 1
77517
77799
  });
77518
77800
  renderer.root.add(container);
77519
- const allModels = config2.provider === "opencode-zen" ? OPENCODE_ZEN_MODELS : OPENROUTER_MODELS;
77801
+ const allModels = getProviderModels(config2.provider);
77520
77802
  const availableModels = allModels.filter((m2) => !m2.disabled).sort((a, b2) => a.name.localeCompare(b2.name));
77521
77803
  const customModels = getCustomModels().sort((a, b2) => a.name.localeCompare(b2.name));
77522
77804
  const options = [
@@ -77650,7 +77932,7 @@ function getCommandPaletteOptions() {
77650
77932
  },
77651
77933
  {
77652
77934
  name: "Switch Provider",
77653
- description: `Current: ${config2.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter"}`,
77935
+ description: `Current: ${getProviderDisplayName(config2.provider)}`,
77654
77936
  key: "s",
77655
77937
  chord: "s",
77656
77938
  action: () => switchProvider()