vertex-ai-proxy 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.ts CHANGED
@@ -9,6 +9,8 @@
9
9
  * vertex-ai-proxy restart Restart the daemon
10
10
  * vertex-ai-proxy status Show proxy status
11
11
  * vertex-ai-proxy logs Show proxy logs
12
+ * vertex-ai-proxy test Run proxy test suite
13
+ * vertex-ai-proxy update Update from npm
12
14
  * vertex-ai-proxy models List all available models
13
15
  * vertex-ai-proxy models fetch Fetch/verify models from Vertex AI
14
16
  * vertex-ai-proxy models info <model> Show detailed model info
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
package/dist/cli.js CHANGED
@@ -9,6 +9,8 @@
9
9
  * vertex-ai-proxy restart Restart the daemon
10
10
  * vertex-ai-proxy status Show proxy status
11
11
  * vertex-ai-proxy logs Show proxy logs
12
+ * vertex-ai-proxy test Run proxy test suite
13
+ * vertex-ai-proxy update Update from npm
12
14
  * vertex-ai-proxy models List all available models
13
15
  * vertex-ai-proxy models fetch Fetch/verify models from Vertex AI
14
16
  * vertex-ai-proxy models info <model> Show detailed model info
@@ -32,7 +34,7 @@ import * as path from 'path';
32
34
  import * as os from 'os';
33
35
  import * as yaml from 'js-yaml';
34
36
  import * as readline from 'readline';
35
- const VERSION = '1.1.0';
37
+ const VERSION = '1.3.0';
36
38
  const CONFIG_DIR = path.join(os.homedir(), '.vertex-proxy');
37
39
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.yaml');
38
40
  const DATA_DIR = path.join(os.homedir(), '.vertex_proxy');
@@ -40,44 +42,32 @@ const PID_FILE = path.join(DATA_DIR, 'proxy.pid');
40
42
  const LOG_FILE = path.join(DATA_DIR, 'proxy.log');
41
43
  const STATS_FILE = path.join(DATA_DIR, 'stats.json');
42
44
  // ============================================================================
43
- // Model Catalog
45
+ // Model Catalog (Updated with correct token counts from Vertex AI docs)
44
46
  // ============================================================================
