jbai-cli 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,9 +35,10 @@ Expected output:
35
35
  ```
36
36
  Testing JetBrains AI Platform (staging)
37
37
 
38
- 1. OpenAI Proxy (GPT): ✅ Working
39
- 2. Anthropic Proxy (Claude): ✅ Working
40
- 3. Google Proxy (Gemini): ✅ Working
38
+ 1. OpenAI Proxy (Chat): ✅ Working
39
+ 2. OpenAI Proxy (Codex /responses): ✅ Working
40
+ 3. Anthropic Proxy (Claude): ✅ Working
41
+ 4. Google Proxy (Gemini): ✅ Working
41
42
  ```
42
43
 
43
44
  ## Usage
@@ -103,7 +104,7 @@ Each tool has a sensible default, but you can specify any available model:
103
104
  jbai-claude --model claude-opus-4-1-20250805
104
105
 
105
106
  # Codex with GPT-5
106
- jbai-codex --model gpt-5-2025-08-07
107
+ jbai-codex --model gpt-5.2-codex
107
108
 
108
109
  # Aider with Gemini Pro
109
110
  jbai-aider --model gemini/gemini-2.5-pro
@@ -126,10 +127,19 @@ jbai-aider --model gemini/gemini-2.5-pro
126
127
  | `gpt-4o-2024-11-20` | Default |
127
128
  | `gpt-5-2025-08-07` | Latest |
128
129
  | `gpt-5.1-2025-11-13` | |
130
+ | `gpt-5.2-2025-12-11` | |
129
131
  | `gpt-5-mini-2025-08-07` | Fast |
130
132
  | `o3-2025-04-16` | Reasoning |
131
133
  | `o3-mini-2025-01-31` | |
132
134
 
135
+ **Codex (OpenAI Responses)** - Use with Codex CLI: `jbai-codex --model <model>`
136
+ | Model | Notes |
137
+ |-------|-------|
138
+ | `gpt-5.2-codex` | Default, coding-optimized |
139
+ | `gpt-5.1-codex` | |
140
+ | `gpt-5.1-codex-mini` | Faster |
141
+ | `gpt-5.1-codex-max` | |
142
+
133
143
  **Gemini (Google)** - Use with Aider: `jbai-aider --model gemini/<model>`
134
144
  | Model | Notes |
135
145
  |-------|-------|
package/bin/jbai-codex.js CHANGED
@@ -59,7 +59,7 @@ const hasModel = args.includes('--model');
59
59
  const finalArgs = ['-c', `model_provider=${providerName}`];
60
60
 
61
61
  if (!hasModel) {
62
- finalArgs.push('--model', config.MODELS.openai.default);
62
+ finalArgs.push('--model', config.MODELS.codex?.default || config.MODELS.openai.default);
63
63
  }
64
64
 
65
65
  // Add super mode flags (full-auto)
@@ -80,21 +80,6 @@ config.MODELS.openai.available.forEach(model => {
80
80
  };
81
81
  });
82
82
 
83
- // Add other provider models (DeepSeek, Mistral, Qwen, XAI, Meta) via OpenAI endpoint
84
- const otherModels = [
85
- ...config.MODELS.other.deepseek,
86
- ...config.MODELS.other.mistral,
87
- ...config.MODELS.other.qwen,
88
- ...config.MODELS.other.xai,
89
- ...config.MODELS.other.meta
90
- ];
91
- otherModels.forEach(model => {
92
- opencodeConfig.provider[providerName].models[model] = {
93
- name: model,
94
- limit: { context: 128000, output: 8192 }
95
- };
96
- });
97
-
98
83
  // Add JetBrains Anthropic provider for Claude models