45
47
  const MODEL_CATALOG = {
46
- // Claude Models
48
+ // Claude Models (all: 200k input, 64k output)
47
49
  'claude-opus-4-5@20251101': {
48
50
  id: 'claude-opus-4-5@20251101',
49
51
  name: 'Claude Opus 4.5',
50
52
  provider: 'anthropic',
51
53
  description: 'Most capable Claude. Best for complex reasoning.',
52
54
  contextWindow: 200000,
53
- maxTokens: 8192,
54
- inputPrice: 15,
55
- outputPrice: 75,
56
- regions: ['us-east5', 'europe-west1'],
57
- capabilities: ['text', 'vision', 'tools', 'thinking']
58
- },
59
- 'claude-opus-4-1@20250410': {
60
- id: 'claude-opus-4-1@20250410',
61
- name: 'Claude Opus 4.1',
62
- provider: 'anthropic',
63
- description: 'Previous Opus generation.',
64
- contextWindow: 200000,
65
- maxTokens: 8192,
55
+ maxTokens: 64000,
66
56
  inputPrice: 15,
67
57
  outputPrice: 75,
68
- regions: ['us-east5', 'europe-west1'],
69
- capabilities: ['text', 'vision', 'tools']
58
+ regions: ['us-east5', 'europe-west1', 'asia-southeast1', 'global'],
59
+ capabilities: ['text', 'vision', 'tools', 'computer-use']
70
60
  },
71
- 'claude-sonnet-4-5@20250514': {
72
- id: 'claude-sonnet-4-5@20250514',
61
+ 'claude-sonnet-4-5@20250929': {
62
+ id: 'claude-sonnet-4-5@20250929',
73
63
  name: 'Claude Sonnet 4.5',
74
64
  provider: 'anthropic',
75
- description: 'Balanced performance and cost.',
65
+ description: 'Balanced performance and cost. Great for coding.',
76
66
  contextWindow: 200000,
77
- maxTokens: 8192,
67
+ maxTokens: 64000,
78
68
  inputPrice: 3,
79
69
  outputPrice: 15,
80
- regions: ['us-east5', 'europe-west1'],
70
+ regions: ['us-east5', 'europe-west1', 'asia-southeast1', 'global'],
81
71
  capabilities: ['text', 'vision', 'tools', 'thinking']
82
72
  },
83
73
  'claude-sonnet-4@20250514': {
@@ -86,37 +76,61 @@ const MODEL_CATALOG = {
86
76
  provider: 'anthropic',
87
77
  description: 'Previous Sonnet generation.',
88
78
  contextWindow: 200000,
89
- maxTokens: 8192,
79
+ maxTokens: 64000,
90
80
  inputPrice: 3,
91
81
  outputPrice: 15,
92
- regions: ['us-east5', 'europe-west1'],
93
- capabilities: ['text', 'vision', 'tools']
82
+ regions: ['us-east5', 'europe-west1', 'asia-east1', 'global'],
83
+ capabilities: ['text', 'vision', 'tools', 'thinking']
94
84
  },
95
85
  'claude-haiku-4-5@20251001': {
96
86
  id: 'claude-haiku-4-5@20251001',
97
87
  name: 'Claude Haiku 4.5',
98
88
  provider: 'anthropic',
99
- description: 'Fastest and most affordable.',
89
+ description: 'Fastest and most affordable. Great for coding.',
100
90
  contextWindow: 200000,
101
- maxTokens: 8192,
102
- inputPrice: 0.25,
103
- outputPrice: 1.25,
104
- regions: ['us-east5', 'europe-west1'],
91
+ maxTokens: 64000,
92
+ inputPrice: 0.80,
93
+ outputPrice: 4,
94
+ regions: ['us-east5', 'europe-west1', 'asia-east1', 'global'],
95
+ capabilities: ['text', 'vision', 'tools', 'thinking']
96
+ },
97
+ 'claude-opus-4@20250410': {
98
+ id: 'claude-opus-4@20250410',
99
+ name: 'Claude Opus 4',
100
+ provider: 'anthropic',
101
+ description: 'Previous Opus generation.',
102
+ contextWindow: 200000,
103
+ maxTokens: 64000,
104
+ inputPrice: 15,
105
+ outputPrice: 75,
106
+ regions: ['us-east5', 'europe-west1', 'asia-southeast1', 'global'],
105
107
  capabilities: ['text', 'vision', 'tools']
106
108
  },
107
109
  // Gemini Models
108
- 'gemini-3-pro': {
109
- id: 'gemini-3-pro',
110
+ 'gemini-3-pro-preview': {
111
+ id: 'gemini-3-pro-preview',
110
112
  name: 'Gemini 3 Pro',
111
113
  provider: 'google',
112
114
  description: 'Latest Gemini with multimodal.',
113
- contextWindow: 1000000,
114
- maxTokens: 8192,
115
+ contextWindow: 1048576,
116
+ maxTokens: 65536,
115
117
  inputPrice: 2.5,
116
118
  outputPrice: 15,
117
- regions: ['us-central1', 'europe-west4'],
119
+ regions: ['global'],
118
120
  capabilities: ['text', 'vision', 'audio', 'video', 'tools']
119
121
  },
122
+ 'gemini-3-pro-image-preview': {
123
+ id: 'gemini-3-pro-image-preview',
124
+ name: 'Gemini 3 Pro Image',
125
+ provider: 'google',
126
+ description: 'Native image generation.',
127
+ contextWindow: 65536,
128
+ maxTokens: 32768,
129
+ inputPrice: 2.5,
130
+ outputPrice: 15,
131
+ regions: ['global'],
132
+ capabilities: ['text', 'vision', 'image-generation']
133
+ },
120
134
  'gemini-2.5-pro': {
121
135
  id: 'gemini-2.5-pro',
122
136
  name: 'Gemini 2.5 Pro',
@@ -206,7 +220,7 @@ function loadConfig() {
206
220
  google_region: 'us-central1',
207
221
  model_aliases: {},
208
222
  fallback_chains: {},
209
- default_model: 'claude-sonnet-4-5@20250514',
223
+ default_model: 'claude-sonnet-4-5@20250929',
210
224
  enabled_models: [],
211
225
  auto_truncate: true,
212
226
  reserve_output_tokens: 4096
@@ -291,7 +305,8 @@ function formatPrice(input, output) {
291
305
  function formatCapabilities(caps) {
292
306
  const icons = {
293
307
  'text': '📝', 'vision': '👁️', 'audio': '🎵', 'video': '🎬',
294
- 'tools': '🔧', 'thinking': '🧠', 'image-generation': '🎨', 'image-edit': '✏️'
308
+ 'tools': '🔧', 'thinking': '🧠', 'image-generation': '🎨', 'image-edit': '✏️',
309
+ 'computer-use': '🖥️'
295
310
  };
296
311
  return caps.map(c => icons[c] || c).join(' ');
297
312
  }
@@ -549,12 +564,216 @@ async function showLogs(options) {
549
564
  }
550
565
  }
551
566
  // ============================================================================
567
+ // Update Command
568
+ // ============================================================================
569
+ async function runUpdate(options) {
570
+ console.log(chalk.blue.bold('\n📦 Updating Vertex AI Proxy\n'));
571
+ const spinner = ora('Checking for updates...').start();
572
+ try {
573
+ // Check current version
574
+ const currentVersion = VERSION;
575
+ // Check npm for latest version
576
+ let latestVersion;
577
+ try {
578
+ const npmInfo = execSync('npm view vertex-ai-proxy version 2>/dev/null', { encoding: 'utf8' }).trim();
579
+ latestVersion = npmInfo;
580
+ }
581
+ catch (e) {
582
+ spinner.fail('Failed to check npm registry');
583
+ console.log(chalk.gray(' Ensure you have npm access'));
584
+ return;
585
+ }
586
+ if (currentVersion === latestVersion) {
587
+ spinner.succeed(`Already at latest version (${currentVersion})`);
588
+ return;
589
+ }
590
+ spinner.text = `Updating ${currentVersion} → ${latestVersion}...`;
591
+ // Stop daemon if running
592
+ const pid = getPid();
593
+ if (pid && isRunning(pid)) {
594
+ spinner.text = 'Stopping daemon...';
595
+ await stopDaemon();
596
+ }
597
+ // Run npm update
598
+ spinner.text = 'Installing update...';
599
+ const installCmd = options.global
600
+ ? 'npm install -g vertex-ai-proxy@latest'
601
+ : 'npm install vertex-ai-proxy@latest';
602
+ execSync(installCmd, { stdio: 'pipe' });
603
+ spinner.succeed(`Updated to version ${latestVersion}`);
604
+ // Restart if was running
605
+ if (pid && isRunning(pid)) {
606
+ console.log(chalk.gray(' Restarting daemon...'));
607
+ await startDaemon({});
608
+ }
609
+ console.log();
610
+ console.log(chalk.gray('Tip: Run `vertex-ai-proxy status` to verify'));
611
+ }
612
+ catch (e) {
613
+ spinner.fail(`Update failed: ${e.message}`);
614
+ console.log(chalk.gray('\nTry manually: npm install -g vertex-ai-proxy@latest'));
615
+ }
616
+ }
617
+ // ============================================================================
618
+ // Test Command
619
+ // ============================================================================
620
+ async function runTest(options) {
621
+ console.log(chalk.blue.bold('\n🧪 Running Proxy Tests\n'));
622
+ const stats = loadStats();
623
+ const port = options.port || stats?.port || 8001;
624
+ const proxyUrl = `http://localhost:${port}`;
625
+ // Check if proxy is running
626
+ const pid = getPid();
627
+ if (!pid || !isRunning(pid)) {
628
+ console.log(chalk.yellow('⚠️ Proxy not running. Starting...'));
629
+ await startDaemon({ port: port.toString() });
630
+ // Wait for startup
631
+ await new Promise(resolve => setTimeout(resolve, 2000));
632
+ }
633
+ // Define tests
634
+ const tests = [
635
+ { name: 'Health endpoint', test: testHealth },
636
+ { name: 'Models endpoint', test: testModels },
637
+ { name: 'Gemini text', test: testGeminiText },
638
+ { name: 'Gemini vision', test: testGeminiVision },
639
+ ];
640
+ if (options.all) {
641
+ tests.push({ name: 'Claude text', test: testClaudeText }, { name: 'Imagen generation', test: testImagen }, { name: 'Gemini native image', test: testGeminiImage });
642
+ }
643
+ let passed = 0;
644
+ let failed = 0;
645
+ for (const { name, test } of tests) {
646
+ const spinner = ora(name).start();
647
+ try {
648
+ const result = await test(proxyUrl);
649
+ spinner.succeed(`${name}: ${result}`);
650
+ passed++;
651
+ }
652
+ catch (e) {
653
+ spinner.fail(`${name}: ${e.message}`);
654
+ failed++;
655
+ }
656
+ }
657
+ console.log();
658
+ console.log(chalk.gray('─'.repeat(40)));
659
+ console.log(`Results: ${chalk.green(`${passed} passed`)}, ${failed > 0 ? chalk.red(`${failed} failed`) : '0 failed'}`);
660
+ if (!options.all) {
661
+ console.log(chalk.gray('\nTip: vertex-ai-proxy test --all (include Claude, Imagen)'));
662
+ }
663
+ }
664
+ async function testHealth(url) {
665
+ const response = await fetch(`${url}/health`);
666
+ if (!response.ok)
667
+ throw new Error(`HTTP ${response.status}`);
668
+ const data = await response.json();
669
+ return `uptime ${data.uptime}s`;
670
+ }
671
+ async function testModels(url) {
672
+ const response = await fetch(`${url}/v1/models`);
673
+ if (!response.ok)
674
+ throw new Error(`HTTP ${response.status}`);
675
+ const data = await response.json();
676
+ return `${data.data?.length || 0} models`;
677
+ }
678
+ async function testGeminiText(url) {
679
+ const response = await fetch(`${url}/v1/chat/completions`, {
680
+ method: 'POST',
681
+ headers: { 'Content-Type': 'application/json' },
682
+ body: JSON.stringify({
683
+ model: 'gemini-2.5-flash',
684
+ messages: [{ role: 'user', content: 'Say "ok" and nothing else' }],
685
+ max_tokens: 10
686
+ })
687
+ });
688
+ if (!response.ok)
689
+ throw new Error(`HTTP ${response.status}`);
690
+ const data = await response.json();
691
+ const text = data.choices?.[0]?.message?.content || '';
692
+ return `"${text.slice(0, 20)}"`;
693
+ }
694
+ async function testGeminiVision(url) {
695
+ const response = await fetch(`${url}/v1/chat/completions`, {
696
+ method: 'POST',
697
+ headers: { 'Content-Type': 'application/json' },
698
+ body: JSON.stringify({
699
+ model: 'gemini-3-pro-preview',
700
+ messages: [{
701
+ role: 'user',
702
+ content: [
703
+ { type: 'text', text: 'What logo? One word.' },
704
+ { type: 'image_url', image_url: { url: 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png' } }
705
+ ]
706
+ }],
707
+ max_tokens: 100
708
+ })
709
+ });
710
+ if (!response.ok)
711
+ throw new Error(`HTTP ${response.status}`);
712
+ const data = await response.json();
713
+ const text = data.choices?.[0]?.message?.content || '';
714
+ return `"${text.slice(0, 20)}"`;
715
+ }
716
+ async function testClaudeText(url) {
717
+ const response = await fetch(`${url}/v1/chat/completions`, {
718
+ method: 'POST',
719
+ headers: { 'Content-Type': 'application/json' },
720
+ body: JSON.stringify({
721
+ model: 'claude-haiku-4-5@20251001',
722
+ messages: [{ role: 'user', content: 'Say "ok"' }],
723
+ max_tokens: 10
724
+ })
725
+ });
726
+ if (!response.ok)
727
+ throw new Error(`HTTP ${response.status}`);
728
+ const data = await response.json();
729
+ const text = data.choices?.[0]?.message?.content || '';
730
+ return `"${text.slice(0, 20)}"`;
731
+ }
732
+ async function testImagen(url) {
733
+ const response = await fetch(`${url}/v1/images/generations`, {
734
+ method: 'POST',
735
+ headers: { 'Content-Type': 'application/json' },
736
+ body: JSON.stringify({
737
+ model: 'imagen-4.0-generate-001',
738
+ prompt: 'red circle',
739
+ n: 1
740
+ })
741
+ });
742
+ if (!response.ok)
743
+ throw new Error(`HTTP ${response.status}`);
744
+ const data = await response.json();
745
+ const size = data.data?.[0]?.b64_json?.length || 0;
746
+ if (size === 0)
747
+ throw new Error('No image returned');
748
+ return `${Math.round(size / 1024)}KB`;
749
+ }
750
+ async function testGeminiImage(url) {
751
+ const response = await fetch(`${url}/v1/chat/completions`, {
752
+ method: 'POST',
753
+ headers: { 'Content-Type': 'application/json' },
754
+ body: JSON.stringify({
755
+ model: 'gemini-3-pro-image-preview',
756
+ messages: [{ role: 'user', content: 'Draw a blue square' }],
757
+ max_tokens: 8000
758
+ })
759
+ });
760
+ if (!response.ok)
761
+ throw new Error(`HTTP ${response.status}`);
762
+ const data = await response.json();
763
+ const size = data.images?.[0]?.b64_json?.length || 0;
764
+ if (size === 0) {
765
+ const text = data.choices?.[0]?.message?.content || '';
766
+ return `text only: "${text.slice(0, 20)}"`;
767
+ }
768
+ return `${Math.round(size / 1024)}KB`;
769
+ }
770
+ // ============================================================================
552
771
  // Commands
553
772
  // ============================================================================
554
773
  const program = new Command();
555
774
  program
556
775
  .name('vertex-ai-proxy')
557
- .description('Proxy server for Vertex AI models with OpenClaw support')
776
+ .description('Proxy server for Vertex AI models with OpenAI-compatible API')
558
777
  .version(VERSION);
559
778
  // --- Daemon management commands ---
560
779
  program.command('start')
@@ -583,6 +802,16 @@ program.command('logs')
583
802
  .option('-f, --follow', 'Follow log output (tail -f style)')
584
803
  .option('-n, --lines <number>', 'Number of lines to show', '50')
585
804
  .action(showLogs);
805
+ // --- New commands ---
806
+ program.command('update')
807
+ .description('Update vertex-ai-proxy from npm')
808
+ .option('-g, --global', 'Update global installation')
809
+ .action(runUpdate);
810
+ program.command('test')
811
+ .description('Run proxy test suite')
812
+ .option('-p, --port <port>', 'Proxy port')
813
+ .option('-a, --all', 'Run all tests including Claude and Imagen')
814
+ .action(runTest);
586
815
  // --- models command ---
587
816
  const modelsCmd = program.command('models').description('List and manage models');
588
817
  modelsCmd
@@ -605,6 +834,80 @@ modelsCmd.command('enable <model>')
605
834
  modelsCmd.command('disable <model>')
606
835
  .description('Disable a model')
607
836
  .action(disableModel);
837
+ modelsCmd.command("discover")
838
+ .description("Probe Vertex AI to discover available Claude models per region")
839
+ .action(async () => {
840
+ console.log(chalk.blue.bold("\n🔍 Discovering Available Claude Models\n"));
841
+ const config = loadConfig();
842
+ if (!config.project_id) {
843
+ console.log(chalk.red("No project ID. Run: vertex-ai-proxy config set"));
844
+ return;
845
+ }
846
+ const { GoogleAuth } = await import("google-auth-library");
847
+ const auth = new GoogleAuth({ scopes: "https://www.googleapis.com/auth/cloud-platform" });
848
+ const client = await auth.getClient();
849
+ const tokenResponse = await client.getAccessToken();
850
+ const accessToken = tokenResponse.token;
851
+ const REGIONS = ["us-east5", "europe-west1", "asia-southeast1", "asia-east1"];
852
+ const CLAUDE_MODELS = [
853
+ "claude-opus-4-5@20251101",
854
+ "claude-sonnet-4-5@20250929",
855
+ "claude-sonnet-4@20250514",
856
+ "claude-haiku-4-5@20251001",
857
+ "claude-3-haiku@20240307",
858
+ "claude-3-5-sonnet@20240620",
859
+ "claude-3-5-sonnet-v2@20241022",
860
+ ];
861
+ const results = {};
862
+ for (const modelId of CLAUDE_MODELS) {
863
+ results[modelId] = [];
864
+ process.stdout.write(chalk.cyan(` ${modelId}:`));
865
+ for (const region of REGIONS) {
866
+ const url = `https://${region}-aiplatform.googleapis.com/v1/projects/${config.project_id}/locations/${region}/publishers/anthropic/models/${modelId}:rawPredict`;
867
+ try {
868
+ const response = await fetch(url, {
869
+ method: "POST",
870
+ headers: {
871
+ "Authorization": `Bearer ${accessToken}`,
872
+ "Content-Type": "application/json"
873
+ },
874
+ body: JSON.stringify({
875
+ anthropic_version: "vertex-2023-10-16",
876
+ max_tokens: 1,
877
+ messages: [{ role: "user", content: "hi" }]
878
+ })
879
+ });
880
+ if (response.ok) {
881
+ results[modelId].push(region);
882
+ process.stdout.write(chalk.green(` ${region}✓`));
883
+ }
884
+ else {
885
+ process.stdout.write(chalk.gray(` ${region}✗`));
886
+ }
887
+ }
888
+ catch (e) {
889
+ process.stdout.write(chalk.red(` ${region}!`));
890
+ }
891
+ }
892
+ console.log();
893
+ }
894
+ // Save to cache
895
+ ensureDataDir();
896
+ const cacheFile = path.join(DATA_DIR, "model_regions.json");
897
+ fs.writeFileSync(cacheFile, JSON.stringify({ updated: Date.now(), models: results }, null, 2));
898
+ console.log(chalk.green(`\n✓ Saved to ${cacheFile}`));
899
+ // Summary
900
+ console.log(chalk.yellow.bold("\n📊 Available Models:\n"));
901
+ for (const [modelId, regions] of Object.entries(results)) {
902
+ if (regions.length > 0) {
903
+ console.log(` ${chalk.green("✓")} ${modelId}: ${regions.join(", ")}`);
904
+ }
905
+ else {
906
+ console.log(` ${chalk.red("✗")} ${modelId}: not available`);
907
+ }
908
+ }
909
+ console.log(chalk.gray("\nNote: Enable Claude at https://console.cloud.google.com/vertex-ai/model-garden"));
910
+ });
608
911
  modelsCmd.action(() => listModels({}));
609
912
  // --- config command ---
610
913
  const configCmd = program.command('config').description('Manage configuration');
@@ -693,6 +996,7 @@ async function listModels(options) {
693
996
  console.log(` ${chalk.gray(model.name)} - ${model.description}`);
694
997
  if (options.all) {
695
998
  console.log(` ${chalk.cyan('Context:')} ${(model.contextWindow / 1000).toFixed(0)}K`);
999
+ console.log(` ${chalk.cyan('Max out:')} ${(model.maxTokens / 1000).toFixed(0)}K`);
696
1000
  console.log(` ${chalk.cyan('Price:')} ${formatPrice(model.inputPrice, model.outputPrice)} /1M tok`);
697
1001
  console.log(` ${chalk.cyan('Regions:')} ${model.regions.join(', ')}`);
698
1002
  console.log(` ${chalk.cyan('Caps:')} ${formatCapabilities(model.capabilities)}`);
@@ -764,7 +1068,7 @@ async function showModelInfo(modelId) {
764
1068
  console.log(`${chalk.cyan('Description:')} ${model.description}`);
765
1069
  console.log();
766
1070
  console.log(`${chalk.cyan('Context:')} ${(model.contextWindow / 1000).toFixed(0)}K tokens`);
767
- console.log(`${chalk.cyan('Max Output:')} ${model.maxTokens} tokens`);
1071
+ console.log(`${chalk.cyan('Max Output:')} ${(model.maxTokens / 1000).toFixed(0)}K tokens`);
768
1072
  console.log(`${chalk.cyan('Price:')} $${model.inputPrice} in / $${model.outputPrice} out (per 1M)`);
769
1073
  console.log();
770
1074
  console.log(`${chalk.cyan('Regions:')} ${model.regions.join(', ')}`);
@@ -860,25 +1164,25 @@ async function interactiveConfig() {
860
1164
  console.log(chalk.yellow('\n📦 Select default model:\n'));
861
1165
  const modelOptions = [
862
1166
  'claude-opus-4-5@20251101 - Most capable ($$)',
863
- 'claude-sonnet-4-5@20250514 - Balanced ($)',
1167
+ 'claude-sonnet-4-5@20250929 - Balanced ($)',
864
1168
  'claude-haiku-4-5@20251001 - Fast & cheap',
865
- 'gemini-2.5-pro - Google\'s best',
1169
+ 'gemini-3-pro-preview - Google\'s best',
866
1170
  'gemini-2.5-flash - Fast Gemini'
867
1171
  ];
868
1172
  const modelIds = [
869
- 'claude-opus-4-5@20251101', 'claude-sonnet-4-5@20250514', 'claude-haiku-4-5@20251001',
870
- 'gemini-2.5-pro', 'gemini-2.5-flash'
1173
+ 'claude-opus-4-5@20251101', 'claude-sonnet-4-5@20250929', 'claude-haiku-4-5@20251001',
1174
+ 'gemini-3-pro-preview', 'gemini-2.5-flash'
871
1175
  ];
872
1176
  const modelChoice = await promptSelect('', modelOptions);
873
1177
  config.default_model = modelIds[modelChoice];
874
1178
  // Enable models
875
1179
  if (await promptYesNo(chalk.cyan('\nEnable all Claude models?'))) {
876
- ['claude-opus-4-5@20251101', 'claude-sonnet-4-5@20250514', 'claude-haiku-4-5@20251001']
1180
+ ['claude-opus-4-5@20251101', 'claude-sonnet-4-5@20250929', 'claude-haiku-4-5@20251001']
877
1181
  .forEach(m => { if (!config.enabled_models.includes(m))
878
1182
  config.enabled_models.push(m); });
879
1183
  }
880
1184
  if (await promptYesNo(chalk.cyan('Enable Gemini models?'))) {
881
- ['gemini-2.5-pro', 'gemini-2.5-flash']
1185
+ ['gemini-3-pro-preview', 'gemini-2.5-flash']
882
1186
  .forEach(m => { if (!config.enabled_models.includes(m))
883
1187
  config.enabled_models.push(m); });
884
1188
  }
@@ -887,20 +1191,20 @@ async function interactiveConfig() {
887
1191
  config.model_aliases = {
888
1192
  ...config.model_aliases,
889
1193
  opus: 'claude-opus-4-5@20251101',
890
- sonnet: 'claude-sonnet-4-5@20250514',
1194
+ sonnet: 'claude-sonnet-4-5@20250929',
891
1195
  haiku: 'claude-haiku-4-5@20251001',
892
- gemini: 'gemini-2.5-pro',
1196
+ gemini: 'gemini-3-pro-preview',
893
1197
  'gemini-flash': 'gemini-2.5-flash',
894
1198
  'gpt-4': 'claude-opus-4-5@20251101',
895
- 'gpt-4o': 'claude-sonnet-4-5@20250514',
1199
+ 'gpt-4o': 'claude-sonnet-4-5@20250929',
896
1200
  'gpt-4o-mini': 'claude-haiku-4-5@20251001'
897
1201
  };
898
1202
  }
899
1203
  // Fallbacks
900
1204
  if (await promptYesNo(chalk.cyan('Set up fallback chains?'))) {
901
1205
  config.fallback_chains = {
902
- 'claude-opus-4-5@20251101': ['claude-sonnet-4-5@20250514', 'gemini-2.5-pro'],
903
- 'claude-sonnet-4-5@20250514': ['claude-haiku-4-5@20251001', 'gemini-2.5-flash'],
1206
+ 'claude-opus-4-5@20251101': ['claude-sonnet-4-5@20250929', 'gemini-3-pro-preview'],
1207
+ 'claude-sonnet-4-5@20250929': ['claude-haiku-4-5@20251001', 'gemini-2.5-flash'],
904
1208
  'claude-haiku-4-5@20251001': ['gemini-2.5-flash-lite']
905
1209
  };
906
1210
  }