99
84
  opencodeConfig.provider[anthropicProviderName] = {
100
85
  npm: '@ai-sdk/anthropic',
package/bin/jbai.js CHANGED
@@ -18,12 +18,6 @@ const TOOLS = {
18
18
  install: 'npm install -g @openai/codex',
19
19
  check: 'codex --version'
20
20
  },
21
- aider: {
22
- name: 'Aider',
23
- command: 'aider',
24
- install: 'pipx install aider-chat',
25
- check: 'aider --version'
26
- },
27
21
  gemini: {
28
22
  name: 'Gemini CLI',
29
23
  command: 'gemini',
@@ -47,10 +41,10 @@ COMMANDS:
47
41
  jbai token Show token status
48
42
  jbai token set Set token interactively
49
43
  jbai token refresh Refresh expired token
50
- jbai test Test all API endpoints
44
+ jbai test Test API endpoints (incl. Codex /responses)
51
45
  jbai env [staging|production] Switch environment
52
46
  jbai models List available models
53
- jbai install Install all AI tools (claude, codex, aider)
47
+ jbai install Install all AI tools (claude, codex, gemini, opencode)
54
48
  jbai install claude Install specific tool
55
49
  jbai doctor Check which tools are installed
56
50
  jbai help Show this help
@@ -58,7 +52,6 @@ COMMANDS:
58
52
  TOOL WRAPPERS:
59
53
  jbai-claude Launch Claude Code with JetBrains AI
60
54
  jbai-codex Launch Codex CLI with JetBrains AI
61
- jbai-aider Launch Aider with JetBrains AI
62
55
  jbai-gemini Launch Gemini CLI with JetBrains AI
63
56
  jbai-opencode Launch OpenCode with JetBrains AI
64
57
 
@@ -66,13 +59,13 @@ SUPER MODE:
66
59
  Add --super (or --yolo or -s) to skip confirmations:
67
60
  jbai-claude --super # Skip permission prompts
68
61
  jbai-codex --super # Full auto mode
69
- jbai-aider --super # Auto-confirm changes
62
+ jbai-gemini --super # Auto-confirm changes
70
63
 
71
64
  EXAMPLES:
72
65
  jbai token set # Set your token
73
66
  jbai-claude # Start Claude Code
74
67
  jbai-codex exec "explain code" # Run Codex task
75
- jbai-aider # Start Aider
68
+ jbai-gemini # Start Gemini CLI
76
69
 
77
70
  TOKEN:
78
71
  Get token: ${config.getEndpoints().tokenUrl}
@@ -146,40 +139,61 @@ async function testEndpoints() {
146
139
  console.log(`Testing JetBrains AI Platform (${config.getEnvironment()})\n`);
147
140
 
148
141
  // Test OpenAI
149
- process.stdout.write('1. OpenAI Proxy (GPT): ');
142
+ process.stdout.write('1. OpenAI Proxy (Chat): ');
150
143
  try {
151
144
  const result = await httpPost(
152
145
  `${endpoints.openai}/chat/completions`,
153
- { model: 'gpt-4o-2024-11-20', messages: [{ role: 'user', content: 'Say OK' }], max_tokens: 5 },
146
+ { model: config.MODELS.openai.default, messages: [{ role: 'user', content: 'Say OK' }], max_tokens: 5 },
147
+ { 'Grazie-Authenticate-JWT': token }
148
+ );
149
+ const ok = result.statusCode === 200 && Array.isArray(result.json?.choices);
150
+ console.log(ok ? '✅ Working' : `❌ Failed (${result.statusCode})`);
151
+ } catch (e) {
152
+ console.log(`❌ ${e.message}`);
153
+ }
154
+
155
+ // Test Codex (Responses)
156
+ process.stdout.write('2. OpenAI Proxy (Codex /responses): ');
157
+ try {
158
+ const result = await httpPost(
159
+ `${endpoints.openai}/responses`,
160
+ { model: config.MODELS.codex.default, input: 'Say OK', max_output_tokens: 64 },
154
161
  { 'Grazie-Authenticate-JWT': token }
155
162
  );
156
- console.log(result.choices ? '✅ Working' : '❌ Failed');
163
+ const outputText = result.json?.output
164
+ ?.find(o => o.type === 'message')
165
+ ?.content?.find(c => c.type === 'output_text')
166
+ ?.text;
167
+ const ok = result.statusCode === 200 && typeof outputText === 'string';
168
+ console.log(ok ? '✅ Working' : `❌ Failed (${result.statusCode})`);
157
169
  } catch (e) {
158
170
  console.log(`❌ ${e.message}`);
159
171
  }
160
172
 
161
173
  // Test Anthropic
162
- process.stdout.write('2. Anthropic Proxy (Claude): ');
174
+ process.stdout.write('3. Anthropic Proxy (Claude): ');
163
175
  try {
164
176
  const result = await httpPost(
165
177
  `${endpoints.anthropic}/messages`,
166
178
  { model: 'claude-sonnet-4-5-20250929', messages: [{ role: 'user', content: 'Say OK' }], max_tokens: 10 },
167
179
  { 'Grazie-Authenticate-JWT': token, 'anthropic-version': '2023-06-01' }
168
180
  );
169
- console.log(result.content ? '✅ Working' : '❌ Failed');
181
+ const ok = result.statusCode === 200 && Array.isArray(result.json?.content);
182
+ console.log(ok ? '✅ Working' : `❌ Failed (${result.statusCode})`);
170
183
  } catch (e) {
171
184
  console.log(`❌ ${e.message}`);
172
185
  }
173
186
 
174
187
  // Test Google
175
- process.stdout.write('3. Google Proxy (Gemini): ');
188
+ process.stdout.write('4. Google Proxy (Gemini): ');
176
189
  try {
177
190
  const result = await httpPost(
178
191
  `${endpoints.google}/v1/projects/default/locations/default/publishers/google/models/gemini-2.5-flash:generateContent`,
179
192
  { contents: [{ role: 'user', parts: [{ text: 'Say OK' }] }] },
180
193
  { 'Grazie-Authenticate-JWT': token }
181
194
  );
182
- console.log(result.candidates ? '✅ Working' : '❌ Failed');
195
+ const ok = result.statusCode === 200 && Array.isArray(result.json?.candidates);
196
+ console.log(ok ? '✅ Working' : `❌ Failed (${result.statusCode})`);
183
197
  } catch (e) {
184
198
  console.log(`❌ ${e.message}`);
185
199
  }
@@ -201,14 +215,16 @@ function httpPost(url, body, headers) {
201
215
  ...headers
202
216
  }
203
217
  }, (res) => {
204
- let body = '';
205
- res.on('data', chunk => body += chunk);
218
+ let rawBody = '';
219
+ res.on('data', chunk => rawBody += chunk);
206
220
  res.on('end', () => {
221
+ let json = null;
207
222
  try {
208
- resolve(JSON.parse(body));
223
+ json = JSON.parse(rawBody);
209
224
  } catch {
210
- reject(new Error('Invalid response'));
225
+ // Some proxy errors are plain text.
211
226
  }
227
+ resolve({ statusCode: res.statusCode, json, rawBody });
212
228
  });
213
229
  });
214
230
 
@@ -227,43 +243,32 @@ function showModels() {
227
243
  console.log(` - ${m}${def}`);
228
244
  });
229
245
 
230
- console.log('\nGPT (OpenAI) - jbai-codex, jbai-aider:');
246
+ console.log('\nGPT (OpenAI Chat) - jbai-aider, jbai-opencode:');
231
247
  config.MODELS.openai.available.forEach((m) => {
232
248
  const def = m === config.MODELS.openai.default ? ' (default)' : '';
233
249
  console.log(` - ${m}${def}`);
234
250
  });
235
251
 
252
+ console.log('\nCodex (OpenAI Responses) - jbai-codex:');
253
+ config.MODELS.codex.available.forEach((m) => {
254
+ const def = m === config.MODELS.codex.default ? ' (default)' : '';
255
+ console.log(` - ${m}${def}`);
256
+ });
257
+
236
258
  console.log('\nGemini (Google) - jbai-gemini:');
237
259
  config.MODELS.gemini.available.forEach((m) => {
238
260
  const def = m === config.MODELS.gemini.default ? ' (default)' : '';
239
261
  console.log(` - ${m}${def}`);
240
262
  });
241
263
 
242
- console.log('\nDeepSeek - jbai-aider, jbai-opencode:');
243
- config.MODELS.other.deepseek.forEach((m) => console.log(` - ${m}`));
244
-
245
- console.log('\nMistral - jbai-aider, jbai-opencode:');
246
- config.MODELS.other.mistral.forEach((m) => console.log(` - ${m}`));
247
-
248
- console.log('\nQwen - jbai-aider, jbai-opencode:');
249
- config.MODELS.other.qwen.forEach((m) => console.log(` - ${m}`));
250
-
251
- console.log('\nXAI (Grok) - jbai-aider, jbai-opencode:');
252
- config.MODELS.other.xai.forEach((m) => console.log(` - ${m}`));
253
-
254
- console.log('\nMeta (Llama) - jbai-aider, jbai-opencode:');
255
- config.MODELS.other.meta.forEach((m) => console.log(` - ${m}`));
256
-
257
264
  // Count total
258
265
  const total = config.MODELS.claude.available.length +
259
266
  config.MODELS.openai.available.length +
260
- config.MODELS.gemini.available.length +
261
- config.MODELS.other.deepseek.length +
262
- config.MODELS.other.mistral.length +
263
- config.MODELS.other.qwen.length +
264
- config.MODELS.other.xai.length +
265
- config.MODELS.other.meta.length;
267
+ config.MODELS.codex.available.length +
268
+ config.MODELS.gemini.available.length;
266
269
  console.log(`\nTotal: ${total} models`);
270
+ console.log('\nNote: Other providers (DeepSeek, Mistral, Qwen, XAI, Meta) are available');
271
+ console.log('via Grazie native API but not via OpenAI-compatible CLI tools.');
267
272
  }
268
273
 
269
274
  function setEnvironment(env) {
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * E2E Model Compatibility Testing Script
5
+ * Tests all configured Grazie models against the API endpoints
6
+ * to identify which models are valid and which return 404 or errors.
7
+ */
8
+
9
+ const https = require('https');
10
+ const config = require('../lib/config');
11
+
12
+ const token = config.getToken();
13
+ if (!token) {
14
+ console.error('❌ No token found. Run: jbai token set');
15
+ process.exit(1);
16
+ }
17
+
18
+ const endpoints = config.getEndpoints();
19
+
20
+ // Colors for terminal output
21
+ const colors = {
22
+ reset: '\x1b[0m',
23
+ green: '\x1b[32m',
24
+ red: '\x1b[31m',
25
+ yellow: '\x1b[33m',
26
+ cyan: '\x1b[36m',
27
+ dim: '\x1b[2m'
28
+ };
29
+
30
+ function httpPost(url, body, headers) {
31
+ return new Promise((resolve, reject) => {
32
+ const urlObj = new URL(url);
33
+ const data = JSON.stringify(body);
34
+
35
+ const req = https.request({
36
+ hostname: urlObj.hostname,
37
+ port: 443,
38
+ path: urlObj.pathname + urlObj.search,
39
+ method: 'POST',
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ 'Content-Length': Buffer.byteLength(data),
43
+ ...headers
44
+ },
45
+ timeout: 30000
46
+ }, (res) => {
47
+ let body = '';
48
+ res.on('data', chunk => body += chunk);
49
+ res.on('end', () => {
50
+ try {
51
+ const parsed = JSON.parse(body);
52
+ resolve({ status: res.statusCode, data: parsed });
53
+ } catch {
54
+ resolve({ status: res.statusCode, data: body, raw: true });
55
+ }
56
+ });
57
+ });
58
+
59
+ req.on('error', (e) => reject(e));
60
+ req.on('timeout', () => {
61
+ req.destroy();
62
+ reject(new Error('Timeout'));
63
+ });
64
+ req.write(data);
65
+ req.end();
66
+ });
67
+ }
68
+
69
+ async function testOpenAIModel(model) {
70
+ try {
71
+ // GPT-5.x and O-series models require max_completion_tokens instead of max_tokens
72
+ const needsCompletionTokens = model.startsWith('gpt-5') || model.startsWith('o1') ||
73
+ model.startsWith('o3') || model.startsWith('o4');
74
+
75
+ const bodyParams = needsCompletionTokens
76
+ ? { max_completion_tokens: 500 } // O-series needs more tokens for reasoning
77
+ : { max_tokens: 5 };
78
+
79
+ const result = await httpPost(
80
+ `${endpoints.openai}/chat/completions`,
81
+ {
82
+ model: model,
83
+ messages: [{ role: 'user', content: 'Say OK' }],
84
+ ...bodyParams
85
+ },
86
+ { 'Grazie-Authenticate-JWT': token }
87
+ );
88
+
89
+ if (result.status === 200 && result.data.choices) {
90
+ return { success: true, message: 'OK' };
91
+ } else if (result.status === 404) {
92
+ return { success: false, message: `404 Not Found`, error: result.data.error?.message || 'Model not found' };
93
+ } else if (result.status === 400) {
94
+ return { success: false, message: `400 Bad Request`, error: result.data.error?.message || 'Invalid request' };
95
+ } else {
96
+ return { success: false, message: `Status ${result.status}`, error: result.data.error?.message || JSON.stringify(result.data).substring(0, 100) };
97
+ }
98
+ } catch (e) {
99
+ return { success: false, message: 'Error', error: e.message };
100
+ }
101
+ }
102
+
103
+ async function testAnthropicModel(model) {
104
+ try {
105
+ const result = await httpPost(
106
+ `${endpoints.anthropic}/messages`,
107
+ {
108
+ model: model,
109
+ messages: [{ role: 'user', content: 'Say OK' }],
110
+ max_tokens: 10
111
+ },
112
+ {
113
+ 'Grazie-Authenticate-JWT': token,
114
+ 'anthropic-version': '2023-06-01'
115
+ }
116
+ );
117
+
118
+ if (result.status === 200 && result.data.content) {
119
+ return { success: true, message: 'OK' };
120
+ } else if (result.status === 404) {
121
+ return { success: false, message: `404 Not Found`, error: result.data.error?.message || 'Model not found' };
122
+ } else if (result.status === 400) {
123
+ return { success: false, message: `400 Bad Request`, error: result.data.error?.message || 'Invalid request' };
124
+ } else {
125
+ return { success: false, message: `Status ${result.status}`, error: result.data.error?.message || JSON.stringify(result.data).substring(0, 100) };
126
+ }
127
+ } catch (e) {
128
+ return { success: false, message: 'Error', error: e.message };
129
+ }
130
+ }
131
+
132
+ async function testGeminiModel(model) {
133
+ try {
134
+ // Gemini API format via Vertex AI proxy
135
+ const result = await httpPost(
136
+ `${endpoints.google}/v1/projects/default/locations/default/publishers/google/models/${model}:generateContent`,
137
+ {
138
+ contents: [{ role: 'user', parts: [{ text: 'Say OK' }] }]
139
+ },
140
+ { 'Grazie-Authenticate-JWT': token }
141
+ );
142
+
143
+ if (result.status === 200 && result.data.candidates) {
144
+ return { success: true, message: 'OK' };
145
+ } else if (result.status === 404) {
146
+ return { success: false, message: `404 Not Found`, error: result.data.error?.message || 'Model not found' };
147
+ } else if (result.status === 400) {
148
+ return { success: false, message: `400 Bad Request`, error: result.data.error?.message || 'Invalid request' };
149
+ } else {
150
+ return { success: false, message: `Status ${result.status}`, error: result.data.error?.message || JSON.stringify(result.data).substring(0, 100) };
151
+ }
152
+ } catch (e) {
153
+ return { success: false, message: 'Error', error: e.message };
154
+ }
155
+ }
156
+
157
+ async function runTests() {
158
+ console.log(`\n${'='.repeat(70)}`);
159
+ console.log(`${colors.cyan}JBAI-CLI E2E MODEL COMPATIBILITY TESTING${colors.reset}`);
160
+ console.log(`Environment: ${config.getEnvironment()}`);
161
+ console.log(`${'='.repeat(70)}\n`);
162
+
163
+ const results = {
164
+ openai: { working: [], failing: [] },
165
+ anthropic: { working: [], failing: [] },
166
+ gemini: { working: [], failing: [] }
167
+ };
168
+
169
+ // Test Claude models (Anthropic)
170
+ console.log(`${colors.cyan}Testing Claude models (jbai-claude)...${colors.reset}`);
171
+ console.log('-'.repeat(50));
172
+ for (const model of config.MODELS.claude.available) {
173
+ process.stdout.write(` ${model.padEnd(35)} `);
174
+ const result = await testAnthropicModel(model);
175
+ if (result.success) {
176
+ console.log(`${colors.green}✓ ${result.message}${colors.reset}`);
177
+ results.anthropic.working.push(model);
178
+ } else {
179
+ console.log(`${colors.red}✗ ${result.message}${colors.reset}`);
180
+ console.log(` ${colors.dim}${result.error}${colors.reset}`);
181
+ results.anthropic.failing.push({ model, error: result.error });
182
+ }
183
+ }
184
+
185
+ // Test OpenAI models (Codex, OpenCode)
186
+ console.log(`\n${colors.cyan}Testing OpenAI/GPT models (jbai-codex, jbai-opencode)...${colors.reset}`);
187
+ console.log('-'.repeat(50));
188
+ for (const model of config.MODELS.openai.available) {
189
+ process.stdout.write(` ${model.padEnd(35)} `);
190
+ const result = await testOpenAIModel(model);
191
+ if (result.success) {
192
+ console.log(`${colors.green}✓ ${result.message}${colors.reset}`);
193
+ results.openai.working.push(model);
194
+ } else {
195
+ console.log(`${colors.red}✗ ${result.message}${colors.reset}`);
196
+ console.log(` ${colors.dim}${result.error}${colors.reset}`);
197
+ results.openai.failing.push({ model, error: result.error });
198
+ }
199
+ }
200
+
201
+ // Test Gemini models
202
+ console.log(`\n${colors.cyan}Testing Gemini models (jbai-gemini)...${colors.reset}`);
203
+ console.log('-'.repeat(50));
204
+ for (const model of config.MODELS.gemini.available) {
205
+ process.stdout.write(` ${model.padEnd(35)} `);
206
+ const result = await testGeminiModel(model);
207
+ if (result.success) {
208
+ console.log(`${colors.green}✓ ${result.message}${colors.reset}`);
209
+ results.gemini.working.push(model);
210
+ } else {
211
+ console.log(`${colors.red}✗ ${result.message}${colors.reset}`);
212
+ console.log(` ${colors.dim}${result.error}${colors.reset}`);
213
+ results.gemini.failing.push({ model, error: result.error });
214
+ }
215
+ }
216
+
217
+ // Summary
218
+ console.log(`\n${'='.repeat(70)}`);
219
+ console.log(`${colors.cyan}SUMMARY${colors.reset}`);
220
+ console.log(`${'='.repeat(70)}`);
221
+
222
+ const totalWorking = results.anthropic.working.length + results.openai.working.length +
223
+ results.gemini.working.length;
224
+ const totalFailing = results.anthropic.failing.length + results.openai.failing.length +
225
+ results.gemini.failing.length;
226
+
227
+ console.log(`\n${colors.green}Working Models: ${totalWorking}${colors.reset}`);
228
+ console.log(`${colors.red}Failing Models: ${totalFailing}${colors.reset}`);
229
+
230
+ console.log(`\n${colors.cyan}By Provider:${colors.reset}`);
231
+ console.log(` Claude (Anthropic): ${colors.green}${results.anthropic.working.length}${colors.reset} working, ${colors.red}${results.anthropic.failing.length}${colors.reset} failing`);
232
+ console.log(` OpenAI/GPT: ${colors.green}${results.openai.working.length}${colors.reset} working, ${colors.red}${results.openai.failing.length}${colors.reset} failing`);
233
+ console.log(` Gemini (Google): ${colors.green}${results.gemini.working.length}${colors.reset} working, ${colors.red}${results.gemini.failing.length}${colors.reset} failing`);
234
+
235
+ if (totalFailing > 0) {
236
+ console.log(`\n${colors.red}FAILING MODELS TO REMOVE:${colors.reset}`);
237
+
238
+ if (results.anthropic.failing.length > 0) {
239
+ console.log(`\n Claude models:`);
240
+ results.anthropic.failing.forEach(f => console.log(` - ${f.model}`));
241
+ }
242
+ if (results.openai.failing.length > 0) {
243
+ console.log(`\n OpenAI models:`);
244
+ results.openai.failing.forEach(f => console.log(` - ${f.model}`));
245
+ }
246
+ if (results.gemini.failing.length > 0) {
247
+ console.log(`\n Gemini models:`);
248
+ results.gemini.failing.forEach(f => console.log(` - ${f.model}`));
249
+ }
250
+ } else {
251
+ console.log(`\n${colors.green}All models are working correctly!${colors.reset}`);
252
+ }
253
+
254
+ console.log(`\n${'='.repeat(70)}\n`);
255
+
256
+ // Return results for programmatic use
257
+ return results;
258
+ }
259
+
260
+ // Run tests
261
+ runTests().catch(console.error);
package/lib/config.js CHANGED
@@ -26,91 +26,91 @@ const ENDPOINTS = {
26
26
  };
27
27
 
28
28
  // All models available from JetBrains AI Platform (Grazie)
29
- // Model names are mapped to SDK-compatible format
29
+ // Model names must match EXACTLY what the Grazie API accepts
30
+ // Run 'node bin/test-models.js' to verify model availability
30
31
  const MODELS = {
31
32
  claude: {
32
33
  default: 'claude-sonnet-4-5-20250929',
33
34
  available: [
34
- // Claude 4.x series (latest)
35
- 'claude-opus-4-5-20250929',
35
+ // Claude 4.5 series (latest)
36
+ 'claude-opus-4-5-20251101',
36
37
  'claude-sonnet-4-5-20250929',
37
- 'claude-haiku-4-5-20250929',
38
+ 'claude-haiku-4-5-20251001',
39
+ // Claude 4.x series
38
40
  'claude-opus-4-1-20250805',
39
41
  'claude-opus-4-20250514',
40
42
  'claude-sonnet-4-20250514',
41
43
  // Claude 3.x series
42
44
  'claude-3-7-sonnet-20250219',
43
- 'claude-3-5-sonnet-20241022',
44
- 'claude-3-5-haiku-20241022',
45
- 'claude-3-haiku-20240307'
45
+ 'claude-3-5-haiku-20241022'
46
46
  ]
47
47
  },
48
48
  openai: {
49
- default: 'gpt-4o',
49
+ // Chat/Completions models (used by Aider/OpenCode)
50
+ // Keep in sync with the OpenAI proxy's advertised list.
51
+ default: 'gpt-4o-2024-11-20',
50
52
  available: [
51
- // GPT-5.x series (latest)
52
- 'gpt-5',
53
- 'gpt-5.1',
53
+ // GPT-5.x series (latest) - require date-versioned names
54
+ 'gpt-5.2-2025-12-11',
54
55
  'gpt-5.2',
55
- 'gpt-5-mini',
56
- 'gpt-5-nano',
56
+ 'gpt-5.1-2025-11-13',
57
+ 'gpt-5-2025-08-07',
58
+ 'gpt-5-mini-2025-08-07',
59
+ 'gpt-5-nano-2025-08-07',
60
+ // GPT-4.1 series
61
+ 'gpt-4.1-2025-04-14',
62
+ 'gpt-4.1-mini-2025-04-14',
63
+ 'gpt-4.1-nano-2025-04-14',
64
+ // GPT-4o/4-turbo
65
+ 'gpt-4o-2024-11-20',
66
+ 'gpt-4o-mini-2024-07-18',
67
+ 'gpt-4-turbo-2024-04-09',
68
+ 'gpt-4-0613',
69
+ // O-series (reasoning) - use max_completion_tokens instead of max_tokens
70
+ 'o4-mini-2025-04-16',
71
+ 'o3-2025-04-16',
72
+ 'o3-mini-2025-01-31',
73
+ 'o1-2024-12-17',
74
+ // Legacy
75
+ 'gpt-3.5-turbo-0125'
76
+ ]
77
+ },
78
+ // Codex CLI uses the OpenAI "responses" API (wire_api = "responses")
79
+ codex: {
80
+ default: 'gpt-5.2-codex',
81
+ available: [
57
82
  'gpt-5-codex',
58
83
  'gpt-5.1-codex',
59
- 'gpt-5.1-codex-max',
60
84
  'gpt-5.1-codex-mini',
61
- 'gpt-5.2-codex',
62
- 'gpt-5.2-pro',
63
- // GPT-4.x series
64
- 'gpt-4.1',
65
- 'gpt-4.1-mini',
66
- 'gpt-4.1-nano',
67
- 'gpt-4o',
68
- 'gpt-4o-mini',
69
- 'gpt-4-turbo',
70
- 'gpt-4',
71
- // O-series (reasoning)
72
- 'o1',
73
- 'o3',
74
- 'o3-mini',
75
- 'o4-mini'
85
+ 'gpt-5.1-codex-max',
86
+ 'gpt-5.2-codex'
76
87
  ]
77
88
  },
78
89
  gemini: {
79
90
  default: 'gemini-2.5-flash',
80
91
  available: [
81
- // Gemini 3.x (latest)
82
- 'gemini-3.0-pro',
83
- 'gemini-3.0-flash',
84
- // Gemini 2.x
92
+ // Gemini 3.x (preview)
93
+ 'gemini-3-pro-preview',
94
+ 'gemini-3-flash-preview',
95
+ // Gemini 2.5
85
96
  'gemini-2.5-pro',
86
97
  'gemini-2.5-flash',
87
98
  'gemini-2.5-flash-lite',
88
- 'gemini-2.0-flash',
89
- 'gemini-2.0-flash-lite'
99
+ // Gemini 2.0
100
+ 'gemini-2.0-flash-001',
101
+ 'gemini-2.0-flash-lite-001'
90
102
  ]
91
- },
92
- // Other providers available via Grazie
93
- other: {
94
- deepseek: ['deepseek-r1'],
95
- mistral: ['mistral-large', 'mistral-small', 'mistral-codestral', 'mistral-open-7b', 'mixtral-open-8x7b', 'mixtral-open-8x22b'],
96
- qwen: ['qwen-max', 'qwen-max-longcontext', 'qwen-plus', 'qwen-turbo'],
97
- xai: ['xai-grok-4', 'xai-grok-4-fast', 'xai-grok-4-1-fast', 'xai-grok-code-fast-1'],
98
- meta: ['grazie-chat-llama-v3-8b-instruct']
99
103
  }
104
+ // NOTE: Other providers (DeepSeek, Mistral, Qwen, XAI, Meta) are available
105
+ // via Grazie native Chat API but NOT via the OpenAI-compatible proxy.
106
+ // They are not supported by CLI tools that use OpenAI API format.
100
107
  };
101
108
 
102
- // All models for tools that support multiple providers (like Aider, OpenCode)
109
+ // All models for tools that support multiple providers (OpenCode, Codex)
103
110
  const ALL_MODELS = {
104
111
  openai: MODELS.openai.available,
105
112
  anthropic: MODELS.claude.available,
106
- gemini: MODELS.gemini.available,
107
- other: [
108
- ...MODELS.other.deepseek,
109
- ...MODELS.other.mistral,
110
- ...MODELS.other.qwen,
111
- ...MODELS.other.xai,
112
- ...MODELS.other.meta
113
- ]
113
+ gemini: MODELS.gemini.available
114
114
  };
115
115
 
116
116
  function ensureConfigDir() {
@@ -190,11 +190,6 @@ const TOOLS = {
190
190
  command: 'codex',
191
191
  install: 'npm install -g @openai/codex'
192
192
  },
193
- aider: {
194
- name: 'Aider',
195
- command: 'aider',
196
- install: 'brew install aider'
197
- },
198
193
  gemini: {
199
194
  name: 'Gemini CLI',
200
195
  command: 'gemini',
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "jbai-cli",
3
- "version": "1.4.0",
4
- "description": "CLI wrappers to use AI coding tools (Claude Code, Codex, Aider, Gemini CLI, OpenCode) with JetBrains AI Platform",
3
+ "version": "1.5.1",
4
+ "description": "CLI wrappers to use AI coding tools (Claude Code, Codex, Gemini CLI, OpenCode) with JetBrains AI Platform",
5
5
  "keywords": [
6
6
  "jetbrains",
7
7
  "ai",
8
8
  "claude",
9
9
  "codex",
10
- "aider",
11
10
  "gemini",
12
11
  "opencode",
13
12
  "cli",
@@ -29,7 +28,6 @@
29
28
  "jbai": "./bin/jbai.js",
30
29
  "jbai-claude": "./bin/jbai-claude.js",
31
30
  "jbai-codex": "./bin/jbai-codex.js",
32
- "jbai-aider": "./bin/jbai-aider.js",
33
31
  "jbai-gemini": "./bin/jbai-gemini.js",
34
32
  "jbai-opencode": "./bin/jbai-opencode.js"
35
33
  },
@@ -41,9 +39,7 @@
41
39
  "engines": {
42
40
  "node": ">=18.0.0"
43
41
  },
44
- "dependencies": {
45
- "yaml": "^2.7.1"
46
- },
42
+ "dependencies": {},
47
43
  "scripts": {
48
44
  "postinstall": "node lib/postinstall.js",
49
45
  "test": "node bin/jbai.js test"
package/bin/jbai-aider.js DELETED
@@ -1,94 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { spawn } = require('child_process');
4
- const fs = require('fs');
5
- const path = require('path');
6
- const os = require('os');
7
- const yaml = require('yaml');
8
- const config = require('../lib/config');
9
-
10
- const token = config.getToken();
11
- if (!token) {
12
- console.error('❌ No token found. Run: jbai token set');
13
- process.exit(1);
14
- }
15
-
16
- if (config.isTokenExpired(token)) {
17
- console.error('⚠️ Token expired. Run: jbai token refresh');
18
- process.exit(1);
19
- }
20
-
21
- const endpoints = config.getEndpoints();
22
- const environment = config.getEnvironment();
23
- let args = process.argv.slice(2);
24
-
25
- // Check for super mode (--super, --yolo, -s)
26
- const superFlags = ['--super', '--yolo', '-s'];
27
- const superMode = args.some(a => superFlags.includes(a));
28
- args = args.filter(a => !superFlags.includes(a));
29
-
30
- // Create model settings file with extra headers for JetBrains authentication
31
- const configDir = path.join(os.homedir(), '.jbai');
32
- const modelSettingsFile = path.join(configDir, 'aider-model-settings.yml');
33
-
34
- // Ensure config directory exists
35
- if (!fs.existsSync(configDir)) {
36
- fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
37
- }
38
-
39
- // Create model settings with extra_headers for authentication
40
- // Use aider/extra_params for global settings that apply to all models
41
- const modelSettings = [
42
- {
43
- name: 'aider/extra_params',
44
- extra_params: {
45
- extra_headers: {
46
- 'Grazie-Authenticate-JWT': token
47
- }
48
- }
49
- }
50
- ];
51
-
52
- // Write model settings file
53
- fs.writeFileSync(modelSettingsFile, yaml.stringify(modelSettings), { mode: 0o600 });
54
-
55
- // Build aider arguments
56
- // Note: --no-stream is required because JetBrains proxy has stream_options issues
57
- const hasModel = args.includes('--model');
58
- const aiderArgs = [
59
- '--openai-api-base', endpoints.openai,
60
- '--openai-api-key', 'placeholder',
61
- '--model-settings-file', modelSettingsFile,
62
- '--no-stream'
63
- ];
64
-
65
- if (!hasModel) {
66
- aiderArgs.push('--model', config.MODELS.openai.default);
67
- }
68
-
69
- // Add super mode flags (auto-confirm all)
70
- if (superMode) {
71
- aiderArgs.push('--yes');
72
- console.log('🚀 Super mode: --yes (auto-confirm) enabled');
73
- }
74
-
75
- aiderArgs.push(...args);
76
-
77
- const child = spawn('aider', aiderArgs, {
78
- stdio: 'inherit',
79
- env: process.env
80
- });
81
-
82
- child.on('error', (err) => {
83
- if (err.code === 'ENOENT') {
84
- const tool = config.TOOLS.aider;
85
- console.error(`❌ ${tool.name} not found.\n`);
86
- console.error(`Install with: ${tool.install}`);
87
- console.error(`Or run: jbai install aider`);
88
- } else {
89
- console.error(`Error: ${err.message}`);
90
- }
91
- process.exit(1);
92
- });
93
-
94
- child.on('exit', (code) => process.exit(code || 0));